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?

  • 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
    Commented Mar 21, 2016 at 8:23
  • \$\begingroup\$ Almost same as Count Vowels in String Python \$\endgroup\$
    – Bhargav Rao
    Commented Mar 22, 2016 at 6:05

2 Answers 2

  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.
        vowels[t] += 1
    except KeyError:  # Ignore consonants.

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})
  • 16
    \$\begingroup\$ OMG. This is why I need to keep learning Python. So simple. Thanks :) \$\endgroup\$
    – nedlud
    Commented Mar 21, 2016 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
    Commented Mar 21, 2016 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\$ Commented Mar 21, 2016 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
    Commented Mar 21, 2016 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\$ Commented May 22, 2016 at 21:41

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,.()')))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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