Manipulating data in a tuple from dictionary keys - python

I have a list of tuples (variable name 'values') in the format
(1, 'K', '-', 0.8878048780487805)
(2, 'Y', '-', 0.32882882882882886)
(3, 'E', '-', 0.7216494845360825)
(4, 'Y', 'B', 0.13963963963963963)
(5, 'V', '-', 0.28169014084507044)
(6, 'E', '-', 0.39690721649484534)
.....
And I have this dictionary
ratios = {'K': 1.1512, 'A': 1.217, 'R': 1.1048, 'N': 1.242, 'D': 1.184,
'C': 1.237, 'Q': 1.1364, 'G': 1.2381, 'H': 1.2174, 'I': 1.1657,
'L': 1.1657, 'M': 1.1914, 'F': 1.2182, 'P': 1.1691, 'S': 1.1923,
'T': 1.2113, 'W': 1.2555, 'Y': 1.1847, 'V':1.2254}
What I would like to do is go through my list and divide the element in index 3 by the value in the dictionary that corresponds to the letter in index 1. For example, 0.8878... would be divided by 1.1512 because of the K, for the next one 0.3288.... would be divided by 1.1847 because of the Y, and so on. I haven't found a way to do this yet.

A list comprehension should work great here
tuple_list =[(1, 'K', '-', 0.8878048780487805),
(2, 'Y', '-', 0.32882882882882886),
(3, 'E', '-', 0.7216494845360825),
(4, 'Y', 'B', 0.13963963963963963),
(5, 'V', '-', 0.28169014084507044),
(6, 'E', '-', 0.39690721649484534)]
ratios = {'K': 1.1512, 'A': 1.217, 'R': 1.1048, 'N': 1.242, 'D': 1.184,
'C': 1.237, 'Q': 1.1364, 'G': 1.2381, 'H': 1.2174, 'I': 1.1657,
'L': 1.1657, 'M': 1.1914, 'F': 1.2182, 'P': 1.1691, 'S': 1.1923,
'T': 1.2113, 'W': 1.2555, 'Y': 1.1847, 'V':1.2254}
[tup[3]/ratios.get(tup[1],1) for tup in tuple_list]
use get so it will not throw an error if key is not present in ratios
Output
[0.7711995118561331,
0.2775629516576592,
0.7216494845360825,
0.11786919864914291,
0.22987607380860978,
0.39690721649484534]

for row in values:
print(row[1], row[3]/ratios[row[1]])

my_tuple_list = [
(1, 'K', '-', 0.8878048780487805),
.....
]
result = {}
for (idx, key, _, number) in my_tuple_list:
result[key] = number / ratios[key]
print(result)
This solution shows how to unpack a list of tuples in a clear manner that may be easier to read than list comprehensions

Related

Updating values of a dictionary by comparing two dictionaries of unequal lengths with similar keys

