【问题标题】:Pythonic way to parse tuples解析元组的 Pythonic 方法
【发布时间】:2020-10-15 10:29:25
【问题描述】:

我正在处理一个元组列表,如下所示:

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

我正在寻找一个输出,该输出将显示此元组列表中的名词、动词、形容词和其他单词的计数,符合以下标准:

  • 名词以NN开头
  • 动词以 VB 或 VP 开头
  • 形容词以JJ开头
  • 其他不符合该标准

到目前为止我有:

# 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

这会正确显示计数:

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

产量

29
7
22
5

重要的是实际返回每个单词标签的总计数,因为这将用作大型数据构建序列的一部分。

但是,有没有更 Python 的方式来完成同样的事情,行数更少,效率更高?

【问题讨论】:

  • 你关心像print("Verb: {}".format(tpl))这样的中间输出吗?
  • @HansMusgrave 坦率地说没有那么多,为了确保代码正常工作,我应该删除它。
  • 使用elif('VB' in tpl[1] or 'VP' in tpl[1]):,您还可以计算'VBP''VBN'。这是预期的吗?
  • @Austin 是的,因为两者都以VB 和/或VP 开头。剩余的字符序列无关紧要;重要的是它的开始。

标签: python list performance tuples list-comprehension


【解决方案1】:

我可能会使用 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, ...}

这里看一个完整的解决方案:

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

【讨论】:

  • 不是downvoter,可能不是预期的输出。
  • 我也没有投反对票,但这与预期的输出不符。
  • 我明白它与输出不匹配,尽管我认为这是 OP 的第一步
  • 我发布的代码确实产生了预期的输出,坦率地说,它效率低下且非 Pythonic。但是,这肯定是要研究的,谢谢。
  • @wwii 如果你重读 OP 的代码,那并不是 OP 想要的。
【解决方案2】:

collections.Counter 是要走的路。

  • 制作一个字典,将两个字符串映射到它们的分类
  • 使用每个元组的第二项查找该字典中的值,get 方法将为未定义的任何内容返回 'other'
  • 将值提供给 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)

作为一个for循环:

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

【讨论】:

    【解决方案3】:

    您可以使用collections.defaultdict 并使其更清洁:

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

    哪些输出:

    Adj 22
    Noun 29
    Verb 7
    Others 5
    

    【讨论】:

      猜你喜欢
      • 2011-01-15
      • 1970-01-01
      • 2014-04-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-16
      相关资源
      最近更新 更多