Replace character in string and form all possible combination - python

I am doing text recognition with pytesseract. Sometimes text is not properly extracted.
For example, "DDR4" might be interpreted as "ODR4"
Hence I have a dictionary which record all possible escape, and code to detect how many char needed to be replace and its index for example,
my_dictionary= {
'D': ['O', '0'],
'O': 'D',
'0': 'D'
}
user_input = "DDR4"
char_to_replace = 0
char_index = []
for index, val in enumerate(user_input):
if val in my_dictionary:
char_to_replace += 1
char_index.append(index)
In this case, how could I produce a list of all possible combination, for example
D0R4, DOR4, 00R4, 0OR4, OOR4, O0R4, 0DR4, ODR4
Appreciate for any inputs

This is what i have come up with, pretty ugly code and im sure it can be done much easier with itertools, but what the heck - i hope it helps:
my_dictionary = {
'D': ['O', '0'],
'O': 'D',
'0': 'D'
}
user_input = "DDR4"
def replace_char(variable=None, replace_index=None, replace_with=None):
"""Supplementary function"""
return variable[:replace_index] + replace_with + variable[replace_index+1:]
# create maximum required iterations (max len of list in dict)
number_of_iterations_required = max([len(f) for f in my_dictionary.values()])
# list for preliminary word combinations
baseword_combinations = [user_input]
for key, val in my_dictionary.items():
for idx, char in enumerate(user_input):
if char == key:
for v in val:
baseword_combinations.append(replace_char(variable=user_input, replace_index=idx, replace_with=v))
# list for final returns, again append initial input
possible_combinations = [user_input]
for word in baseword_combinations:
for idx, char in enumerate(word):
for key, val in my_dictionary.items():
for v in val:
if char == key:
possible_combinations.append(replace_char(variable=word, replace_index=idx, replace_with=v))
if char == val:
possible_combinations.append(replace_char(variable=word, replace_index=idx, replace_with=key))
# get rid of duplicates, print result
print(list(set(possible_combinations)))
Result:
['OOR4', '00R4', 'ODR4', 'D0R4', '0DR4', 'DDR4', '0OR4', 'O0R4', 'DOR4']
Edit
The part with the number_of_iterations_required was unused in my above code, also i reworked it a little to use list comprehension - which makes it much less understandable, but much shorter, so here you go:
my_dictionary = {
'D': ['O', '0'],
'O': 'D',
'0': 'D'
}
user_input = "DDR4"
def replace_char(variable=None, replace_index=None, replace_with=None):
"""Supplementary function"""
return variable[:replace_index] + replace_with + variable[replace_index+1:]
# list for preliminary word combinations
base = [user_input]
base.extend([replace_char(user_input, idx, v) for key, val in my_dictionary.items()
for idx, char in enumerate(user_input) for v in val if char == key])
# list for final results
final_results = [user_input]
final_results.extend([replace_char(word, idx, key) for word in base for idx, char in enumerate(word)
for key, val in my_dictionary.items() for v in val if char == val])
result = list(set(final_results))
print(result)

I'm try to solve this problem in this way,hope that can help you:
import itertools as it
my_list= ['D','O','0']
result = []
input = 'DDR4'
# 'DDR4' -> '**R4'
for i in range(len(my_list)):
if input[i] in my_list:
input = input.replace(input[i],'*')
for e in it.product('DO0',repeat=input.count('*')):
a = list(e)
input_copy = input
for i in a:
# print(i)
input_copy = input_copy.replace('*',i,1)
result.append(input_copy)
result:
['DDR4', 'DOR4', 'D0R4', 'ODR4', 'OOR4', 'O0R4', '0DR4', '0OR4', '00R4']

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'

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

I have two letters with equal count in a name, how to print both the letters with their count

