py3k: mapping dictionary (string->number) into list (of strings) - python

Assume we have dictionary that translates strings into numbers.
How to reverse it into list ?
Let assume, we can fill not mapped numbers with empty string ''.
Here example how it works:
>>> dic_into_list({'x':0, 'z':2, 'w':3})
['x', '', 'z', 'w']

d = {'x':0, 'z':2, 'w':3}
lst = [""] * (max(d.values()) + 1)
for k, v in d.items():
lst[v] = k
print(lst)
prints
['x', '', 'z', 'w']

The simplest way is to flip the dict and then iterate up to the maximum value (now key) in the dict:
original = {'x':0, 'z':2, 'w':3}
d = dict((v, k) for k, v in original.iteritems())
print [d.get(i, '') for i in range(max(d) + 1)]

I share my current solution: (I look for shorter and cleared implementation in other posts):
def dic_into_list(dic):
maxindex = max([v for i,v in dic.items()])
dicrev = {num:name for name,num in dic.items()}
l=[]
for i in range(0,maxindex+1):
if i in dicrev:
l.append(dicrev[i])
else:
l.append('')
return l

Related

Python - Replace string characters and get all combinations

I want to map the dictionary d = {'R': ['a', 'g'], 'Y': ['c', 't']} to the string s = '----YY----RR----' to get the following output:
----cc----aa----
----cc----ag----
----cc----ga----
----cc----gg----
----ct----aa----
----ct----ag----
----ct----ga----
----ct----gg----
----tc----aa----
----tc----ag----
----tc----ga----
----tc----gg----
----tt----aa----
----tt----ag----
----tt----ga----
----tt----gg----
My (very) inefficient code is as below:
seqs = set()
for k,v in d.items():
for i in v:
i_seq = seq.replace(k,i,1)
for n in v:
n_seq = i_seq.replace(k,n,1)
for k2,v2 in d.items():
for i2 in v2:
i2_seq = n_seq.replace(k2,i2,1)
for n2 in v2:
n2_seq = i2_seq.replace(k2,n2,1)
if not 'Y' in n2_seq and not 'R' in n2_seq:
seqs.add(n2_seq)
What is a smarter way to do that?
A general approach without itertools:
def replaceCombinations(s, d):
string_set = set([s])
for i in range(len(s)):
if s[i] in d.keys():
new_set = set()
for c in string_set:
new_set.update(set(c.replace(s[i], new_char, 1) for new_char in d[s[i]]))
string_set = new_set
return string_set
string = "----YY----RR----"
d = {'R': ['a', 'g'], 'Y': ['c', 't']}
for c in sorted(replaceCombinations(string, d)):
print(c)
Use itertools.product:
from itertools import product
for p in product(product(d['R'],repeat=2),product(d['Y'],repeat=2)):
print(f'----{p[0][0]}{p[0][1]}-----{p[1][0]}{p[1][1]}-----')
----aa-----cc-----
----aa-----ct-----
----aa-----tc-----
----aa-----tt-----
----ag-----cc-----
----ag-----ct-----
----ag-----tc-----
----ag-----tt-----
----ga-----cc-----
----ga-----ct-----
----ga-----tc-----
----ga-----tt-----
----gg-----cc-----
----gg-----ct-----
----gg-----tc-----
----gg-----tt-----

List to nested list and dictionary

