counting letters in a string python - python

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

Related

How to define and implement a function that behaves like the dict constructor/update

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

How do I change tuple values in a dictionary?

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)

Create function to count occurrence of characters in a string

I want get the occurrence of characters in a string, I got this code:
string = "Foo Fighters"
def conteo(string):
copia = ''
for i in string:
if i not in copia:
copia = copia + i
conteo = [0]*len(copia)
for i in string:
if i in copia:
conteo[copia.index(i)] = conteo[copia.index(i)] + 1
out = ['0']*2*len(copia)
for i in range(len(copia)):
out[2*i] = copia[i]
out[2*i + 1] = conteo[i]
return (out)
And I want return something like: ['f', 2, 'o', 2, '', 1, 'i', 1, 'g', 1, 'h', 1, 't', 1, 'e', 1, 'r', 1, 's', 1]
How can I do it? Without use a python library
Thank you
Use Python Counter (part of standard library):
>>> str = 'foo fighters'
>>> from collections import Counter
>>> counter = Counter(str)
Counter({'f': 2, 'o': 2, ' ': 1, 'e': 1, 'g': 1, 'i': 1, 'h': 1, 's': 1, 'r': 1, 't': 1})
>>> counter['f']
2
>>>
Depending on why you want this information, one method could be to use a Counter:
from collections import Counter
print(Counter("Foo Fighters"))
Of course, to create exactly the same output as requested, use itertools as well:
from collections import Counter
from itertools import chain
c = Counter("Foo Fighters")
output = list(chain.from_iterable(c.items()))
>> ['F', 2, 'o', 2, ' ', 1, 'i', 1, 'g', 1, 'h', 1, 't', 1, 'e', 1, 'r', 1, 's', 1]
It's not clear whether you want a critique of your current attempt or a pythonic solution. Below is one way where output is a dictionary.
from collections import Counter
mystr = "Foo Fighters"
c = Counter(mystr)
Result
Counter({' ': 1,
'F': 2,
'e': 1,
'g': 1,
'h': 1,
'i': 1,
'o': 2,
'r': 1,
's': 1,
't': 1})
Output as list
I purposely do not combine the tuples in this list, as it's a good idea to maintain structure until absolutely necessary. It's a trivial task to combine these into one list of strings.
list(c.items())
# [('F', 2),
# ('o', 2),
# (' ', 1),
# ('i', 1),
# ('g', 1),
# ('h', 1),
# ('t', 1),
# ('e', 1),
# ('r', 1),
# ('s', 1)]

How to sort counter by alphabetical order THEN by value order in Python

I have a counter that I would like to sort in the following way:
Counter({'M': 9, 'L': 5, 'P': 5, 'S': 2, 'd': 1, 'T': 1})
when I use my code, here is what it gives me:
Counter({'M': 9, 'P': 5, 'L': 5, 'S': 2, 'T': 1, 'd': 1})
I tried the function sorted(), but when I use it, its return value is not a counter anymore.
Here is my code, how would you do that?
def most_encountered_letters(dictionnary):
counter = collections.Counter()
for line in dictionnary:
words = line.split(',')[0].split(' ')
for word in words:
counter[word[0]] += 1
print(counter)
return counter.most_common(5)
Counters are unordered. They are a subclass of dict, and like dict, are not ordered. It doesn't make sense to say "a sorted Counter". You can get a list of the items in Counter sorted the way you want, e.g.:
>>> from collections import Counter
>>> c = Counter({'M': 9, 'L': 5, 'P': 5, 'S': 2, 'd': 1, 'T': 1})
>>> c
Counter({'M': 9, 'L': 5, 'P': 5, 'S': 2, 'T': 1, 'd': 1})
>>> sorted(c.items(), key= lambda t: (t[1], t[0]), reverse=True)
[('M', 9), ('P', 5), ('L', 5), ('S', 2), ('d', 1), ('T', 1)]
If you want an ordered Mapping type, you either have to use the built-in OrderedDict, or implement your own. We can use multiple inheritance to re-use built-in classes to get what we want. This is a recipe straight from the docs:
class OrderedCounter(Counter, OrderedDict):
'Counter that remembers the order elements are first encountered'
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__, OrderedDict(self))
def __reduce__(self):
return self.__class__, (OrderedDict(self),)
So, in action:
>>> oc = OrderedCounter()
>>> for k,v in sorted(c.items(), key= lambda t: (t[1], t[0]), reverse=True):
... oc[k] = v
...
>>> oc
OrderedCounter(OrderedDict([('M', 9), ('P', 5), ('L', 5), ('S', 2), ('d', 1), ('T', 1)]))
>>> for k,v in oc.items():
... print(k,v)
...
M 9
P 5
L 5
S 2
d 1
T 1
>>>
More importantly, you should consider why you need an ordered Counter... do you really need one?

Using python for frequency analysis

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

Categories

Resources