Counting only the frequency of letters in a string - python

I am trying to make my program count everything but numbers in a string, and store it in a dictionary.
So far I have this:
string = str(input("Enter a string: "))
stringUpper = string.upper()
dict = {}
for n in stringUpper:
keys = dict.keys()
if n in keys:
dict[n] += 1
else:
dict[n] = 1
print(dict)
I just want the alphabetical numbers quantified, but I cannot figure out how to exclude the non-alphabetical characters.

Basically there are multiple steps involved:
Getting rid of the chars that you don't want to count
Count the remaining
You have several options available to do these. I'll just present one option, but keep in mind that there might be other (and better) alternatives.
from collections import Counter
the_input = input('Enter something')
Counter(char for char in the_input.upper() if char.isalpha())
For example:
Enter something: aashkfze3f8237rhbjasdkvjuhb
Counter({'A': 3,
'B': 2,
'D': 1,
'E': 1,
'F': 2,
'H': 3,
'J': 2,
'K': 2,
'R': 1,
'S': 2,
'U': 1,
'V': 1,
'Z': 1})
So it obviously worked. Here I used collections.Counter to count and a generator expression using str.isalpha as condition to get rid of the unwanted characters.
Note that there are several bad habits in your code that will make your life more complicated than it needs to be:
dict = {} will shadow the built-in dict. So it's better to choose a different name.
string is the name of a built-in module, so here a different name might be better (but not str which is a built-in name as well).
stringUpper = string.upper(). In Python you generally don't use camelCase but use _ to seperate word (i.e. string_upper) but since you only use it to loop over you might as well use for n in string.upper(): directly.
Variable names like n aren't very helpful. Usually you can name them char or character when iterating over a string or item when iterating over a "general" iterable.

You can use re to replace all non-alphabetical characters before doing any manipulation:
regex = re.compile('[^a-zA-Z]')
#First parameter is the replacement, second parameter is your input string
regex.sub('', stringUpper )

string = str(input("Enter a string: "))
stringUpper = string.upper()
dict = {}
for n in stringUpper:
if n not in '0123456789':
keys = dict.keys()
if n in keys:
dict[n] += 1
else:
dict[n] = 1
print(dict)

for n in stringUpper:
if n.isalpha()
dict[n] += 1
else:
dict[n] = 1
print(dict)

You can check string for alphanumeric
n.isalnum()
for aphabetic:
n.isalpha()
So your code will be like:
dict = {}
for n in stringUpper:
if n.isalpha():
keys = dict.keys()
if n in keys:
dict[n] += 1
else:
dict[n] = 1
print(dict)
else:
#do something....

While iterating, check if the lower() and upper() is the same for a character. If they are different from each other, then it is an alphabetical letter.
if n.upper() == n.lower():
continue
This should do it.

Related

how to find the most popular letter in a string that also has the lowest ascii value