I need to display the letter and it's count if it has maximum count in a name. However, I have two letters (n:2, u:2) with equal count in a name, how to print both the letters with their count as they have maximum and equal count. I could only do for one letter.
name = 'Annuu'
name = name.lower()
names = set(name)
highest = 0
p = ''
for i in names:
if name.count(i) > highest:
highest = name.count(i)
p = i
print(f"{p} {highest}")
You can use Counter object to find the count.
Then find the maximum count to filter the letters.
from collections import Counter
name = "annuu"
count_dict = Counter(name)
max_count = max(count_dict.values())
for letter, count in count_dict.items():
if count == max_count:
print(letter, count)
This is without using any imports:
name = "Onnuu"
name = name.lower()
names = set(name)
print(names)
l = []
for i in names:
l.append((name.count(i),i))
l.sort(reverse = True)
for i in l:
if l[0][0] == i[0]:
print(i[1])
Store the values in dict and find the max_frequency
name = 'Annuu'
name = name.lower()
d={}
for i in name:
d[i]=d.get(i,0)+1
max_freq = max((d.values()))
for k,v in sorted(d.items(),key=lambda (x,y):(y,x), reverse=True):
if v == max_freq:
print(k,v)
else:
break
The following code works and produces the output:
The maximum characters and their respective count is as follows:
n 2
u 2
name = 'Annuu'
name = name.lower()
names = set(name)
name_count_dict = {} # Use dictionary because of easy mapping between character and respective max
for current_char in names:
# First save the counts in a dictionary
name_count_dict[current_char] = name.count(current_char)
# Use the max function to find the max (only one max at this point but will find the remaining in the lines below)
max_char = max(name_count_dict, key=name_count_dict.get)
max_value = name_count_dict[max_char]
# Find all other characters which match the max-value, i.e. all maximums
array_of_all_maxes = [k for k, v in name_count_dict.items() if v == max(name_count_dict.values())]
print("The maximum characters and their respective count is as follows:")
for max_chars in array_of_all_maxes:
print(f"{max_chars} {max_value}")
I think this would be a simple solution for the problem without using any external package like collections.
Here, I written 2 test cases and repeated the same lines of code. You haven't to do like that. What you have more than 2, 3 etc. So it's better to write any other function to test the code by passing different values to it.
def get_count_and_highest (name):
name = name.lower()
names = set(name)
highest = 0
d = {}
for ch in names:
count = name.count(ch)
if count >= highest:
highest = count
if highest in d:
d[highest].append(ch)
else:
d[highest] = [ch]
return highest, d
#Test case 1
highest, d = get_count_and_highest("Annuu")
l = d.get(highest, []) # in case if dictionary d is empty then l will be an empty list
output = {ch: highest for ch in l}
print(highest) # 2
print(d) # {1: ['a'], 2: ['n', 'u']}
print(l) # ['n', 'u']
print (output) # {'u': 2, 'n': 2}
# Test case 2
highest, d = get_count_and_highest("Babylon python new one")
l = d.get(highest, []) # in case if dictionary d is empty then l will be an empty list
output = {ch: highest for ch in l}
print(highest) # 4
print(d) # {3: ['o', 'p', 'l', 'b', 't', ' ', 'h', 'y', 'w'], 4: ['n', 'e', 'a']}
print(l) # ['n', 'e', 'a']
print (output) # {'n': 4, 'e': 4, 'a': 4}
An example with collections.Counter
from collections import Counter
name = 'Annuu'
c = Counter(name.lower())
mc = c.most_common()
max_count = mc[0][1]
for i, x in enumerate(mc):
if x[1] < max_count:
break
print(mc[:i+1]) # [('n', 2), ('u', 2)]

Counting triplets in a DNA-sequence