I have a list of characters from the list character_list = ['A', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'Y'] which I am using to track the number of character occurrences in a given string. My approach is to make a dictionary that has each character in character_list as its key and its count as the value. If a character within character_list is not present within string then its value will be None
I have a string which I used to make a dictionary to count the frequency of each character within the string.
from collections import Counter
character_list = ['A', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'Y']
string ='LDPQKLFWWWWWWWWWWWWWWWWWDKIRERNDCEQGHILYKMFPSTRTKRCQTSGGGPHDGPQDLDRELFKLKQMGKDMNTFPNFTFEDPKFE'
string_counts = dict(sorted((Counter(string)).items(), key=lambda tuple_element: tuple_element[0] ) )
string_counts yields :
{'C': 2, 'D': 8, 'E': 5, 'F': 7, 'G': 6, 'H': 2, 'I': 2, 'K': 8, 'L': 6, 'M': 3, 'N': 3, 'P': 6, 'Q': 5, 'R': 5, 'S': 2, 'T': 5, 'W': 17, 'Y': 1}
Since not all characters in string are in string_counts, character_list and string_count are of different lengths and won't have all the same keys. This makes constructing the dictionary difficult.
To get around this I tried making a dictionary of Boolean Values, where if the character is present in both string and character_list the value will be True and None if the character is not present in string in order to make them both the same length. I did that using zip and cycle
from itertools import cycle
bool_dict = dict()
for string_count_letter, char_letter in zip( cycle( string_counts.keys() ), character_list):
if char_letter in string_counts.keys():
bool_dict[char_letter] = True
else :
bool_dict[char_letter] = None
print(bool_dict)
bool_dict yields:
{'A': None, 'C': True, 'D': True, 'E': True, 'F': True, 'G': True, 'H': True, 'I': True, 'K': True, 'L': True, 'M': True, 'N': True, 'P': True, 'Q': True, 'R': True, 'S': True, 'T': True, 'V': None, 'W': True, 'Y': True}
Then from here I want my final dictionary to be:
dict_i_want = {'A': None, 'C': 2, 'D': 8, 'E': 5, 'F': 7, 'G': 6, 'H': 2, 'I': 2, 'K': 8, 'L': 6, 'M': 3, 'N': 6, 'P': 6, 'Q': 5, 'R': 2, 'S': 5, 'T': 5, 'V': None,'W':17,'Y':1}
}
but using this code that updates bool_dict values if the value is True to the frequency of a character within ``string``` gets me a dictionary that mismatches the frequencies to the wrong character:
string_count_values = list(string_counts.values())
bool_values = list(bool_dict.values())
bool_keys = list(bool_dict.keys())
for string_count_v, bool_v, bool_k in zip( cycle(string_count_values),bool_values , bool_keys ):
print(bool_v)
if bool_v == True :
bool_dict[bool_k] = string_count_v
print(bool_dict)
bool_dict{'A': None, 'C': 8, 'D': 5, 'E': 7, 'F': 6, 'G': 2, 'H': 2, 'I': 8, 'K': 6, 'L': 3, 'M': 3, 'N': 6, 'P': 5, 'Q': 5, 'R': 2, 'S': 5, 'T': 17, 'V': None, 'W': 2, 'Y': 8} # this is wrong
#compared to
dict_i_want = {'A': None, 'C': 2, 'D': 8, 'E': 5, 'F': 7, 'G': 6, 'H': 2, 'I': 2, 'K': 8, 'L': 6, 'M': 3, 'N': 6, 'P': 6, 'Q': 5, 'R': 2, 'S': 5, 'T': 5, 'V': None,'W':17,'Y':1}
}
# this is right
All you need:
from collections import Counter
character_list = ['A', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'Y']
string ='LDPQKLFWWWWWWWWWWWWWWWWWDKIRERNDCEQGHILYKMFPSTRTKRCQTSGGGPHDGPQDLDRELFKLKQMGKDMNTFPNFTFEDPKFE'
c = Counter(string)
dict_i_want = {k: None if k not in c else c[k] for k in character_list}
print(dict_i_want)
Result:
{'A': None, 'C': 2, 'D': 8, 'E': 5, 'F': 7, 'G': 6, 'H': 2, 'I': 2, 'K': 8, 'L': 6, 'M': 3, 'N': 3, 'P': 6, 'Q': 5, 'R': 5, 'S': 2, 'T': 5, 'V': None, 'W': 17, 'Y': 1}
What I'd prefer:
dict_i_want = {k: 0 if k not in c else c[k] for k in character_list}
And then even this works:
dict_i_want = {k: c[k] for k in character_list}
Because a Counter returns 0 for a key that's not in it anyway.
(By the way, naming a variable string shadows the Python module string - not used very commonly these days, but you may want to avoid shadowing it all the same and use a more descriptive name for a variable, e.g. sample)

Is there are more efficient method for creating a dictionary of letters mapped to letters shifted by a set integer?

I have written a function which takes an integer and returns a dictionary where the letters of the alphabet are mapped to an alphabet shifted by the integer. It has the requirement that lowercase and uppercase letters must correpsond, that is if a -> b then A->B.
I'm fairly new to python and have little experience with creating lists so I'm just wondering if there is an alternative more efficient or even just more elegant way to complete the same outcome.
import string
dictionary = []
for letter in string.ascii_lowercase + string.ascii_uppercase:
dictionary.append(letter)
def build_shift(shift):
shifted_dictionary = []
for i in range(0,26):
shifted_dictionary.append(dictionary[(i + shift) % 26])
for i in range(26, 53):
shifted_dictionary.append(dictionary[(( i + shift) % 26) + 26])
return (shifted_dictionary)
mapped_dictionary = dict( zip(dictionary, build_shift(2)))
print(mapped_dictionary)
You can use a dict comprehension that iterates characters over each of the lowercase and uppercase strings to output the character mapped to the one at the current index plus the offset and reduced with a modulo of 26. Below is an example using 2 as the offset:
{
c: s[(i + 2) % 26]
for s in (string.ascii_lowercase, string.ascii_uppercase)
for i, c in enumerate(s)
}
You can slice the string and get the desired result in less number of operations.
import string
shift = 2
changed_lc = f'{string.ascii_lowercase[shift:]}{string.ascii_lowercase[:shift]}'
changed_up = f'{string.ascii_uppercase[shift:]}{string.ascii_uppercase[:shift]}'
changed = f'{changed_lc}{changed_up}'
mapped_dictionary = dict(zip(string.ascii_letters, changed))
print(mapped_dictionary)
output:
{'a': 'c', 'b': 'd', 'c': 'e', 'd': 'f', 'e': 'g', 'f': 'h', 'g': 'i', 'h': 'j', 'i': 'k', 'j': 'l', 'k': 'm', 'l': 'n', 'm': 'o', 'n': 'p', 'o': 'q', 'p': 'r', 'q': 's', 'r': 't', 's': 'u', 't': 'v', 'u': 'w', 'v': 'x', 'w': 'y', 'x': 'z', 'y': 'a', 'z': 'b', 'A': 'C', 'B': 'D', 'C': 'E', 'D': 'F', 'E': 'G', 'F': 'H', 'G': 'I', 'H': 'J', 'I': 'K', 'J': 'L', 'K': 'M', 'L': 'N', 'M': 'O', 'N': 'P', 'O': 'Q', 'P': 'R', 'Q': 'S', 'R': 'T', 'S': 'U', 'T': 'V', 'U': 'W', 'V': 'X', 'W': 'Y', 'X': 'Z', 'Y': 'A', 'Z': 'B'}

My ascii value dictionary doesn't work

I got an assignment yesterday about Rot and I was told to write all 26 of them in code, or atleast a program that did, so I thought I'd start with the dictionary, since I wanted to also call for a certain rot I came up with this.
def generate_rotated_dictionary(n):
for i in range(0, 26):
letter = alphabet_list[i]
new_let = ord(letter) + n
new_let = chr(new_let)
rot_dic.update({letter: new_let})
return(rot_dic)
Unfortunately, this doesn't work and results in:
{'a': 'b', 'c': 'd', 'b': 'c', 'e': 'f', 'd': 'e', 'g': 'h', 'f': 'g', 'i':
'j', 'h': 'i', 'k': 'l', 'j': 'k', 'm': 'n', 'l': 'm', 'o': 'p', 'n': 'o',
'q': 'r', 'p': 'q', 's': 't', 'r': 's', 'u': 'v', 't': 'u', 'w': 'x', 'v':
'w', 'y': 'z', 'x': 'y', 'z': '{'}
{'a': 'c', 'c': 'e', 'b': 'd', 'e': 'g', 'd': 'f', 'g': 'i', 'f': 'h', 'i':
'k', 'h': 'j', 'k': 'm', 'j': 'l', 'm': 'o', 'l': 'n', 'o': 'q', 'n': 'p',
'q': 's', 'p': 'r', 's': 'u', 'r': 't', 'u': 'w', 't': 'v', 'w': 'y', 'v':
'x', 'y': 'z', 'x': 'z', 'z': '|'}
{'a': 'd', 'c': 'f', 'b': 'e', 'e': 'h', 'd': 'g', 'g': 'j', 'f': 'i', 'i':
'l', 'h': 'k', 'k': 'n', 'j': 'm', 'm': 'p', 'l': 'o', 'o': 'r', 'n': 'q',
'q': 't', 'p': 's', 's': 'v', 'r': 'u', 'u': 'x', 't': 'w', 'w': 'z', 'v':
'y', 'y': '|', 'x': 'z', 'z': '}'}
{'a': 'e', 'c': 'g', 'b': 'f', 'e': 'i', 'd': 'h', 'g': 'k', 'f': 'j', 'i':
'm', 'h': 'l', 'k': 'o', 'j': 'n', 'm': 'q', 'l': 'p', 'o': 's', 'n': 'r',
'q': 'u', 'p': 't', 's': 'w', 'r': 'v', 'u': 'y', 't': 'x', 'w': 'z', 'v':
'z', 'y': '}', 'x': '|', 'z': '~'}
How do I fix it with the ending, I know that it is because of my use of Ascii values, but how do i get this to start with the a again (number 97)
So if you need to rotate alphabet you can use following method:
import string
alph = string.ascii_lowercase
for i in range(26):
print(''.join([alph[(k + i) % 26] for k in range(26)]))
Output is alphabetic strings with offset:
abcdefghijklmnopqrstuvwxyz
bcdefghijklmnopqrstuvwxyza
cdefghijklmnopqrstuvwxyzab
defghijklmnopqrstuvwxyzabc
.....
zabcdefghijklmnopqrstuvwxy
If you need lists instead of strings just remove join().
If you required some specific rotation like 13 then you can use this code inside function with argument:
def rot(i):
return ''.join([alph[(k + i) % 26] for k in range(26)])
This will give rotation for specific number.
Example:
print(rot(13))
Output:
nopqrstuvwxyzabcdefghijklm
l = list('abcdefghijklmnopqrstuvwxyz')
n = 1
rot = dict((c, chr(97 + ((ord(c)-97 + n)%26) )) for c in l)
The comprehension there is a little dense, so lets break it down:
(c, chr(97 + ((ord(c)-97 + n)%26) ))
produces tuples of one character and its shifted counterpart
chr(97 + ((ord(c)-97 + n)%26) )
makes a character
97 + (ord(c)-97 + n)%26
get the value of the old character and calculate how many to shift over.
So, in order to summarize, here's a full method:
# Generates a function which rotate a sequence by an offset
def generate_rot(seq, offset):
l = len(seq)
def rot(c):
n = (seq.index(c) + offset) % l
return seq[n]
return rot
> alpha = map(chr,range(97, 123)) # [a...z]
> rot13 = generate_rot(alpha, 13)
> print(rot13('a'))
n
> print(rot13('n'))
a
> rot21 = generate_rot(alpha, 21)
v
You can then bind it to the alphabet, specifically:
> def generate_alpha_rot(x):
return generate_rot(map(chr,range(97, 123)),x)
# or using a lambda function to create it inline, because why not?
> generate_alpha_rot = lambda x:generate_rot(map(chr,range(97, 123)),x)
> rot22 = generate_alpha_rot(22)
> rot22('a')
w
# Or call it inline:
> generate_alpha_rot(23)('a')
x
Hardly worth a full answer, but you want to take the modulo 26 of new_let before you convert to a character. by doing remainder division, you get how far past 26 the index has gone
Consider:
>>>26 + 13 # rot13 for z
39
>>>39 % 26
13

Dictionary as value in dictionary

There is a dictionary with dictionaries as the value inside like so:
{'a': {'b': 'c', 'd': 'e', 'f': 'g'}, 'h': {'i': 'j', 'k': 'l', 'm': 'n'}}
How is it possible to access, let's say; the 'f' key (producing 'g') for 'a'?
Just chain key look-ups:
outer_dictionary['a']['f']
Here dictionary['a'] returns the value for the key 'a', which is itself a dictionary.
You could also store the intermediary result:
nested_dictionary = outer_dictionary['a']
nested_dictionary['f']
This does the exact same thing, but also leaves another reference to the nested dictionary available as nested_dictionary.
Quick demo:
>>> nested_dictionary = {'a': {'b': 'c', 'd': 'e', 'f': 'g'}, 'h': {'i': 'j', 'k': 'l', 'm': 'n'}}
>>> nested_dictionary['a']
{'b': 'c', 'd': 'e', 'f': 'g'}
>>> nested_dictionary['a']['f']
'g'

Why my code prints others characters? cipher

I want print only letters but it's prints special characters of ASCII. My code:
import string
def caesar(shift):
alphabet = string.ascii_lowercase + string.ascii_uppercase
dict={}
emptylist=[]
int(shift)
for x in alphabet:
emptylist.append(x)
code = ""
for letters in emptylist:
code = chr(ord(letters) + shift)
dict[letters]=code
return dict
caesar(12)
My output:
'm': 'y', 'l': 'x', 'o': '{', 'n': 'z', 'q': '}', 'p': '|', 's': '\x7f', 'r': '~', 'u': '\x81', 't': '\x80', 'w': '\x83', 'v': '\x82', 'y': '\x85', 'x': '\x84', 'z': '\x86'
Correct output:
'm': 'y', 'l': 'x', 'o': 'a', 'n': 'z', 'q': 'c', 'p': 'b', 's': 'e', 'r': 'd', 'u': 'g', 't': 'f', 'w': 'i', 'v': 'h', 'y': 'k', 'x': 'j', 'z': 'l'
Using ord() and changing the character code won't restrict the resulting character to your dictionary.
I'd just find the index of the letter in your dictionary, shift it, and use the modulo operator:
import string
def caesar(shift):
alphabet = string.ascii_uppercase # <- Change it back to what you had before
# and see what will happen.
mapping = {}
for letter in alphabet:
index = alphabet.index(letter)
mapping[letter] = alphabet[(index + shift) % len(alphabet)]
return mapping
Test (dictionaries don't preserve order, so it's pretty hard to read):
>>> caesar(12)
{'A': 'M', 'C': 'O', 'B': 'N', 'E': 'Q', 'D': 'P', 'G': 'S', 'F': 'R', 'I': 'U', 'H': 'T', 'K': 'W', 'J': 'V', 'M': 'Y', 'L': 'X', 'O': 'A', 'N': 'Z', 'Q': 'C', 'P': 'B', 'S': 'E', 'R': 'D', 'U': 'G', 'T': 'F', 'W': 'I', 'V': 'H', 'Y': 'K', 'X': 'J', 'Z': 'L'}
Let's look at one error in particular: o: '{'.
Notice that ord('o') is 111, so let's look at the chr of integers in the range(111,130):
Starting at o, shifting by 12, takes you to the { character:
In [75]: ' '.join([chr(x) for x in range(111,130)])
Out[75]: 'o p q r s t u v w x y z { | } ~ \x7f \x80 \x81'
^ 1 2 3 4 5 6 7 8 9 ...12
So the reason why you are getting incorrect output is because your formula
code = chr(ord(letters) + shift)
isn't taking into account what happens if the shift bounces you out of the ords associated with a-z or A-Z. (Note that the ord ranges for a-z and A-Z are not contiguous either!)
Here is a hint on how to fix:
In [82]: alphabet = string.ascii_lowercase + string.ascii_uppercase
In [83]: alphabet.index('o')
Out[83]: 14
In [84]: alphabet[alphabet.index('o')+12]
Out[84]: 'A'
but
In [85]: alphabet[alphabet.index('O')+12]
results in IndexError: string index out of range. That's because len(alphabet) is 52, and
In [91]: alphabet.index('O')+12
Out[91]: 52
Somehow we need 52 to wrap back around to 0. You can do that with the % modulo operator:
In [92]: 52 % 52
Out[92]: 0

Categories

Resources