Python - Replace string characters and get all combinations - python

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-----

Related

Filling a dictionary using for loop in a set

I have 2 strings:
s1 = "Are they here"
s2 = "yes, they are here"
I want to create a dictionary (e) that has as key the maximum number of times each shared element is present in the string that contains it the most and as value the element (i.e. the "y" is contained once in s1 and twice in s2. Therefore I want a dict that goes:
e = {2:y} # and so on
To describe my code, I thought of creating a list (c) with all the shared elements:
c = ['r', 'e', 't', 'h', 'e', 'y', 'h', 'e', 'r', 'e', 'y', 'e', 't', 'h', 'e', 'y', 'r', 'e', 'h', 'e', 'r', 'e']
then switch it to a set to eliminate duplicates and using them as iterators:
d = {'h', 'y', 'r', 't', 'e'}
Ultimately I thought of using a for loop to fill the dict (e) by iterating every element in d and reporting the maximum times it was present.
Here's my full code
please note that I don't want to use any library.
Also note that the code works with dict comprehension:
def mix(s1, s2):
c = [] # create a var to be filled with all shared chars
for i in s1:
if i != " ":
if i in s2:
c.append(i)
for i in s2:
if i != " ":
if i in s1:
c.append(i) # end of 1st process
d = set(c) # remove duplicates
e = {} # create a dict to align counting and relative char
for i in d:
a = s1.count(i)
b = s2.count(i)
m = max(a, b)
e[m] = i
# z = {i:max(s1.count(i), s2.count(i)) for i in d} this is what actually works
return e # z works instead
The issue I get is that the for loop stops after 3 iteration.
Edit: I see that Rakshith B S has made a better version of my comment, refer to thiers.
I'll start by saying I'm an amateur, and the following can absolutely be simplified.
First, decide about capitalization, A != a, use str.lower or str.upper.
Second, switching the dictionary to be {'letter':count} would make everything easier.
Then, it would most likely be easier to create two dictionaries to count the unique letters in each string.
d1 = {}
s1 = s1.lower()
for letter in s1:
if letter != " ":
if letter in d1:
d1[letter] += 1 # if in dict, add one to count
else:
d1[letter] = 1 #add new letter to dict
d2 = {}
s2 = s2.lower()
for letter in s2:
if letter != " ":
if letter in d2:
d2[letter] += 1 # if in dict, add one to count
else:
d2[letter] = 1 #add new letter to dict
That should make two dictionaries, for loop it to compare and append the max values (this part can be made more efficiently).
d3 = {}
for let in d1:
if let not in d2:
d3[let] = d1.get(let)
if let in d2:
if d1[let] >= d2[let]:
d3[let] = d1.get(let)
else:
d3[let] = d2.get(let)
for let in d2:
if let not in d1:
d3[let] = d2.get(let)
del d3[',']
This should at least get you on the right track.
I have just realized that sets can obviously have UNIQUE values as keys, so, of course my code will be display "partially".
When it gets the same key, it overwrites it.
So using the element as key will work and the for loop can be like so:
for i in d:
a = s1.count(i)
b = s2.count(i)
m = max(a, b)
e[i] = m
def mix(s1, s2):
dict1 = dict()
dict2 = dict()
for i in s1:
if i != " " and i != ",":
if i in dict1:
dict1[i] += 1
else:
dict1[i] = 1
for i in s2:
if i != " " and i != ",":
if i in dict2:
dict2[i] += 1
else:
dict2[i] = 1
# print(dict1)
# print(dict2)
for key, value in dict2.items():
if key in dict1:
# print(f' check {key}, {value}')
if value >= dict1[key]:
dict1[key] = value
else:
dict1[key] = value
#print(f' create {key}, {value}')
return {v: k for k, v in dict1.items()} #inverted
s1 = "eeeeaaabbbcccc"
s2 = "eeeeeaaa"
print(mix(s1, s2))
Why create a merged list and recheck against the counter set
Here I've compared values from dict1( which is s1) and dict2(again s2) and overwritten dict1 if the value is high else if its not found I've assigned it as the highest
OUTPUTS:
{'e': 5, 'a': 3, 'b': 3, 'c': 4}
{5: 'e', 3: 'b', 4: 'c'}
This might end up overwriting as 'a' is overwritten by 'b'

how can i get a dictionary to produce two dictionaries with some keys the same

I have a dictionary, what:
what = {'a': ['b'], 'c': ['d\n', 'e\n', 'f'], 'd': ['f', 'g']}
and need to get the items with '/n' separate and without '/n' (order is important, need to sort the values.):
[[{'a': ['b'], 'c': ['f'], 'd': ['f', 'g']}], [{'c': ['d\n', 'e\n']}]]
This is what I tried:
def lst(profiles_file: TextIO):
solve_lst = []
new_lst = fix_files(profiles_file)
for k, v in new_lst.items():
for i in v:
if i.find('\n') != -1:
get_index = [v.index(i)]
solve_lst.append(get_index)
return solve_lst
How can i get this without doing anything to complicated?
Here's a solution using defaultdict
from collections import defaultdict
def lst(profiles_file: TextIO):
initial_dict = fix_files(profiles_file)
with_n = defaultdict(list)
without_n = defaultdict(list)
for k, v in initial_dict.items():
for item in v:
if '\n' in item:
with_n[k].append(item)
else:
without_n[k].append(item)
return [without_n, with_n]

Combine elements of list

I have a problem with Python and hope someone can help me. I have a list, for example this one:
list = [['a','b','c'],['a','c1','d1'],['b','c1','c2']]
I want to combine the list in a way that all arrays with the same index[0] will be together, so then it will be like:
a, b, c, c1, d1
b, c1, c2
I tried something like this, but I did not get it working..
list = [['a','b','c'],['a','c1','d1'],['b','c1','c2']]
empty_list = []
for i in list:
if i not in empty_list:
empty_list.append(i)
print empty_list
Can someone help me?
You can try this :)
old_list = [['a','b','c'],['a','c1','d1'],['b','c1','c2']]
prev = None
empty_list = []
for l in old_list: # iterate through each sub list, sub list as l
if l[0] == prev:
# append your elements to existing sub list
for i in l: # iterate through each element in sub list
if i not in empty_list[-1]:
empty_list[-1].append(i)
else:
empty_list.append(l) # create new sub list
prev = l[0] # update prev
print(empty_list)
# [['a', 'b', 'c', 'c1', 'd1'], ['b', 'c1', 'c2']]
Using itertools.groupby:
from itertools import groupby
from operator import itemgetter
listt = [['a','b','c'],['a','c1','d1'],['b','c1','c2']]
grouped = [list(g) for _,g in groupby(listt,itemgetter(0))]
result = [[item for sslist in slist for item in sslist] for slist in grouped]
An OrderedDict can do most of the work:
from collections import OrderedDict
l = [['a','b','c'], ['a','c1','d1'], ['b','c1','c2']]
d = OrderedDict()
for el in l:
d.setdefault(el[0], el[0:1]).extend(el[1:])
print(d.values())
you can also try using defaultdict(list)
l = [['a','b','c'], ['a','c1','d1'], ['b','c1','c2']]
from collections import defaultdict
d_dict = defaultdict(list)
for i in l:
d_dict[i[0]].extend(i[1:])
[ list(k) + v for k, v in d_dict.items() ]
Output:
[['a', 'b', 'c', 'c1', 'd1'], ['b', 'c1', 'c2']]

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']]

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

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

Categories