I have this one long list and want to convert it to a nested list and a dictionary.
L= ["a","abc","de","efg","", "b","ijk","lm","op","qr","", "c","123","45","6789"]
output:
nested list:
[["a","abc","de","efg"], ["b","ijk","lm","op","qr"], ["c","123","45","6789"]]
dictionary:
{"a":["abc","de","efg"],
"b":["ijk","lm","op","qr"], "c":["123","45","6789] }
Can anyone tell me how to do that in python?
And I can't import anything
I assume the groups are separated by the empty strings. For this you can use itertools.groupby:
from itertools import groupby
data = ["a","abc","de","efg","", "b","ijk","lm","op","qr","", "c","123","45","6789"]
nl = [list(g) for k, g in groupby(data, ''.__ne__) if k]
d = {next(g): list(g) for k, g in groupby(data, ''.__ne__) if k}
print(nl)
print(d)
Results:
[['a', 'abc', 'de', 'efg'], ['b', 'ijk', 'lm', 'op', 'qr'], ['c', '123', '45', '6789']]
{'a': ['abc', 'de', 'efg'], 'b': ['ijk', 'lm', 'op', 'qr'], 'c': ['123', '45', '6789']}
In the groupby I'm using ''.__ne__ which is the function for "not equal" of an empty string. This way it's only capturing groups of non-empty strings.
EDIT
I just read that you cannot import. Here's a solution just using a loop:
nl = [[]]
for s in data:
if s:
nl[-1].append(s)
else:
nl.append([])
And for the dict:
itr = iter(data)
key = next(itr)
d = {key: []}
while True:
try: val = next(itr)
except StopIteration: break
if val:
d[key].append(val)
else:
key = next(itr)
d[key] = []
Here's how to convert L to a nested list:
L= ["a","abc","de","efg","","b","ijk","lm","op","qr","","c","123","45","6789"]
nested_list_L = []
temp = []
for item in L:
if item != "":
temp.append(item)
else:
nested_list_L.append(temp)
temp = []
nested_list_L.append(temp)
And here's how to convert L to a dictionary:
L= ["a","abc","de","efg","","b","ijk","lm","op","qr","","c","123","45","6789"]
dict_L = {}
temp = []
key = ""
for item in L:
if len(item) == 1:
key = item
elif len(item) > 1:
temp.append(item)
else:
dict_L[key] = temp
temp = []
key = ""
dict_L[key] = temp
From my understanding, you are trying to:
Split a list by empty string, then
Convert the resulting nested list into a dictionary, using first element of each sub-list as the key and the rest as value.
You can certainly accomplish the task without any imports. To split a list, just iterate over it and build the nested list along the way:
def split(data, on):
nested = []
curr = []
for x in data:
if x == on:
nested.append(curr)
curr = []
else:
curr.append(x)
if curr != [] or data[-1:] == [on]:
nested.append(curr)
return nested
Then, again, iterate over this nested list to build your desired dictionary:
def build_dict(key_valss):
d = {}
for key_vals in key_valss:
if key_vals != []:
key = key_vals[0]
vals = key_vals[1:]
d[key] = vals
return d
Compose the two functions to get what you want:
>>> build_dict( split(data = ["a","abc","de","efg","", "b","ijk","lm","op","qr","", "c","123","45","6789"] , on = '') )
{'a': ['abc', 'de', 'efg'], 'b': ['ijk', 'lm', 'op', 'qr'], 'c': ['123', '45', '6789']}

How to add index to a list inside a dictionary

I have a dictionary here:
dict = {'A':['1','1','1','1','1'], 'B':['2','2'], 'C':['3','3','3','3']}
What is the necessary process to get the following result?
dict = {'A':['1_01','1_02','1_03','1_04','1_05'], 'B':['2_01','2_02'], 'C':['3_01','3_02','3_03','3_04']}
I have been learning python for quite a while now but dictionary is kind of new to me.
As others mentioned, refrain from using built-in keywords as variable names, such as dict. I kept it for simplicity on your part.
This is probably the most pythonic way of doing it (one line of code):
dict = {key:[x+"_0"+str(cnt+1) for cnt,x in enumerate(value)] for key,value in dict.items()}
You could also iterate through each dictionary item, and then each list item and manually change the list names as shown below:
for key,value in dict.items():
for cnt,x in enumerate(value):
dict[key][cnt] = x+"_0"+str(cnt+1)
Also, as some others have mentioned, if you want numbers greater than 10 to save as 1_10 rather than 1_010 you can you an if/else statement inside of the list comprehension...
dict = {key:[x+"_0"+str(cnt+1) if cnt+1 < 10 else x+"_"+str(cnt+1) for cnt,x in enumerate(value)] for key,value in dict.items()}
First iterate on keys.
Then loop on keys you are getting on key like for 'A' value is ['1','1','1','1','1'] then we can change the element at ['1','1','1','1','1']
enumerate() helps you iterate on index,value then index starts with zero as per your expected output add 1 to index. As you want the trailing 0 before each count we did '%02d'% (index+1)
Like this:
dict = {'A':['1','1','1','1','1'], 'B':['2','2'], 'C':['3','3','3','3']}
for i in dict.keys(): #iterate on keys
for index,val in enumerate(dict[i]): #took value as we have key in i
element='%02d'% (index+1) #add trailing 0 we converted 1 to int 01
dict[i][index]=val+"_"+ str(element) #assign new value with converting integer to string
print(dict)
Output:
{'A': ['1_01', '1_02', '1_03', '1_04', '1_05'], 'C': ['3_01', '3_02', '3_03', '3_04'], 'B': ['2_01', '2_02']}
Use enumerate to iterate over list keeping track of index:
d = {'A':['1','1','1','1','1'], 'B':['2','2'], 'C':['3','3','3','3']}
newd = {}
for k, v in d.items():
newd[k] = [f'{x}_0{i}' for i, x in enumerate(v, 1)]
print(newd)
Also a dictionary-comprehension:
d = {k: [f'{x}_0{i}' for i, x in enumerate(v, 1)] for k, v in d.items()}
Note: Don't name your dictionary as dict because it shadows the built-in.
d= {'A':['1','1','1','1','1'], 'B':['2','2'], 'C':['3','3','3','3']}
{x:[j + '_'+ '{:02}'.format(i+1) for i,j in enumerate(y)] for x,y in d.items()}
adict = {'A':['1','1','1','1','1'], 'B':['2','2'], 'C':['3','3','3','3'], 'D': '23454'}
newdict = {}
for i,v in adict.items():
if isinstance(v, list):
count = 0
for e in v:
count += 1
e += '_0' + str(count)
newdict[i] = newdict.get(i, [e]) + [e]
else:
newdict[i] = newdict.get(i, v)
print (newdict)
#{'A': ['1_01', '1_01', '1_02', '1_03', '1_04', '1_05'], 'B': ['2_01', '2_01', '2_02'], 'C': ['3_01', '3_01', '3_02', '3_03', '3_04'], 'D': '23454'}
This solution will check for whether your value in the dictionary is a list before assigning an index to it
You can use a dictcomp:
from itertools import starmap
d = {
'A': ['1', '1', '1', '1', '1'],
'B': ['2', '2'],
'C': ['3', '3', '3', '3']
}
f = lambda x, y: '%s_%02d' % (y, x)
print({k: list(starmap(f, enumerate(v, 1))) for k, v in d.items()})
# {'A': ['1_01', '1_02', '1_03', '1_04', '1_05'], 'B': ['2_01', '2_02'], 'C': ['3_01', '3_02', '3_03', '3_04']}

Collapse a list of lists, grouping on specific element and appending other elements

How could I transform a list such as:
l=[ ['A', 'C21'], ['A','D43'],['B','D34'],['C','D45'],['C',D56']
to:
[ ['A','C21 D43'], ['B','D34'],['C','D45 D56'] ]
Where the grouping is performed according to element #0 of each sub list
and elements #1 are string concatenated within each group?
Try this :
l=[ ['A', 'C21'], ['A','D43'],['B','D34'],['C','D45'],['C','D56']]
x = {}
for a in l:
if a[0] not in x.keys():
x[a[0]] = [a[1]]
else:
x[a[0]].append(a[1])
print x
array_result = []
for keys, vals in x.iteritems():
array_result.append([keys, ' '.join(vals)])
print array_result
If the keys are contiguous then you can use itertools.groupby, eg:
from itertools import groupby
data =[ ['A', 'C21'], ['A','D43'],['B','D34'],['C','D45'],['C','D56'] ]
new_data = [[k, ' '.join(el[1] for el in g)] for k, g in groupby(data, lambda L: L[0])]
# [['A', 'C21 D43'], ['B', 'D34'], ['C', 'D45 D56']]
If not and order doesn't really matter, then:
from collections import defaultdict
dd = defaultdict(list)
for key, val in data:
dd[key].append(val)
new_data = [[k, ' '.join(v)] for k,v in dd.items()]
# [['B', 'D34'], ['C', 'D45 D56'], ['A', 'C21 D43']]
Alternatively - make use of dict.setdefault, eg:
d = {}
for key, val in data:
d.setdefault(key, []).append(val)
new_data = [[k, ' '.join(v)] for k,v in d.items()]
Or, if the keys aren't contiguous, but the output should maintain the order of the input, then use collections.OrderedDict, eg:
from collections import OrderedDict
d = OrderedDict()
for key, val in data:
d.setdefault(key, []).append(val)
new_data = [[k, ' '.join(v)] for k,v in d.items()]
# [['A', 'C21 D43'], ['B', 'D34'], ['C', 'D45 D56']]

Converting dictionaries to list sorted by values, with multiple values per item

In Python, I have a simple problem of converting lists and dictionaries that I have solved using explicit type check to tell the difference between integers and list of integers. I'm somewhat new to python, and I'd curious if there is a more 'pythonic' way to solve the problem,i.e. that avoids an explicit type check.
In short: Trying to sort a dictionary's keys using the values, but where each key can have multiple values, and the key needs to appear multiple times in the list. Data comes in the form {'a':1, 'b':[0,2],...}. Everything I have come up (using sorted( , key = ) ) with is tripped up by the fact the values that occur once can be specified not as an integer instead of a length of list 1.
I'd like to convert between dictionaries of the form {'a':3, 'b':0, 'c':[2,4], 'd':[1,5]} and lists ['b', 'd', 'c', 'a', 'c', 'd'] (the positions of the items in list being specified by the values in the dictionary).
The function list_to_dictionary should have a key for each item appearing in the list with the value giving the location in the list. In case an item appears more than once, the value should be a list storing all of those locations.
The function dictionary_to_list should create a list consisting of the keys of the dictionary, sorted by value. In case the value is not a single integer but instead a list of integers, that key should appear in the list multiple times at the corresponding sorted locations.
My solution was as follows:
def dictionary_to_list(d):
"""inputs a dictionary a:i or a:[i,j], outputs a list of a sorted by i"""
#Converts i to [i] as value of dictionary
for a in d:
if type(d[a])!=type([0,1]):
d[a] = [d[a]]
#Reverses the dictionary from {a:[i,j]...} to {i:a, j:a,...}
reversed_d ={i:a for a in d for i in d[a]}
return [x[1] for x in sorted(reversed_d.items(), key=lambda x:x[0])]
def list_to_dictionary(x):
d = {}
for i in range(len(x)):
a = x[i]
if a in d:
d[a].append(i)
else:
d[a]=[i]
#Creates {a:[i], b:[j,k],...}
for a in d:
if len(d[a])==1:
d[a] = d[a][0]
#Converts to {a:i, b:[j,k],...}
return d
I can't change the problem to have lists of length 1 in place of the single integers as the values of the dictionaries due to the interaction with the rest of my code. It seems like there should be a simple way to handle this but I can't figure it out. A better solution here would have several applications for my python scripts.
Thanks
def dictionary_to_list(data):
result = {}
for key, value in data.items():
if isinstance(value, list):
for index in value:
result[index] = key
else:
result[value] = key
return [result[key] for key in sorted(result)]
def list_to_dictionary(data):
result = {}
for index, char in enumerate(data):
result.setdefault(char, [])
result[char].append(index)
return dict((key, value[0]) if len(value) == 1 else (key, value) for key, value in result.items())
dictData = {'a':3, 'b':0, 'c':[2,4], 'd':[1,5]}
listData = ['b', 'd', 'c', 'a', 'c', 'd']
print dictionary_to_list(dictData)
print list_to_dictionary(listData)
Output
['b', 'd', 'c', 'a', 'c', 'd']
{'a': 3, 'c': [2, 4], 'b': 0, 'd': [1, 5]}
In [17]: d = {'a':3, 'b':0, 'c':[2,4], 'd':[1,5]}
In [18]: sorted(list(itertools.chain.from_iterable([[k]*(1 if isinstance(d[k], int) else len(d[k])) for k in d])), key=lambda i:d[i] if isinstance(d[i], int) else d[i].pop(0))
Out[18]: ['b', 'd', 'c', 'a', 'c', 'd']
The call is to:
sorted(
list(
itertools.chain.from_iterable(
[[k]*(1 if isinstance(d[k], int) else len(d[k]))
for k in d
]
)
),
key=lambda i:d[i] if isinstance(d[i], int) else d[i].pop(0)
)
The idea is that the first part (i.e. list(itertools.chain.from_iterable([[k]*(1 if isinstance(d[k], int) else len(d[k])) for k in d])) creates a list of the keys in d, repeating by the number of values associated with it. So if a key has as single int (or a list containing only one int) as its value, it appears once in this list; else, it appears as many times as there are items in the list.
Next, we assume that the values are sorted (trivial to do as a pre-processing step, otherwise). So now, what we do is to sort the keys by their first value. If they have only a single int as their value, it is considered; else, the first element in the list containing all its values. This first element is also removed from the list (by the call to pop) so that subsequent occurrences of the same key won't reuse the same value
If you'd like to do this without the explicit typecheck, then you could listify all values as a preprocessing step:
In [22]: d = {'a':3, 'b':0, 'c':[2,4], 'd':[1,5]}
In [23]: d = {k:v if isinstance(v, list) else [v] for k,v in d.iteritems()}
In [24]: d
Out[24]: {'a': [3], 'b': [0], 'c': [2, 4], 'd': [1, 5]}
In [25]: sorted(list(itertools.chain.from_iterable([[k]*len(d[k]) for k in d])), key=lambda i:d[i].pop(0))
Out[25]: ['b', 'd', 'c', 'a', 'c', 'd']
def dictionary_to_list(d):
return [k[0] for k in sorted(list(((key,n) for key, value in d.items() if isinstance(value, list) for n in value))+\
[(key, value) for key, value in d.items() if not isinstance(value, list)], key=lambda k:k[1])]
def list_to_dictionary(l):
d = {}
for i, c in enumerate(l):
if c in d:
if isinstance(d[c], list):
d[c].append(i)
else:
d[c] = [d[c], i]
else:
d[c] = i
return d
l = dictionary_to_list({'a':3, 'b':0, 'c':[2,4], 'd':[1,5]})
print(l)
print(list_to_dictionary(l))

Categories