Implement the function most_popular_character(my_string), which gets the string argument my_string and returns its most frequent letter. In case of a tie, break it by returning the letter of smaller ASCII value.
Note that lowercase and uppercase letters are considered different (e.g., ‘A’ < ‘a’). You may assume my_string consists of English letters only, and is not empty.
Example 1: >>> most_popular_character("HelloWorld") >>> 'l'
Example 2: >>> most_popular_character("gggcccbb") >>> 'c'
Explanation: cee and gee appear three times each (and bee twice), but cee precedes gee lexicographically.
Hints (you may ignore these):
Build a dictionary mapping letters to their frequency;
Find the largest frequency;
Find the smallest letter having that frequency.
def most_popular_character(my_string):
char_count = {} # define dictionary
for c in my_string:
if c in char_count: #if c is in the dictionary:
char_count[c] = 1
else: # if c isn't in the dictionary - create it and put 1
char_count[c] = 1
sorted_chars = sorted(char_count) # sort the dictionary
char_count = char_count.keys() # place the dictionary in a list
max_per = 0
for i in range(len(sorted_chars) - 1):
if sorted_chars[i] >= sorted_chars[i+1]:
max_per = sorted_chars[i]
break
return max_per
my function returns 0 right now, and I think the problem is in the last for loop and if statement - but I can't figure out what the problem is..
If you have any suggestions on how to adjust the code it would be very appreciated!
Your dictionary didn't get off to a good start by you forgetting to add 1 to the character count, instead you are resetting to 1 each time.
Have a look here to get the gist of getting the maximum value from a dict: https://datagy.io/python-get-dictionary-key-with-max-value/
def most_popular_character(my_string):
# NOTE: you might want to convert the entire sting to upper or lower case, first, depending on the use
# e.g. my_string = my_string.lower()
char_count = {} # define dictionary
for c in my_string:
if c in char_count: #if c is in the dictionary:
char_count[c] += 1 # add 1 to it
else: # if c isn't in the dictionary - create it and put 1
char_count[c] = 1
# Never under estimate the power of print in debugging
print(char_count)
# max(char_count.values()) will give the highest value
# But there may be more than 1 item with the highest count, so get them all
max_keys = [key for key, value in char_count.items() if value == max(char_count.values())]
# Choose the lowest by sorting them and pick the first item
low_item = sorted(max_keys)[0]
return low_item, max(char_count.values())
print(most_popular_character("HelloWorld"))
print(most_popular_character("gggcccbb"))
print(most_popular_character("gggHHHAAAAaaaccccbb 12 3"))
Result:
{'H': 1, 'e': 1, 'l': 3, 'o': 2, 'W': 1, 'r': 1, 'd': 1}
('l', 3)
{'g': 3, 'c': 3, 'b': 2}
('c', 3)
{'g': 3, 'H': 3, 'A': 4, 'a': 3, 'c': 4, 'b': 2, ' ': 2, '1': 1, '2': 1, '3': 1}
('A', 4)
So: l and 3, c and 3, A and 4
def most_popular_character(my_string):
history_l = [l for l in my_string] #each letter in string
char_dict = {} #creating dict
for item in history_l: #for each letter in string
char_dict[item] = history_l.count(item)
return [max(char_dict.values()),min(char_dict.values())]
I didn't understand the last part of minimum frequency, so I make this function return a maximum frequency and a minimum frequency as a list!
Use a Counter to count the characters, and use the max function to select the "biggest" character according to your two criteria.
>>> from collections import Counter
>>> def most_popular_character(my_string):
... chars = Counter(my_string)
... return max(chars, key=lambda c: (chars[c], -ord(c)))
...
>>> most_popular_character("HelloWorld")
'l'
>>> most_popular_character("gggcccbb")
'c'
Note that using max is more efficient than sorting the entire dictionary, because it only needs to iterate over the dictionary once and find the single largest item, as opposed to sorting every item relative to every other item.

How to create function that takes a text string and returns a dictionary containing how many times some defined characters occur even if not present?

