55
\$\begingroup\$

My son just started high school and in Math, he was asked to do some stats homework. The exercise was to count all the vowels in a passage of text, then answer some questions about frequency.

Out of curiosity, I wrote a quick Python script to count the vowels:

text = "Australian Rules Football is a ball game played by two teams of eighteen players with an ellipsoid ball on a large oval field with four upright posts at each end. Each team attempts to score points by kicking the ball through the appropriate posts (goals) and prevent their opponents from scoring. The team scoring the most points in a given time is the winner. Usually this period is divided into four quarters of play.Play begins at the beginning of a quarter or after a goal, with a tap contest between two opposing players (rucks) in the centre of the ground after the umpire either throws the ball up or bounces it down."

vowels = {'a':0, 'e':0, 'i':0, 'o':0, 'u':0}

for t in text:
    if t.lower() == 'a':
        vowels['a'] = 1 + vowels['a']
    elif t.lower() == 'e':
        vowels['e'] = 1 + vowels['e']
    elif t.lower() == 'i':
        vowels['i'] = 1 + vowels['i']
    elif t.lower() == 'o':
        vowels['o'] = 1 + vowels['o']
    elif t.lower() == 'u':
        vowels['u'] = 1 + vowels['u']

print vowels

I don't claim to be very good at Python. I enjoy mucking around with it, but I think I'm missing something here. The script works, but I'm clearly repeating myself in the for loop. What's the better "Pythonic" was for me to have done this?

\$\endgroup\$

migrated from stackoverflow.com Apr 2 '16 at 8:59

This question came from our site for professional and enthusiast programmers.

  • 2
    \$\begingroup\$ Look up collections.Counter in the documentation - this is like a dictionary but also provides operation like most_common(n) which returns an ordered list of the n most common (highest count) entries. \$\endgroup\$ – barny Mar 21 '16 at 8:23
  • \$\begingroup\$ Almost same as Count Vowels in String Python \$\endgroup\$ – Bhargav Rao Mar 22 '16 at 6:05
82
\$\begingroup\$
  1. Change to lower once: text.lower() instead of t.lower() inside loop.
  2. Use t in vowels to check if the character is vowels.
  3. vowels = {...} can be replaced with dict.fromkeys('aeiou', 0) (See dict.fromkeys)

    Caution: Use this only if the value is immutable.

    >>> dict.fromkeys('aeiou', 0)
    {'a': 0, 'i': 0, 'e': 0, 'u': 0, 'o': 0}
    

vowels = dict.fromkeys('aeiou', 0)

for t in text.lower():  # Change to lower once.
    if t in vowels:
        vowels[t] += 1

print vowels

Alternatively, you can use try ... except KeyError ...:

for t in text.lower():  # Change to lower once.
    try:
        vowels[t] += 1
    except KeyError:  # Ignore consonants.
        pass

Or using battery included, collections.Counter:

from collections import Counter
vowels = Counter(c for c in text.lower() if c in 'aeiou')
# => Counter({'e': 54, 'a': 41, 'o': 40, 'i': 37, 'u': 14})
\$\endgroup\$
  • 15
    \$\begingroup\$ OMG. This is why I need to keep learning Python. So simple. Thanks :) \$\endgroup\$ – nedlud Mar 21 '16 at 7:27
  • 1
    \$\begingroup\$ Worth noting that, at least in 2.x and probably some older 3.x versions, Counter is kind of inefficient. For performance critical code, you'd want the dict version (or maybe a defaultdict version in some cases). \$\endgroup\$ – jpmc26 Mar 21 '16 at 11:40
  • 1
    \$\begingroup\$ +1, though an EAFP version (try: vowels[t] += 1 except KeyError: pass) might be worth mentioning as well. Not ideal in this case I suspect, but just for the sake of completeness \$\endgroup\$ – Tobias Kienzler Mar 21 '16 at 13:53
  • 2
    \$\begingroup\$ From a maintainability point of view, wouldn't it be better to replace t with character to make it easier to quickly understand what is happening? \$\endgroup\$ – Cronax Mar 21 '16 at 14:22
  • 1
    \$\begingroup\$ You don't even need to import collections.Counter. Then it's a real one liner: vowels = {v: text.lower().count(v) for v in 'aeiou'} \$\endgroup\$ – Simon Fromme May 22 '16 at 21:41
3
\$\begingroup\$

The standard way outlined in another answer is:

vowels_2 = {'a':0, 'e':0, 'i':0, 'o':0, 'u':0}
for i in text.lower():
    if i in vowels_2:
        vowels_2[i] += 1

print vowels_2

which converts to lowercase and then increment elements which are in the dictionary.


Another approach which uses a Counter in a slightly more efficient way:

from collections import Counter
data = Counter(text.lower())
vowels_3 = {i: data[i] for i in 'aeiou'}
print vowels_3

I think that it might be more efficient because I am not not iterating over each character in a string, but rather outsource to calculate counters for all the characters to a performant library and then only select counts that I care about.


and another way which uses another very helpful string function: translate, which translates one set of characters to another one. Here we basically ignore all the characters (note that you have to add all characters you want to ignore and it might not be so nice if you have too many of them - like unicode string).

dict(Counter(text.lower().translate(None, 'bcdfghjklmnpqrstvxzwy 0123456789,.()')))
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.