I want to make a code which counts all triplets in a sequence. I've read a plenty of posts so far, but none of them could help me.
This is my code:
def cnt(seq):
mydict = {}
if len(seq) % 3 == 0:
a = [x for x in seq]
for i in range(len(seq)//3):
b = ''.join(a[(0+3*i):(3+3*i)])
for base1 in ['A', 'T', 'G', 'C']:
for base2 in ['A', 'T', 'G', 'C']:
for base3 in ['A', 'T', 'G', 'C']:
triplet = base1 + base2 + base3
if b == triplet:
mydict[b] = 1
for key in sorted(mydict):
print("%s: %s" % (key, mydict[key]))
else:
print("Error")
Does Biopython provide a function to solve this problem?
EDIT:
Note that, for instance, in the sequence 'ATGAAG', 'TGA' or 'GAA' are not "valid" triplets, only 'ATG' and 'AAG', because in biology and bioinformatics, we read it 'ATG' and 'AAG', thats the information we need to translate it or whatever else.
You can imagine it as a sequence of words, for example "Hello world". The way we read it is "Hello" and "world", not "Hello", "ello ", "llo w",...
It took me a while to understand that you do not want to count the number of codons but the frequency of each codon. Your title is a bit misleading in this respect. Anyway, you can employ collections.Counter for your task:
from collections import Counter
def cnt(seq):
if len(seq) % 3 == 0:
#split list into codons of three
codons = [seq[i:i+3] for i in range(0, len(seq), 3)]
#create Counter dictionary for it
codon_freq = Counter(codons)
#determine number of codons, should be len(seq) // 3
n = sum(codon_freq.values())
#print out all entries in an appealing form
for key in sorted(codon_freq):
print("{}: {} = {:5.2f}%".format(key, codon_freq[key], codon_freq[key] * 100 / n))
#or just the dictionary
#print(codon_freq)
else:
print("Error")
seq = "ATCGCAGAAATCCGCAGAATC"
cnt(seq)
Sample output:
AGA: 1 = 14.29%
ATC: 3 = 42.86%
CGC: 1 = 14.29%
GAA: 1 = 14.29%
GCA: 1 = 14.29%
You can use clever techniques, as suggested in the other answers, but I will build a solution starting from your code, which is almost working: Your problem is that every time you do mydict[b] = 1, you reset the count of b to 1.
A minimal fix
You could solve this by testing if the key is present, if not, create the entry in the dict, then increment the value, but there are more convenient tools in python.
A minimal change to your code would be to use a defaultdict(int) instead of a dict. Whenever a new key is encountered, it is assumed to have the associated default value for an int: 0. So you can increment the value instead of resetting:
from collections import defaultdict
def cnt(seq):
# instanciate a defaultdict that creates ints when necessary
mydict = defaultdict(int)
if len(seq) % 3 == 0:
a = [x for x in seq]
for i in range(len(seq)//3):
b = ''.join(a[(0+3*i):(3+3*i)])
for base1 in ['A', 'T', 'G', 'C']:
for base2 in ['A', 'T', 'G', 'C']:
for base3 in ['A', 'T', 'G', 'C']:
triplet = base1 + base2 + base3
if b == triplet:
# increment the existing count (or the default 0 value)
mydict[b] += 1
for key in sorted(mydict):
print("%s: %s" % (key, mydict[key]))
else:
print("Error")
It works as desired:
cnt("ACTGGCACT")
ACT: 2
GGC: 1
Some possible improvements
Now let's try to improve your code a bit.
First, as I wrote in the comments, let's avoid the un-necessary conversion of your sequence to a list, and use a better variable name for the currently counted codon:
from collections import defaultdict
def cnt(seq):
mydict = defaultdict(int)
if len(seq) % 3 == 0:
a = [x for x in seq]
for i in range(len(seq)//3):
codon = seq[(0+3*i):(3+3*i)]
for base1 in ['A', 'T', 'G', 'C']:
for base2 in ['A', 'T', 'G', 'C']:
for base3 in ['A', 'T', 'G', 'C']:
triplet = base1 + base2 + base3
if codon == triplet:
mydict[codon] += 1
for key in sorted(mydict):
print("%s: %s" % (key, mydict[key]))
else:
print("Error")
Now lets simplify the nested loop part, trying all possible codons, by generating in advance the set of possible codons:
from collections import defaultdict
from itertools import product
codons = {
"".join((base1, base2, base3))
for (base1, base2, base3) in product("ACGT", "ACGT", "ACGT")}
def cnt(seq):
mydict = defaultdict(int)
if len(seq) % 3 == 0:
a = [x for x in seq]
for i in range(len(seq)//3):
codon = seq[(0+3*i):(3+3*i)]
if codon in codons:
mydict[codon] += 1
for key in sorted(mydict):
print("%s: %s" % (key, mydict[key]))
else:
print("Error")
Now, your code simply ignores the triplets that are not valid codons. Maybe you should instead issue a warning:
from collections import defaultdict
from itertools import product
codons = {
"".join((base1, base2, base3))
for (base1, base2, base3) in product("ACGT", "ACGT", "ACGT")}
def cnt(seq):
mydict = defaultdict(int)
if len(seq) % 3 == 0:
a = [x for x in seq]
for i in range(len(seq)//3):
codon = seq[(0+3*i):(3+3*i)]
# We count even invalid triplets
mydict[codon] += 1
# We display counts only for valid triplets
for codon in sorted(codons):
print("%s: %s" % (codon, mydict[codon]))
# We compute the set of invalid triplets:
# the keys that are not codons.
invalid = mydict.keys() - codons
# An empty set has value False in a test.
# We issue a warning if the set is not empty.
if invalid:
print("Warning! There are invalid triplets:")
print(", ".join(sorted(invalid)))
else:
print("Error")
A more fancy solution
Now a more fancy solution, using cytoolz (probably needs to be installed because it is not part of usual python distributions: pip3 install cytoolz, if you are using pip):
from collections import Counter
from itertools import product, repeat
from cytoolz import groupby, keymap, partition
# To make strings out of lists of strings
CAT = "".join
# The star "extracts" the elements from the result of repeat,
# so that product has 3 arguments, and not a single one
codons = {CAT(bases) for bases in product(*repeat("ACGT", 3))}
def cnt(seq):
# keymap(CAT, ...) transforms the keys (that are tuples of letters)
# into strings
# if len(seq) is not a multiple of 3, pad="-" will append "-"
# to complete the last triplet (which will be an invalid one)
codon_counts = keymap(CAT, Counter(partition(3, seq, pad="-")))
# separate encountered codons into valids and invalids
codons_by_validity = groupby(codons.__contains__, codon_counts.keys())
# get allows to provide a default value,
# in case one of the categories is not present
valids = codons_by_validity.get(True, [])
invalids = codons_by_validity.get(False, [])
# We display counts only for valid triplets
for codon in sorted(valids):
print("%s: %s" % (codon, codon_counts[codon]))
# We issue a warning if there are invalid codons.
if invalids:
print("Warning! There are invalid triplets:")
print(", ".join(sorted(invalids)))
Hope this helps.
You could do something like this:
from itertools import product
seq = 'ATGATG'
all_triplets = [seq[i:i+3] for i in range(len(seq)) if i <= len(seq)-3]
# this gives ['ATG', 'TGA', 'GAT', 'ATG']
# add more valid_triplets here
valid_triplets = ['ATG']
len([(i, j) for i, j in product(valid_triplets, all_triplets) if i==j])
Output:
2
It is unclear what output is expected. Here we use one of many grouping functions from more_itertools to build adjacent triplets or "codons".
import more_itertools as mit
seq = "ATGATG"
codons = ["".join(w) for w in mit.grouper(3, seq)]
codons
# ['ATG', 'ATG']
Count the number of codons by calling len.
len(triplets)
# 2
For more detailed analysis, consider splitting the problem into smaller functions that (1) extract codons and (2) compute occurrences.
Code
import collections as ct
def split_codons(seq):
"Return codons from a sequence; raise for bad sequences."
for w in mit.windowed(seq, n=3, step=3, fillvalue=""):
part = "".join(w)
if len(part) < 3:
raise ValueError(f"Sequence not divisible by 3. Got extra '{part}'.")
yield part
def count_codons(codons):
"""Return dictionary of codon occurences."""
dd = ct.defaultdict(int)
for i, c in enumerate(codons, 1):
dd[c] += 1
return {k: (v, 100 * v/i) for k, v in dd.items()}
Demo
>>> seq = "ATCGCAGAAATCCGCAGAATC"
>>> bad_seq = "ATCGCAGAAATCCGCAGAATCA"
>>> list(split_codons(seq))
['ATC', 'GCA', 'GAA', 'ATC', 'CGC', 'AGA', 'ATC']
>>> list(split_codons(bad_seq))
ValueError: Sequence not divisible by 3. Got extra 'A'.
>>> count_codons(split_codons(seq))
{'ATC': (3, 42.857142857142854),
'GCA': (1, 14.285714285714286),
'GAA': (1, 14.285714285714286),
'CGC': (1, 14.285714285714286),
'AGA': (1, 14.285714285714286)}

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