I have this dictionary:
d = {'a': (1, 2, 'a'), 'b': (1, 2, 'b'), 'c': (2, 4, 'c'), 'd': (1, 3, 'd'), 'e': (0, 1, 'e'), 'f': (0, 1, 'f'), 'g': (1, 3, 'g'), 'h': (0, 1, 'h'), 'j': (1, 2, 'j'), 'i': (0, 1, 'i'), 'k': (-1, 0, 'k')}
How can I subtract the value by 1 for a specific key/value pair in this dictionary if the key matches the parameter?
For example, I want to subtract the values of key a by 1, so that it now displays:
{'a': (0, 1, 'a')
How can I edit the values of that specific key and decrease only the integers by 1 while creating the same dictionary again?
Code so far:
def matching(key_to_subtract):
for key, value in d.items():
if key_to_subtract == key:
matching("a")
Desired Output:
{'a': (0, 1, 'a'), 'b': (1, 2, 'b'), 'c': (2, 4, 'c'), 'd': (1, 3, 'd'), 'e': (0, 1, 'e'), 'f': (0, 1, 'f'), 'g': (1, 3, 'g'), 'h': (0, 1, 'h'), 'j': (1, 2, 'j'), 'i': (0, 1, 'i'), 'k': (-1, 0, 'k')}
Since a tuple is immutable you have to build a new one and bind it to the key. You do not, however, have to iterate a dict to search for a given key. That is the whole point of a dict:
def decrement(d, k):
# if k in d: # if you are not certain the key exists
d[k] = tuple(v-1 if isinstance(v, int) else v for v in d[k])
You cannot change the content of a tuple. So you need to construct a new tuple and assign it to the dict:
d = {'a': (1, 2, 'a'), 'b': (1, 2, 'b'), 'c': (2, 4, 'c')}
d['a'] = tuple( i - 1 if isinstance(i, int) else i for i in d['a'] )
# d become {'a': (0, 1, 'a'), 'b': (1, 2, 'b'), 'c': (2, 4, 'c')}
As tuples are immutable you have to rebuild the tuple again and reassign the new tuple to the same key you got it from. This can be written in 1-2 lines using comprehensions but would make it difficult to understand, hope it helps!
Using generator expressions
def matching(key_to_subtract):
# generator expression force evaluated as tuple
d[key_to_subtract] = tuple(item-1 if isinstance(item, int) else item for item in d.get(key_to_subtract))
Expanded form
d = {'a': (1, 2, 'a'), 'b': (1, 2, 'b'), 'c': (2, 4, 'c'), 'd': (1, 3, 'd'), 'e': (0, 1, 'e'), 'f': (0, 1, 'f'), 'g': (1, 3, 'g'), 'h': (0, 1, 'h'), 'j': (1, 2, 'j'), 'i': (0, 1, 'i'), 'k': (-1, 0, 'k')}
def matching(key_to_subtract):
buffer = [] # to build new tuple later from this list
for item in d.get(key_to_subtract): # get the tuple and iterate over
try:
buffer.append(item - 1) # subtract 1
except TypeError:
buffer.append(item) # append directly if its not integer
d[key_to_subtract] = tuple(buffer) # reassign the list as tuple
matching("a")
From your question, I propose another different approach from your code. The reason is because d.items() create a view which is good for checking but not for updating the code.
Therefore, I suggest using d.keys() to search for instance of key_to_subtract. Then change the tuple to list (or you can you another method of slicing the tuple at the position you want then add the new value in which can be faster in the case of a large tuple) then replacing that key with the new value (after changing it back from list to tuple):
def matching(key_to_subtract):
#check for key_to_subtract
#if found
if key_to_subtract in d.keys():
#change to list
lst_val = list(d[key_to_subtract])
#subtract the two values
lst_val[0]-=1
lst_val[1]-=1
#change back to tuple
tuple_val = tuple(lst_val)
#replace the old value of the same key with the new value
d[key_to_subtract] = tuple_val
based on the functions in: https://docs.python.org/3.7/library/stdtypes.html#dict-views
you can do in this way as tuple are immutable objects.
d = {'a': (1, 2, 'a'), 'b': (1, 2, 'b'), 'c': (2, 4, 'c'), 'd': (1, 3, 'd'), 'e': (0, 1, 'e'), 'f': (0, 1, 'f'), 'g': (1, 3, 'g'), 'h': (0, 1, 'h'), 'j': (1, 2, 'j'), 'i': (0, 1, 'i'), 'k': (-1, 0, 'k')}
def change_value_by_key_pos(data, key, pos, step):
for k,v in data.items():
if k == key:
list_items = list(data[key])
list_items[pos] = list_items[pos]+step
data[key] = tuple(list_items)
return data
data = change_value_by_key_pos(d,'a',1,1)
print(data)
Related
I want to change a dictionary below ...
dict = {
'A': [('B', 1), ('C', 3), ('D', 7)],
'B': [('D', 5)],
'C': [('D', 12)] }
into other form like this:
dict = [
('A', 'B', 1), ('A', 'C', 3), ('A', 'D', 7),
('B', 'D', 5), ('C', 'D', 12)]
This is what I done.
dict = {
'A': [('B', 1), ('C', 3), ('D', 7)],
'B': [('D', 5)],
'C': [('D', 12)] }
if(i[0] in dict):
value = dict[i[0]]
newvalue = i[1],i[2]
value.append(newvalue)
dict1[i[0]]=value
else:
newvalue = i[1],i[2]
l=[]
l.append(newvalue)
dict[i[0]]=l
print(dict)
Thanks
Python tuple is an immutable object. Hence any operation that tries to modify it (like append) is not allowed. However, following workaround can be used.
dict = {
'A': [('B', 1), ('C', 3), ('D', 7)],
'B': [('D', 5)],
'C': [('D', 12)] }
new_dict = []
for key, tuple_list in dict.items():
for tuple_item in tuple_list:
entry = list(tuple_item)
entry.append(key)
new_dict.append(tuple(entry))
print(new_dict)
Output:
[('B', 1, 'A'), ('C', 3, 'A'), ('D', 7, 'A'), ('D', 5, 'B'), ('D', 12, 'C')]
A simple aproach could be
new_dict = []
for letter1, list in dict.items():
for letter2, value in list:
new_dict.append([letter1, letter2, value])
With list comprehension;
dict_ = {
'A': [('B', 1), ('C', 3), ('D', 7)],
'B': [('D', 5)],
'C': [('D', 12)] }
result = [(key, value[0], value[1]) for key, list_ in dict_.items() for value in list_]
Output;
[('A', 'B', 1), ('A', 'C', 3), ('A', 'D', 7), ('B', 'D', 5), ('C', 'D', 12)]
You can iterate through the dictionary using .items(). Notice that each value is by itself a list of tuples. We want to unpack each tuple, so we need a nested for-loop as shown below. res is the output list that we will populate within the loop.
res = []
for key, values in dict.items():
for value in values:
res.append((key, value[0], value[1]))
Sample output:
>>> res
[('A', 'B', 1), ('A', 'C', 3), ('A', 'D', 7), ('B', 'D', 5), ('C', 'D', 12)]
EDIT: If value is a tuple of more than two elements, we would modify the last line as follows, using tuple unpacking:
res.append((key, *value))
This effectively unpacks all the elements of value. For example,
>>> test = (1, 2, 3)
>>> (0, *test)
(0, 1, 2, 3)
dicts support several initialization/update arguments:
d = dict([('a',1), ('b',2)]) # list of (key,value)
d = dict({'a':1, 'b':2}) # copy of dict
d = dict(a=1, b=2) #keywords
How do I write the signature and handle the arguments of a function that handles the same set of cases?
I'm writing a class that I want to support the same set of arguments, but just store the list of (key,value) pairs in order to allow for multiple items with the same key. I'd like to be able to add to the list of pairs from dicts, or lists of pairs or keywords in a way that is the same as for dict.
The C implementation of update isn't very helpful to me as I'm implementing this in python.
Here is a function that should behave the same as dict (except for the requirement to support multiple keys):
def func(*args, **kwargs):
result = []
if len(args) > 1:
raise TypeError('expected at most 1 argument, got 2')
elif args:
if all(hasattr(args[0], a) for a in ('keys', '__getitem__')):
result.extend(dict(args[0]).items())
else:
for k, v in args[0]:
hash(k)
result.append((k, v))
result.extend(kwargs.items())
return result
A few notes:
The collections.abc.Mapping class isn't used to test for mapping objects, because dict has lower requirements than that.
The signature of the function could be defined more precisely using positional-only syntax, like this: def func(x=None, /, **kwargs). However, that requires Python >= 3.8, so a more backwards-compatible solution has been preferred.
The function should raise the same exceptions as dict, but the messages won't always be exactly the same.
Below are some simple tests that compare the behaviour of the function with the dict constructor (although it does not attempt to cover every possibility):
def test(fn):
d1 = {'a': 1, 'b': 2, 'c': 3}
d2 = {'d': 4, 'e': 5, 'f': 6}
class Maplike():
def __getitem__(self, k):
return d1[k]
def keys(self):
return d1.keys()
print('test %s\n=========\n' % fn.__name__)
print('dict:', fn(d1))
print('maplike:', fn(Maplike()))
print('seq:', fn(tuple(d1.items())))
print('it:', fn(iter(d1.items())))
print('gen:', fn(i for i in d1.items()))
print('set:', fn(set(d2.items())))
print('str:', fn(['fu', 'ba', 'r!']))
print('kwargs:', fn(**d1))
print('arg+kwargs:', fn(d1, **d2))
print('dup-keys:', fn(d1, **d1))
print('empty:', fn())
print()
try:
fn(d1, d2)
print('ERROR')
except Exception as e:
print('len-args: %s' % e)
try:
fn([(1, 2, 3)])
print('ERROR')
except Exception as e:
print('pairs: %s' % e)
try:
fn([([], 3)])
print('ERROR')
except Exception as e:
print('hashable: %s' % e)
print()
test(func)
test(dict)
Ouput:
test func
=========
dict: [('a', 1), ('b', 2), ('c', 3)]
maplike: [('a', 1), ('b', 2), ('c', 3)]
seq: [('a', 1), ('b', 2), ('c', 3)]
it: [('a', 1), ('b', 2), ('c', 3)]
gen: [('a', 1), ('b', 2), ('c', 3)]
set: [('d', 4), ('e', 5), ('f', 6)]
str: [('f', 'u'), ('b', 'a'), ('r', '!')]
kwargs: [('a', 1), ('b', 2), ('c', 3)]
arg+kwargs: [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('e', 5), ('f', 6)]
dup-keys: [('a', 1), ('b', 2), ('c', 3), ('a', 1), ('b', 2), ('c', 3)]
empty: []
len-args: expected at most 1 argument, got 2
pairs: too many values to unpack (expected 2)
hashable: unhashable type: 'list'
test dict
=========
dict: {'a': 1, 'b': 2, 'c': 3}
maplike: {'a': 1, 'b': 2, 'c': 3}
seq: {'a': 1, 'b': 2, 'c': 3}
it: {'a': 1, 'b': 2, 'c': 3}
gen: {'a': 1, 'b': 2, 'c': 3}
set: {'d': 4, 'e': 5, 'f': 6}
str: {'f': 'u', 'b': 'a', 'r': '!'}
kwargs: {'a': 1, 'b': 2, 'c': 3}
arg+kwargs: {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6}
dup-keys: {'a': 1, 'b': 2, 'c': 3}
empty: {}
len-args: dict expected at most 1 argument, got 2
pairs: dictionary update sequence element #0 has length 3; 2 is required
hashable: unhashable type: 'list'
This should do the trick:
class NewDict:
def __init__(self, *args, **kwargs):
self.items = []
if kwargs:
self.items = list(kwargs.items())
elif args:
if type(args[0]) == list:
self.items = list(args[0])
elif type(args[0]) == dict:
self.items = list(args[0].items())
def __repr__(self):
s = "NewDict({"
for i, (k,v) in enumerate(self.items):
s += repr(k) + ": " + repr(v)
if i < len(self.items) - 1:
s += ", "
s += "})"
return s
d1 = NewDict()
d2 = NewDict([('a',1), ('b',2), ('a',2)])
d3 = NewDict({'a':1, 'b':2})
d4 = NewDict(a=1, b=2)
print(d1, d2, d3, d4, sep="\n")
print(d2.items)
Output:
NewDict({})
NewDict({'a': 1, 'b': 2, 'a': 2})
NewDict({'a': 1, 'b': 2})
NewDict({'a': 1, 'b': 2})
[('a', 1), ('b', 2), ('a', 2)]
I'm not saying this is a safe or great way to do this, but how about ...
def getPairs(*args, **kwargs):
pairs = []
for a in args:
if type(a) == dict:
pairs.extend([[key, val] for key, val in a.items()])
elif type(a) == list:
for a2 in a:
if len(a2) > 1:
pairs.append([a2[0], a2[1:]])
pairs.extend([[key, val] for key, val in kwargs.items()])
return pairs
I was trying to find the best variable to split a decision tree on and it required grouping and counting the occurrence of some values.
A dummy data set is
zipped=[(‘a’, ‘None’), (‘b’, ‘Premium’), (‘c’, ‘Basic’), (‘d’, ‘Basic’), (‘b’, ‘Premium’), (‘e’, ‘None’), (‘e’, ‘Basic’), (‘b’, ‘Premium’), (‘a’, ‘None’), (‘c’, ‘None’), (‘b’, ‘None’), (‘d’, ‘None’), (‘c’, ‘Basic’), (‘a’, ‘None’), (‘b’, ‘Basic’), (‘e’, ‘Basic’)]
So, I would like to find how many None, Basic and Premium are there in each of the a,b,c,d,e
I need it to look like
{‘a’:[‘None’:3,‘Basic’:0,‘Premium’:0], ‘b’:[‘None’:1,‘Basic’:1,‘Premium’:3],…} .
I am also open to a better way of aggregation or data structure.
Here is what I tried to do
temp=Counter( x[1] for x in zipped if x[0]=='b')
print(temp)
and I got
Counter({'Premium': 3, 'None': 1, 'Basic': 1})
Assuming your a,b etc are your slashdot, google:
zipped=[('a', 'None'), ('b', 'Premium'), ('c', 'Basic'), ('d', 'Basic'), ('b', 'Premium'),
('e', 'None'), ('e', 'Basic'), ('b', 'Premium'), ('a', 'None'), ('c', 'None'),
('b', 'None'), ('d', 'None'), ('c', 'Basic'), ('a', 'None'), ('b', 'Basic'),
('e', 'Basic')]
from collections import Counter
d = {}
for key,val in zipped:
d.setdefault(key,[]).append(val) # create key with empty list (if needed) + append val.
# now they are ordered lists, overwrite with Counter of it:
for key in d:
d[key] = Counter(d[key])
print(d)
Output:
{'a': Counter({'None': 3}),
'b': Counter({'Premium': 3, 'None': 1, 'Basic': 1}),
'c': Counter({'Basic': 2, 'None': 1}),
'd': Counter({'Basic': 1, 'None': 1}),
'e': Counter({'Basic': 2, 'None': 1})}
Counter gives you .most_common() to get the lists you want:
for k in d:
print(k,d[k].most_common())
Output:
a [('None', 3)]
b [('Premium', 3), ('None', 1), ('Basic', 1)]
c [('Basic', 2), ('None', 1)]
d [('Basic', 1), ('None', 1)]
e [('Basic', 2), ('None', 1)]
If you really need 0-counts, you can add them after the fact:
allVals = {v for _,v in zipped} # get distinct values of zipped
for key in d:
for v in allVals:
d[key].update([v]) # add value once
d[key].subtract([v]) # subtract value once
Bit cumbersome, but that way anyting will be present for all of them, with a 0 value if not present in zipped
for k in d:
print(k,d[k].most_common())
Output:
a [('None', 3), ('Premium', 0), ('Basic', 0)]
b [('Premium', 3), ('None', 1), ('Basic', 1)]
c [('Basic', 2), ('None', 1), ('Premium', 0)]
d [('Basic', 1), ('None', 1), ('Premium', 0)]
e [('Basic', 2), ('None', 1), ('Premium', 0)]
You can try something like this :
data=[('a', 'None'), ('b', 'Premium'), ('c', 'Basic'), ('d', 'Basic'), ('b', 'Premium'),
('e', 'None'), ('e', 'Basic'), ('b', 'Premium'), ('a', 'None'), ('c', 'None'),
('b', 'None'), ('d', 'None'), ('c', 'Basic'), ('a', 'None'), ('b', 'Basic'),
('e', 'Basic')]
manual_dict={}
for i,j in enumerate(data):
if j[0] not in manual_dict:
manual_dict[j[0]]=[j[1]]
else:
manual_dict[j[0]].append(j[1])
final_dict={}
for ia,aj in manual_dict.items():
final_dict[ia]={'None':aj.count('None'),'Basic':aj.count('Basic'),'Premium':aj.count('Premium')}
print(final_dict)
output:
{'c': {'Premium': 0, 'None': 1, 'Basic': 2}, 'a': {'Premium': 0, 'None': 3, 'Basic': 0}, 'd': {'Premium': 0, 'None': 1, 'Basic': 1}, 'b': {'Premium': 3, 'None': 1, 'Basic': 1}, 'e': {'Premium': 0, 'None': 1, 'Basic': 2}}
I am trying to use python to help me crack Vigenère ciphers. I am fairly new to programming but I've managed to make an algorithm to analyse single letter frequencies. This is what I have so far:
Ciphertext = str(input("What is the cipher text?"))
Letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
def LetterFrequency():
LetterFrequency = {'A': 0, 'B': 0, 'C': 0, 'D': 0, 'E': 0, 'F': 0, 'G': 0, 'H': 0, 'I': 0, 'J': 0, 'K': 0, 'L': 0, 'M': 0, 'N': 0, 'O': 0, 'P': 0, 'Q': 0, 'R': 0, 'S': 0, 'T': 0, 'U': 0, 'V': 0, 'W': 0, 'X': 0, 'Y': 0, 'Z': 0}
for letter in Ciphertext.upper():
if letter in Letters:
LetterFrequency[letter]+=1
return LetterFrequency
print (LetterFrequency())
But is there a way for me to print the answers in descending order starting from the most frequent letter? The answers are shown in random order right now no matter what I do.
Also does anyone know how to extract specific letters form a large block of text to perform frequency analysis? So for instance if I wanted to put every third letter from the text “THISISARATHERBORINGEXAMPLE” together to analyse, I would need to get:
T H I
S I S
A R A
T H E
R B O
R I N
G E X
A M P
L E
Normally I would have to do this by hand in either notepad or excel which takes ages. Is there a way to get around this in python?
Thanks in advance,
Tony
For the descending order you could use Counter:
>>> x = "this is a rather boring example"
>>> from collections import Counter
>>> Counter(x)
Counter({' ': 5, 'a': 3, 'e': 3, 'i': 3, 'r': 3, 'h': 2, 's': 2, 't': 2, 'b': 1, 'g': 1, 'm': 1, 'l': 1, 'o': 1, 'n': 1, 'p': 1, 'x': 1})
As for the second question you could iterate per 3.
To exclude spaces you can try what #not_a_robot suggests in the comment or
delete it manually like:
>>> y = Counter(x)
>>> del y[' ']
>>> y
Counter({'a': 3, 'e': 3, 'i': 3, 'r': 3, 'h': 2, 's': 2, 't': 2, 'b': 1, 'g': 1, 'm': 1, 'l': 1, 'o': 1, 'n': 1, 'p': 1, 'x': 1})
Another approach, although the collections.Counter example from #coder is your best bet.
from collections import defaultdict
from operator import itemgetter
Letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Ciphertext = "this is a rather boring example"
def LetterFrequency():
LetterFrequency = {letter: 0 for letter in Letters}
for letter in Ciphertext.upper():
if letter in Letters:
LetterFrequency[letter]+=1
return LetterFrequency
def sort_dict(dct):
return sorted(dct.items(), key = itemgetter(1), reverse = True)
print(sort_dict(LetterFrequency()))
Which prints this, a list of tuples sorted descendingly by frequency:
[('A', 3), ('I', 3), ('E', 3), ('R', 3), ('T', 2), ('S', 2), ('H', 2), ('L', 1), ('G', 1), ('M', 1), ('P', 1), ('B', 1), ('N', 1), ('O', 1), ('X', 1), ('Y', 0), ('J', 0), ('D', 0), ('U', 0), ('F', 0), ('C', 0), ('Q', 0), ('W', 0), ('Z', 0), ('K', 0), ('V', 0)]
I have to write a function, countLetters(word), that takes in a word as argument and returns a list that counts the number of times each letter appears. The letters must be sorted in alphabetical order.
This is my attempt:
def countLetters(word):
x = 0
y = []
for i in word:
for j in range(len(y)):
if i not in y[j]:
x = (i, word.count(i))
y.append(x)
return y
I first tried it without the if i not in y[j]
countLetters("google")
result was
[('g', 2), ('o', 2), ('o', 2), ('g', 2), ('l', 1), ('e', 1)]
when I wanted
[('e', 1), ('g', 2), ('l', 1), ('o', 2)]
When I added the if i not in y[j] filter, it just returns an empty list [].
Could someone please point out my error here?
I recommend the collections module's Counter if you're in Python 2.7+
>>> import collections
>>> s = 'a word and another word'
>>> c = collections.Counter(s)
>>> c
Counter({' ': 4, 'a': 3, 'd': 3, 'o': 3, 'r': 3, 'n': 2, 'w': 2, 'e': 1, 'h': 1, 't': 1})
You can do the same in any version Python with an extra line or two:
>>> c = {}
>>> for i in s:
... c[i] = c.get(i, 0) + 1
This would also be useful to check your work.
To sort in alphabetical order (the above is sorted by frequency)
>>> for letter, count in sorted(c.items()):
... print '{letter}: {count}'.format(letter=letter, count=count)
...
: 4
a: 3
d: 3
e: 1
h: 1
n: 2
o: 3
r: 3
t: 1
w: 2
or to keep in a format that you can reuse as a dict:
>>> import pprint
>>> pprint.pprint(dict(c))
{' ': 4,
'a': 3,
'd': 3,
'e': 1,
'h': 1,
'n': 2,
'o': 3,
'r': 3,
't': 1,
'w': 2}
Finally, to get that as a list:
>>> pprint.pprint(sorted(c.items()))
[(' ', 4),
('a', 3),
('d', 3),
('e', 1),
('h', 1),
('n', 2),
('o', 3),
('r', 3),
('t', 1),
('w', 2)]
I think the problem lies in your outer for loop, as you are iterating over each letter in the word.
If the word contains more than one of a certain letter, for example "bees", when it iterates over this, it will now count the number of 'e's twice as the for loop does not discriminate against unique values. Look at string iterators, this might clarify this more. I'm not sure this will solve your problem, but this is the first thing that I noticed.
You could maybe try something like this:
tally= {}
for s in check_string:
if tally.has_key(s):
tally[s] += 1
else:
tally[s] = 1
and then you can just retrieve the tally for each letter from that dictionary.
Your list y is always empty. You are never getting inside a loop for j in range(len(y))
P.S. your code is not very pythonic
Works fine with latest Py3 and Py2
def countItems(iter):
from collections import Counter
return sorted(Counter(iter).items())
Using a dictionary and pprint from answer of #Aaron Hall
import pprint
def countLetters(word):
y = {}
for i in word:
if i in y:
y[i] += 1
else:
y[i] = 1
return y
res1 = countLetters("google")
pprint.pprint(res1)
res2 = countLetters("Google")
pprint.pprint(res2)
Output:
{'e': 1, 'g': 2, 'l': 1, 'o': 2}
{'G': 1, 'e': 1, 'g': 1, 'l': 1, 'o': 2}
I am not sure what is your expected output, according to the problem statement, it seems you should sort the word first to get the count of letters in a sorted order. code below may be helpful:
def countLetters(word):
letter = []
cnt = []
for c in sorted(word):
if c not in letter:
letter.append(c)
cnt.append(1)
else:
cnt[-1] += 1
return zip(letter, cnt)
print countLetters('hello')
this will give you [('e', 1), ('h', 1), ('l', 2), ('o', 1)]
You can create dict of characters first, and than list of tulips
text = 'hello'
my_dict = {x : text.count(x) for x in text}
my_list = [(key, my_dict[key]) for key in my_dict]
print(my_dict)
print(my_list)
{'h': 1, 'e': 1, 'l': 2, 'o': 1}
[('h', 1), ('e', 1), ('l', 2), ('o', 1)]