Getting key values from a list of dictionaries - python

I have a list that contains dictionaries with Letters and Frequencies. Basically, I have 53 dictionaries each for every alphabet (lowercase and uppercase) and space.
adict = {'Letter':'a', 'Frequency':0}
bdict = {'Letter':'b', 'Frequency':0}
cdict = {'Letter':'c', 'Frequency':0}
If you input a word, it will scan the word and update the frequency for its corresponding letter.
for ex in range(0, len(temp)):
if temp[count] == 'a': adict['Frequency']+=1
elif temp[count] == 'b': bdict['Frequency']+=1
elif temp[count] == 'c': cdict['Frequency']+=1
For example, I enter the word "Hello", The letters H,e,l,l,o is detected and its frequencies updated. Non zero frequencies will be transferred to a new list.
if adict['Frequency'] != 0 : newArr.append(adict)
if bdict['Frequency'] != 0 : newArr.append(bdict)
if cdict['Frequency'] != 0 : newArr.append(cdict)
After this, I had the newArr sorted by Frequency and transferred to a new list called finalArr. Below is a sample list contents for the word "Hello"
{'Letter': 'H', 'Frequency': 1}
{'Letter': 'e', 'Frequency': 1}
{'Letter': 'o', 'Frequency': 1}
{'Letter': 'l', 'Frequency': 2}
Now what I want is to transfer only the key values to 2 seperate lists; letterArr and numArr. How do I do this? my desired output is:
letterArr = [H,e,o,l]
numArr = [1,1,1,2]

Why don't you just use a collections.Counter? For example:
from collections import Counter
from operator import itemgetter
word = input('Enter a word: ')
c = Counter(word)
letter_arr, num_arr = zip(*sorted(c.items(), key=itemgetter(1,0)))
print(letter_arr)
print(num_arr)
Note the use of sorted() to sort by increasing frequency. itemgetter() is used to reverse the sort order so that the sort is performed first on the frequency, and then on the letter. The sorted frequencies are then separated using zip() on the unpacked list.
Demo
Enter a word: Hello
('H', 'e', 'o', 'l')
(1, 1, 1, 2)
The results are tuples, but you can convert to lists if you want with list(letter_arr) and list(num_arr).

I have a hard time understanding your data structure choice for this problem.
Why don't you just go with a dictionary like this:
frequencies = { 'H': 1, 'e': 1, 'l': 2, 'o': 1 }
Which is even easier to implement with a Counter:
from collections import Counter
frequencies = Counter("Hello")
print(frequencies)
>>> Counter({ 'H': 1, 'e': 1, 'l': 2, 'o': 1 })
Then to add another word, you'd simply have to use the updatemethod:
frequencies.update("How")
print(frequencies)
>>> Counter({'l': 2, 'H': 2, 'o': 2, 'w': 1, 'e': 1})
Finally, to get your 2 arrays, you can do:
letterArr, numArr = zip(*frequencies.items())
This will give you tuples though, if you really need lists, just do: list(letterArr)

You wanted a simple answer without further todo like zip, collections, itemgetter etc. This does the minimum to get it done, 3 lines in a loop.
finalArr= [{'Letter': 'H', 'Frequency': 1},
{'Letter': 'e', 'Frequency': 1},
{'Letter': 'o', 'Frequency': 1},
{'Letter': 'l', 'Frequency': 2}]
letterArr = []
numArr = []
for i in range(len(finalArr)):
letterArr.append(finalArr[i]['Letter'])
numArr.append(finalArr[i]['Frequency'])
print letterArr
print numArr
Output is
['H', 'e', 'o', 'l']
[1, 1, 1, 2]

Related

In Python, trying to sort a poker hand by means of lambda and dictionary key