Hello I asked this question previously and I wanted to adjust the code that I have now. I want to adjust this code so that if a letter is not present in a text string it still returns the value 0 to it assigned.
count = {}
for l in text.lower():
if l in let:
if l in count.keys():
count[l] += 1
else:
count[l] = 1
return count
It currently returns this:
example = "Sample String"
print(func(example, "sao")
{'s': 2, 'a' : 1}
This would be my desired output
example = "Sample String"
print(func(example, "sao"))
{'s': 2, 'a' : 1, 'o' :0}
If you don't mind using tools designed especially for your purpose, then the following will do:
from collections import Counter
def myfunc(inp, vals):
c = Counter(inp)
​return {e: c[e] for e in vals}
s = 'Sample String'
print(myfunc(s, 'sao')
Otherwise you can explicitly set all missing values in your functions.
def func(inp, vals):
count = {e:0 for e in vals}
for s in inp:
if s in count:
count[s] += 1
return count
# create a function
def stringFunc(string, letters):
# convert string of letters to a list of letters
letter_list = list(letters)
# dictionary comprehension to count the number of times a letter is in the string
d = {letter: string.lower().count(letter) for letter in letter_list}
return d
stringFunc('Hello World', 'lohdx')
# {'l': 3, 'o': 2, 'h': 1, 'd': 1, 'x': 0}
You can use a Dict Comprehensions and str.count:
def count_letters(text, letters):
lower_text = text.lower()
return {c: lower_text.count(c) for c in letters}
print(count_letters("Sample String", "sao"))
result: {'s': 2, 'a': 1, 'o': 0}
You can use collections.Counter and obtain character counts via the get method:
from collections import Counter
def func(string, chars):
counts = Counter(string.lower())
return {c: counts.get(c, 0) for c in chars}

Counting subsequent letters

So I am trying to implement code that will count the next letter in a sentence, using python.
so for instance,
"""So I am trying to implement code that will count the next letter in a sentence, using
python"""
most common letters one after the other
for 's'
'o' :1
'e' :1
for 'o'
' ' :1
'd' :1
'u' :1
'n' :1
I think you get the idea
I already have written code for counting letters prior
def count_letters(word, char):
count = 0
for c in word:
if char == c:
count += 1
return count
As you can see this just counts for letters, but not the next letter. can someone give me a hand on this one?
from collections import Counter, defaultdict
counts = defaultdict(Counter)
s = """So I am trying to implement code that will count the next letter in a sentence, using
python""".lower()
for c1, c2 in zip(s, s[1:]):
counts[c1][c2] += 1
(apart from being simpler, this should be significantly faster than pault's answer by not iterating over the string for every letter)
Concepts to google that aren't named in the code:
for c1, c2 in ... (namely the fact that there are two variables): tuple unpacking
s[1:]: slicing. Basically this is a copy of the string after the first character.
Here is a relatively terse way to do it:
from itertools import groupby
from collections import Counter
def countTransitionFrequencies(text):
prevNext = list(zip(text[:-1], text[1:]))
prevNext.sort(key = lambda pn: pn[0])
transitions = groupby(prevNext, lambda pn: pn[0])
freqs = map(
lambda kts: (kts[0], Counter(map(lambda kv: kv[1], kts[1]))),
transitions
)
return freqs
Explanation:
zip creates list of pairs with (previous, next) characters
The pairs are sorted and grouped by the previous character
The frequencies of the next characters (extracted from pairs by kv[1]) are then counted using Counter.
Sorting is not really necessary, but unfortunately, this is how the provided groupby works.
An example:
for k, v in countTransitionFrequencies("hello world"):
print("%r -> %r" % (k, v))
This prints:
' ' -> Counter({'w': 1})
'e' -> Counter({'l': 1})
'h' -> Counter({'e': 1})
'l' -> Counter({'l': 1, 'o': 1, 'd': 1})
'o' -> Counter({' ': 1, 'r': 1})
'r' -> Counter({'l': 1})
'w' -> Counter({'o': 1})
Here's a way using collections.Counter:
Suppose the string you provided was stored in a variable s.
First we iterate over the set of all lower case letters in s. We do this by making another string s_lower which will convert the string s to lowercase. We then wrap this with the set constructor to get unique values.
For each char, we iterate through the string and check to see if the previous letter is equal to char. If so, we store this in a list. Finally, we pass this list into the collections.Counter constructor which will count the occurrences.
Each counter is stored in a dictionary, counts, where the keys are the unique characters in the string.
from collections import Counter
counts = {}
s_lower = s.lower()
for char in set(s_lower):
counts[char] = Counter(
[c for i, c in enumerate(s_lower) if i > 0 and s_lower[i-1] == char]
)
For your string, this has the following outputs:
>>> print(counts['s'])
#Counter({'i': 1, 'e': 1, 'o': 1})
>>> print(counts['o'])
#Counter({' ': 2, 'd': 1, 'n': 1, 'u': 1})
One caveat is that this method will iterate through the whole string for each unique character, which could potentially make it slow for large lists.
Here is an alternative approach using collections.Counter and collections.defaultdict that only loops through the string once:
from collections import defaultdict, Counter
def count_letters(s):
s_lower = s.lower()
counts = defaultdict(Counter)
for i in range(len(s_lower) - 1):
curr_char = s_lower[i]
next_char = s_lower[i+1]
counts[curr_char].update(next_char)
return counts
counts = count_letters(s)
We loop over each character in the string (except the last) and on each iteration we update a counter using the next character.
This should work, the only thing is it doesn't sort the values, but that can be solved by creating a new dictionary with list of tuples (char, occurrences) and using sorted function on tuple[1].
def countNext(word):
d = {}
word = word.lower()
for i in range(len(word) - 1):
c = word[i]
cc = word[i+1]
if(not c.isalpha() or not cc.isalpha()):
continue
if c in d:
if cc in d[c]:
d[c][cc] += 1
else:
d[c][cc] = 1
else:
d[c] = {}
d[c][cc] = 1
return d

Program is repeating itself -can't figure out why

This is my program so far...it takes a message (input from user) and tells the user how many A's are in the program, how many B's, etc. Except when I input a message such as "Dad", it'll tell me how many D's there are twice instead of just saying everything once.
It says:
D ... 2
A ... 1
D ... 2
I want it to say:
A ... 1
D ... 2
How do I fix this without using zip, and without importing anything?
message=input("what is your message?").upper()
alphabet=["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"]
count=[0]*len(alphabet)
for i in message:
if i in alphabet:
count[alphabet.index(i)]+=1
for i in message:
print (i,"...",count[alphabet.index(i)])
(Thanks to Uriel Eli for helping me get the program this far btw).
I don't agree with your approach here. You have actually over complicated this. The proper way to solve this is to actually use a dictionary to keep track of all the letters in the string and keep a count every time the same character comes up. Note, this also sticks to the rule of not importing anything.
Furthermore, this removes the necessity to have a list of letters to check against as well.
Also, if you need to count upper and lower case characters separtely, do not call upper at the end of your input. Just remove it. If you have to count upper and lower case as the same character, then you can leave it.
message=input("what is your message?").upper()
d = {}
for c in message:
if c in d:
d[c] += 1
else:
d[c] = 1
Demo
what is your message?thisisastringofthings
{'H': 1, 'F': 0, 'O': 0, 'R': 0, 'G': 1, 'S': 3, 'T': 2, 'A': 0, 'I': 3, 'N': 1}
To provide an output similar to what you are expecting, you just need to iterate through your final result and print:
for character, count in d.items():
print("{} ... {}".format(character, count))
Finally, just for the sake of showing the best way to do this, is to actually use Counter from collections:
>>> from collections import Counter
>>> Counter("thisisastring")
Counter({'s': 3, 'i': 3, 't': 2, 'h': 1, 'n': 1, 'a': 1, 'r': 1, 'g': 1})
Just for future reference, and I know that you CANT import anything now. The best way probably would be:
from collections import Counter
message=input("what is your message?").upper()
print(Counter(message))
# Counter({'D': 2, 'A': 1})
Your second for loop is iterating through message, so if the user input DAD (well... after upper casing it), you're gonna get:
message == DAD
i = D --> Shows 2
i = A --> Shows 1
i = D --> Shows 2 (again)
Maybe you'd want to iterate through count, keeping the index that you are iterating (to use it latter to match it with the alphabet list). Something like that:
for index, num_occurences in enumerate(count):
if num_occurences > 0:
print("Match found at i=%s which corresponds with alphabet[%s]=%s" %
(index, index, alphabet[index]))
print(alphabet[index], "...", num_occurences)
You should check what enumerate does.
If you still want to iterate through message, you can do it, keeping track of what letter did you already display using an auxiliary set (so you don't show the same letter again)
already_shown_letters = set()
for i in message:
if i not in already_shown_letters:
print (i,"...",count[alphabet.index(i)])
already_shown_letters.add(i)

key error: 'x' --> adding key value pair in for loop, key being char

I am a beginner in python and I am trying to solve a coding problem, got this error. Don't understand why ? I went through a couple of Q/A's here but they don't seem to solve my problem. Essentially what I am trying to do is iterate over a string, through its characters and fill these characters in a dictionary. With characters being the keys and values being the number of times these characters appeared. So I'm trying the following:
def myfunc(mystring):
for i in mystring:
if charCounter[i]:
charCounter[i] += 1
charCounter[i] = 1
mystring = "hello! how are you ?"
myfunc(mystring)
and Im getting following error:
File "xyq.py", line 3, in myfunc
if CharCounter[i]:
KeyError: 'h'
Can someone please suggest, where am I going wrong ? And if possible how can I improve the code ?
Thanks
You need to check if i is in charCounter before you try to retrieve it:
if i in charCounter:
charCounter[i] += 1
else:
charCounter[i] = 1
Or alternatively:
if charCounter.get(i):
...
if charCounter[i]:
throws KeyError if the key does not exist. What you want to do isuse if i in charCounter: instead:
if i in char_counter:
char_counter[i] += 1
else:
char_counter[i] = 1
Alternatively you could use get which gets the value if it exists, or returns the second (optional) value if it didn't exist:
char_counter[i] = char_counter.get(i, 0) + 1
However this counting pattern is so popular that a whole class exists for it: collections.Counter:
from collections import Counter
def my_func(my_string):
return Counter(my_string)
Example:
>>> counts = my_func('hello! how are you ?')
>>> counts
Counter({' ': 4, 'o': 3, 'h': 2, 'l': 2, 'e': 2, '!': 1, 'r': 1, 'a': 1,
'?': 1, 'w': 1, 'u': 1, 'y': 1})
>>> counts[' ']
4
collections.Counter is a subclass of dictionary, so it would behave in the same way that an ordinary dictionary would do with item access and so forth.

Categories