artemis
artemis

Reputation: 7281

Pythonic way to parse tuples

I am working with a list of tuples, like below:

res = [('stori', 'JJ'), ('man', 'NN'), ('unnatur', 'JJ'), ('feel', 'NN'), ('pig', 'JJ'), ('start', 'NN'), ('open', 'JJ'), ('scene', 'NN'), ('terrif', 'NN'), ('exampl', 'NN'), ('absurd', 'JJ'), ('comedi', 'NN'), ('formal', 'JJ'), ('orchestra', 'NN'), ('audienc', 'NN'), ('turn', 'VBP'), ('insan', 'JJ'), ('violent', 'JJ'), ('mob', 'NN'), ('crazi', 'NN'), ('chant', 'JJ'), ('singer', 'NN'), ('unfortun', 'JJ'), ('stay', 'NN'), ('absurd', 'IN'), ('whole', 'JJ'), ('time', 'NN'), ('general', 'JJ'), ('narrat', 'NN'), ('eventu', 'VBP'), ('make', 'VBP'), ('put', 'VB'), ('even', 'RB'), ('era', 'NN'), ('turn', 'NN'), ('cryptic', 'JJ'), ('dialogu', 'NN'), ('would', 'MD'), ('make', 'VB'), ('shakespear', 'JJ'), ('seem', 'JJ'), ('easi', 'JJ'), ('third', 'JJ'), ('grader', 'NN'), ('technic', 'JJ'), ('level', 'NN'), ('better', 'RBR'), ('might', 'MD'), ('think', 'VB'), ('good', 'JJ'), ('cinematographi', 'NN'), ('futur', 'NN'), ('great', 'JJ'), ('vilmo', 'JJ'), ('zsigmond', 'NN'), ('futur', 'NN'), ('star', 'NN'), ('salli', 'NN'), ('kirkland', 'NN'), ('freder', 'NN'), ('forrest', 'JJS'), ('seen', 'VBN'), ('briefli', 'NN')]

I am looking for an output that will show the count of nouns, verbs, adjectives, and other words in this list of tuples, with the following criteria:

So far I have:

# Create a set of all values that appear
appears = set([x[1] for x in res])

cnt_noun = 0
cnt_adj = 0
cnt_vb = 0
cnt_other = 0

for tpl in res:
    if('NN' in tpl[1]):
        cnt_noun += 1
    elif('JJ' in tpl[1]):
        cnt_adj += 1
    elif('VB' in tpl[1] or 'VP' in tpl[1]):
        cnt_vb += 1
    else:
        cnt_other += 1

And that properly displays the counts:

cnts = [cnt_noun, cnt_vb, cnt_adj, cnt_other]
for x in cnts:
    print(x)

Yields

29
7
22
5

It is important to actually return the counts by each word tag in totality, as this will be used as part of a large data building sequence.

However, is there a more pythonic way to accomplish the same thing, with less lines and more efficiently?

Upvotes: 2

Views: 96

Answers (3)

Austin
Austin

Reputation: 26037

You can use collections.defaultdict and make it cleaner:

d = defaultdict(list)

for x, y in res:
    if 'VB' in y or 'VP' in y:
        y = 'Verb'
    elif 'JJ' in y:
        y = 'Adj'     
    elif 'NN' in y:
        y = 'Noun'
    else:
        y = 'Others'

    d[y].append(x)

        
for k, v in d.items():
    print(k, len(v))

Which outputs:

Adj 22
Noun 29
Verb 7
Others 5

Upvotes: 1

Ronie Martinez
Ronie Martinez

Reputation: 1275

I'd probably use collections.Counter:

res = [('stori', 'JJ'), ('man', 'NN'), ...]
counter = Counter([b for a, b in res])
# Counter acts like a dictionary containing e.g. {'JJ': 3, ...}

Here take a look at a complete solution:

from collections import Counter

res = [('stori', 'JJ'), ('man', 'NN'), ('unnatur', 'JJ'), ('feel', 'NN'), ('pig', 'JJ'), ('start', 'NN'), ('open', 'JJ'), ('scene', 'NN'), ('terrif', 'NN'), ('exampl', 'NN'), ('absurd', 'JJ'), ('comedi', 'NN'), ('formal', 'JJ'), ('orchestra', 'NN'), ('audienc', 'NN'), ('turn', 'VBP'), ('insan', 'JJ'), ('violent', 'JJ'), ('mob', 'NN'), ('crazi', 'NN'), ('chant', 'JJ'), ('singer', 'NN'), ('unfortun', 'JJ'), ('stay', 'NN'), ('absurd', 'IN'), ('whole', 'JJ'), ('time', 'NN'), ('general', 'JJ'), ('narrat', 'NN'), ('eventu', 'VBP'), ('make', 'VBP'), ('put', 'VB'), ('even', 'RB'), ('era', 'NN'), ('turn', 'NN'), ('cryptic', 'JJ'), ('dialogu', 'NN'), ('would', 'MD'), ('make', 'VB'), ('shakespear', 'JJ'), ('seem', 'JJ'), ('easi', 'JJ'), ('third', 'JJ'), ('grader', 'NN'), ('technic', 'JJ'), ('level', 'NN'), ('better', 'RBR'), ('might', 'MD'), ('think', 'VB'), ('good', 'JJ'), ('cinematographi', 'NN'), ('futur', 'NN'), ('great', 'JJ'), ('vilmo', 'JJ'), ('zsigmond', 'NN'), ('futur', 'NN'), ('star', 'NN'), ('salli', 'NN'), ('kirkland', 'NN'), ('freder', 'NN'), ('forrest', 'JJS'), ('seen', 'VBN'), ('briefli', 'NN')]
mapping = {'NN':'noun', 'JJ':'adjective','VB':'verb','VP':'verb'}
counter = Counter([mapping.get(b[:2], "other") for a, b in res])
print(counter)  # Counter({'noun': 29, 'adjective': 22, 'verb': 7, 'other': 5})

Upvotes: 5

wwii
wwii

Reputation: 23783

collections.Counter is the way to go.

  • Make a dictionary mapping the two-character strings to their classifications.
  • Use second item of each tuple to look-up values in that dictionary the get method will return 'other' for anything not defined.
  • Feed the values to collections.Counter.

import collections
stuff = {'NN':'noun', 'JJ':'adjective','VB':'verb','VP':'verb'}
c = collections.Counter(stuff.get(thing[:2],'other') for word,thing in res)

As a for loop:

c = collections.Counter()
for word,quality in res:
    c.update(stuff.get(quality[:2],'other'))

>>> c
Counter({'noun': 29, 'adjective': 22, 'verb': 7, 'other': 5})
>>>

Upvotes: 3

Related Questions