I have a hand contain certain cards, like this:
expectHandFormat = ['Ks','Qs','Ah','Ad','Ac','Jh','Qs','Ks','Ad']
I would like to sort them by their ranks then suits, as the suits in my game have different priority values.
However, sort() function seems to have only 1 key, while I need "priority" and "index" keys together.
I only managed to sort them with respect to Rank, like this:
hand = ['K','Q','A','A','A','J','Q','K','A']
rankPriority = {'A': 1, 'K': 2, 'Q': 3, 'J': 4}
suitPriority = {'s': 1, 'h': 2, 'c': 3, 'd': 4}
hand.sort(key=rankPriority.get)
# hand = ['A', 'A', 'A', 'A', 'K', 'K', 'Q', 'Q', 'J']
or sort them with expected format but no priority dictionary, like this:
hand = ['Ks','Qs','As','Ah','Ac','Jh','Qd','Kc','Ad']
hand.sort(key= lambda x: x[0])
# hand['As', 'Ah', 'Ac', 'Ad', 'Jh', 'Ks', 'Kc', 'Qs', 'Qd'], failed to follow A > K > Q...
Is there a way to sort the list at a certain index, at the same time sort with respect to user-defined priority?
Thanks!
I hope I'm understanding your problem correctly, it seems to me that the rank always has priority on the suit, therefore your problems boils down to ordering two digits numbers : see
hand.sort(key= lambda x: rankPriority[x[0]]*10 + suitPriority[x[1]])
hand
output
['As', 'Ah', 'Ac', 'Ad', 'Ks', 'Kc', 'Qs', 'Qd', 'Jh']
You could encode each rank differently and then use the sum of both as an encoding.
hand = ['Ks','Qs','As','Ah','Ac','Jh','Qd','Kc','Ad']
rankPriority = {'A': 10, 'K': 20, 'Q': 30, 'J': 40}
suitPriority = {'s': 1, 'h': 2, 'c': 3, 'd': 4}
hand.sort(key=lambda x: rankPriority.get(x[0]) + suitPriority.get(x[1]))
Or you could use the fact that tuples can be sorted in order of elements and use the solution proposed by Paul M. on the comment:
hand.sort(key=lambda card: (rankPriority[card[0]], suitPriority[card[1]]))
Here the sorting function will return a tuple, and python will sort on the first element then the second.

How to check whether the letter (value) is in the dictionary?

I hope you are doing well. I'm working on a dictionary program:
strEntry = str(input("Enter a string: ").upper())
strEntry = strEntry.replace(",", "")
strEntry = strEntry.replace(" ", "")
print('')
def freq_finder(strFinder):
dict = {}
for i in strFinder:
keys = dict.keys()
if i in keys:
dict[i] += 1
else:
dict[i] = 1
return dict
newDic = freq_finder(strEntry)
print(newDic)
newLetter = str(input("Choose a letter: ").upper())
if newLetter in newDic.values():
print("Number of occurrence of", message.count(newLetter))
newDic.pop(newLetter)
print("Dictinary after that letter removed:", newDic)
else:
print("Letter not in dictionary")
sortedDic = sorted(newDic)
print(sortedDic)
Everything works fine before this part:
newLetter = str(input("Choose a letter: ").upper())
if newLetter in newDic.values():
print("Number of occurrence of", message.count(newLetter))
newDic.pop(newLetter)
print("Dictinary after that letter removed:", newDic)
else:
print("Letter not in dictionary")
I'm trying to figure out how to check whether the letter is in the dictionary. If it is not, display the message “Letter not in dictionary”. Otherwise, display the frequency count of that letter, remove the letter from the dictionary and display the dictionary after that letter has been removed.
It should look something like this:
Enter a string: Magee, Mississippi
Dictionary: {'M': 2, 'A': 1, 'G': 1, 'E': 2, 'I': 4, 'S': 4, 'P': 2}
Choose a letter: s
Frequency count of that letter: 4
Dictionary after that letter removed: {'M': 2, 'A': 1, 'G': 1, 'E': 2, 'I': 4, 'P': 2}
Letters sorted: ['A', 'E', 'G', 'I', 'M', 'P']
I would highly appreciate it if you could tell me what's wrong and how to fix it.
Check for the keys, not the value (Because the Value is a number, not a letter) -
if newLetter in newDic: # Or if newLetter in list(newDic.keys())
print("Number of occurrence of", message.count(newLetter))
For this - Dictionary: {'M': 2, 'A': 1, 'G': 1, 'E': 2, 'I': 4, 'S': 4, 'P': 2}, you could use Collections.Counter instead

