如何使用循环访问句子中动词前面的单词,使用spaCy?蟒

时间:2018-06-05 15:03:04

标签: python-3.x spacy

我使用spaCy通过POS标签在句子中定位动词,然后尝试操纵动词。动词的操纵取决于条件 - 例如取决于动词之前的单词。例如,我可能想要转换这个句子 - 包含三个动词(确实,伤害,运行):

(1) "Why does it hurt to run very fast."

进入这句话:

(2) "It hurts to run very fast."

这对我来说很简单。但是,当我在同一个句子中遇到两次相同的POS标签时,我的功能有问题。看起来在这种情况下,其中一个IF子句(下面的第13行)没有更新,所以它的评估为False,而它应该是True。我无法弄清楚我在忽视什么以及如何解决它。这是我的代码:

import pandas as pd
import spacy
nlp = spacy.load('en')

s = "Why does it hurt to run very fast."
df = pd.DataFrame({'sentence':[s]})
k = df['sentence']

1 def marking(row):
2    L = row
3    verblst = [('VB'), ('VBZ'), ('VBP')] # list of verb POS tags to focus on
4    chunks = []
5    pos = []
6    for token in nlp(L):
7        pos.append(token.tag_) # Just to check if POS tags are handled well
8    print(pos)  
9    if "Why" in L:  
10        for token in nlp(L):
11            if token.tag_ in verblst: 
                 # This line checks the POS tag of the word preceding the verb:
12               print(pos[pos.index(token.tag_)-1]) 
13                if pos[pos.index(token.tag_)-1] == 'TO': # Here things go wrong
14                    chunks.append(token.text + token.whitespace_)
15                elif pos[pos.index(token.tag_)-1] == 'WRB':
16                    chunks.append(token.text + token.whitespace_)                                
17                else: 
18                    chunks.append(token.text + 's' + token.whitespace_)
19            else:
20                chunks.append(token.text_with_ws)                    
        L = chunks
        L.pop(0)
        L.pop(0)
        L = [L[0].capitalize()] + L[1:] 
    L = "".join(L)
    return L

x = k.apply(marking)
print(x)

这给出了以下结果:

"It hurts to runs very fast."  # The 's' after run should not be there

                  0      1      2      3     4     5    6     7     8
POS list of s: ['WRB', 'VBZ', 'PRP', 'VB', 'TO', 'VB', 'RB', 'RB', '.']
sentence s:     "Why   does     it   hurt   to   run   very  fast.  ."

问题是由于' VB'在索引3和5处都可以找到它。看起来第13行中的索引在第一个' VB'之后没有更新。 - 我预计会自动发生。结果,对于第二个' VB',第13行查看索引2而不是索引4.因此,不满足13中的条件,并且第18行在第18行中处理 - 导致错误。我很困惑为什么会发生这种情况。我没看到什么?这怎么能解决?

非常感谢您的帮助。

1 个答案:

答案 0 :(得分:1)

这里的问题似乎是您只是在您预先编译的词性标记字符串列表中查找token.tag_字符串值的索引。这总是返回第一个匹配 - 所以在“run”的情况下,你的脚本实际上不会在索引5之前检查POS(这将是TO),而是索引3之前的POS(PRP)。

考虑以下抽象示例:

test = ['a', 'b', 'c', 'a', 'd']
for value in test:
    print(test.index(value))  # this will print 0, 1, 2, 0, 4

更好(也可能更简单)的解决方案是迭代Token个对象并使用Token.i attribute,它返回父文档中的索引。理想情况下,您希望处理文本一次,存储doc,然后在需要时将其编入索引。例如:

chunks = []
doc = nlp("Why does it hurt to run very fast.")

if doc[0].text == 'Why':  # the first token's text is "Why"
    for token in doc:
        if token.tag_ in ['VB', 'VBZ', 'VBP']:
            token_index = token.i  # this is the token index in the document
            prev_token = doc[token_index - 1]  # the previous token in the document
            if prev_token.tag_ == 'TO':
                chunks.append(token.text_with_ws)  # token text + whitespace
            # and so on

理想情况下,您总是希望尽可能晚地将spaCy的输出转换为纯文本 。您在代码中尝试解决的大多数问题都是spaCy已经为您做的事情 - 例如,它为您提供了Doc对象及其视图SpanToken performant,让你索引它们,在任何地方迭代令牌,更重要的是,永远不会破坏原始文本中的任何可用信息。一旦您的输出是单个文本字符串加上空格加上您添加的其他字符,您将无法轻松恢复原始标记。您也不知道哪个令牌附有空格以及各个令牌是如何相互关联的。

有关DocTokenSpan个对象的详细信息,请参阅this section in the docsAPI reference,其中列出了每个对象的可用属性。