How to create a dictionary whose values are lists

I read a code in a book 'Think Python'. This code gets stuck at the inverse[val].[key] with an error:
'str' object has no attribute 'append''
Which makes sense as inverse[val] contains a string object.
Here d is the input dictionary.
def invert_dict(d):
inverse = dict()
for key in d:
val = d[key]
if val not in inverse:
inverse[val] = [key]
else:
inverse[val].append(key)
return inverse
The input dictionary is {'a': 1, 'p': 1, 'r': 2, 't': 1, 'o': 1}
The expected output is {1: ['a', 'p', 't', 'o'], 2: ['r']}
How do I implement this, by modifying the given block of code?
You can use collections.defaultdict to create a dictionary of lists. Then append to dictionary values while iterating your input dictionary.
from collections import defaultdict
d_in = {'a': 1, 'p': 1, 'r': 2, 't': 1, 'o': 1}
d_out = defaultdict(list)
for k, v in d_in.items():
d_out[v].append(k)
print(d_out)
defaultdict(<class 'list'>, {1: ['a', 'p', 't', 'o'], 2: ['r']})
Your code can be improved by iterating keys and values simultaneously via dict.items, instead of iterating keys and manually extracting the value. In addition, your indentation is incorrect. After resolving these issues:
def invert_dict(d):
inverse = dict()
for key, val in d.items():
if val not in inverse:
inverse[val] = [key]
else:
inverse[val].append(key)
return inverse
try this:
def invert_dict(data):
inverse = {}
for key, value in data.items():
if value not in inverse:
inverse[value] = [key]
else:
inverse[value].append(key)
return inverse
A one-liner using reduce:
inverted_dict = reduce((lambda inverted_dict, key: inverted_dict.setdefault(dd[key], []).append(key) or inverted_dict), d, {})
Output:
{1: ['t', 'o', 'p', 'a'], 2: ['r']}
You can also follow a different approach in which you take all values from your dictionary and match each value with the keys that have this value in the initial dictionary:
def invert_dict(d):
values = set(d.values())
inverse = dict((v,[k for k in d.keys() if d[k]==v]) for v in values)
return inverse
inv = invert_dict({'a': 1, 'p': 1, 'r': 2, 't': 1, 'o': 1})
print(inv)
Output:
{1: ['a', 'p', 't', 'o'], 2: ['r']}

"TypeError: unhashable type: 'list'" yet I'm trying to only slice the value of the list, not use the list itself

I've been having issues trying to create a dictionary by using the values from a list.
alphabetList = list(string.ascii_lowercase)
alphabetList.append(list(string.ascii_lowercase))
alphabetDict = {}
def makeAlphabetDict (Dict, x):
count = 0
while count <= len(alphabetList):
item1 = x[(count + (len(alphabetList) / 2))]
item2 = item1
Dict[item1] = item2
count += 1
makeAlphabetDict(alphabetDict , alphabetList)
Which returns:
TypeError: unhashable type: 'list'
I tried here and other similar questions yet I still can't see why Python thinks I'm trying to use the list, rather than just a slice from a list.
Your list contains a nested list:
alphabetList.append(list(string.ascii_lowercase))
You now have a list with ['a', 'b', ..., 'z', ['a', 'b', ..., 'z']]. It is that last element in the outer list that causes your problem.
You'd normally would use list.extend() to add additional elements:
alphabetList.extend(string.ascii_lowercase)
You are using string.ascii_lowercase twice there; perhaps you meant to use ascii_uppercase for one of those strings instead? Even so, your code always uses the same character for both key and value so it wouldn't really matter here.
If you are trying to map lowercase to uppercase or vice-versa, just use zip() and dict():
alphabetDict = dict(zip(string.ascii_lowercase, string.ascii_uppercase))
where zip() produces pairs of characters, and dict() takes those pairs as key-value pairs. The above produces a dictionary mapping lowercase ASCII characters to uppercase:
>>> import string
>>> dict(zip(string.ascii_lowercase, string.ascii_uppercase))
{'u': 'U', 'v': 'V', 'o': 'O', 'k': 'K', 'n': 'N', 'm': 'M', 't': 'T', 'l': 'L', 'h': 'H', 'e': 'E', 'p': 'P', 'i': 'I', 'b': 'B', 'x': 'X', 'q': 'Q', 'g': 'G', 'd': 'D', 'r': 'R', 'z': 'Z', 'c': 'C', 'w': 'W', 'a': 'A', 'y': 'Y', 'j': 'J', 'f': 'F', 's': 'S'}
As Martijn Pieters noted, you have problem with the list append that adds a list within your other list. You can add two list in any of the following ways for simplicity:
alphabetList = list(string.ascii_lowercase)
alphabetList += list(string.ascii_lowercase)
# Adds two lists; same as that of alphabetList.extend(alphabetList)
alphabetList = list(string.ascii_lowercase) * 2
# Just for your use case to iterate twice over the alphabets
In either case, your alphabetDict will have only 26 alphabets and not 52 as you cannot have repeated keys within the dict.

frequency of letters in column python

I want to calculate the frequency of occurrence of each letter in all columns:
for example I have this three sequences :
seq1=AATC
seq2=GCCT
seq3=ATCA
here, we have: in the first column frequency of 'A' is 2 , 'G' is 1 .
for the second column : the frequency of 'A' is 1, 'C' is 1 and 'T' is 1. (the same thing in the rest of column)
first, I try to do the code of calculating frequency I try this:
for example:
s='AATC'
dic={}
for x in s:
dic[x]=s.count(x)
this gives: {'A':2,'T':1,'C':1}
now, I want to apply this on columns.for that I use this instruction:
f=list(zip(seq1,seq2,seq3))
gives:
[('A', 'G', 'A'), ('A', 'C', 'T'), ('T', 'C', 'C'), ('C', 'T', 'A')]
So, here, I calculate the frequency of letters in ():
How can I do this ?
if I work on a file of sequences, how can I use this code to apply it on the sequences of file?
for example my file contains 100 sequences each time I take three sequences and apply this code.
Here:
sequences = ['AATC',
'GCCT',
'ATCA']
f = zip(*sequences)
counts = [{letter: column.count(letter) for letter in column} for column in f]
print(counts)
Output (reformatted):
[{'A': 2, 'G': 1},
{'A': 1, 'C': 1, 'T': 1},
{'C': 2, 'T': 1},
{'A': 1, 'C': 1, 'T': 1}]
Salient features:
Rather than explicitly naming seq1, seq2, etc., we put them into a list.
We unpack the list with the * operator.
We use a dict comprehension inside a list comprehension to generate the counts for each letter in each column. It's basically what you did for the one-sequence case, but more readable (IMO).
As with my answer to your last question, you should wrap your functionality in a function:
def lettercount(pos):
return {c: pos.count(c) for c in pos}
Then you can easily apply it to the tuples from zip:
counts = [lettercount(t) for t in zip(seq1, seq2, seq3)]
Or combine it into the existing loop:
...
counts = []
for position in zip(seq1, seq2, seq3): # sets at same position
counts.append(lettercount(position))
for pair in combinations(position, 2): # pairs within set
...

Categories

Resources