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

How to use a loop to access the word preceding a verb in a sentence, using spaCy? Python

我使用 spaCy 通过 POS 标签定位句子中的动词,然后尝试操纵动词。动词的操作取决于条件——例如取决于动词前面的词。例如,我可能想转换这句话 - 包含三个动词 (does, hurt, 运行):

(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 行中的条件,第二个 VB 在第 18 行中处理 - 导致一个错误。我对为什么会这样感到困惑。我没看到什么?如何解决?

非常感谢您的帮助。

这里的问题似乎是您只是在预先编译的词性标记字符串列表中查找 token.tag_ 字符串值的索引。这总是 returns 第一个 匹配 – 所以在 "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,returns 它的索引在父文档中。理想情况下,您希望处理文本一次,存储 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高性能,让你索引它们,在任何地方迭代标记,更重要的是,永远不会破坏原始文本中可用的任何信息。一旦您的输出是单个文本字符串加上空格和您添加的其他字符,您将无法很容易地恢复原始标记。您也不会知道哪个标记附加了空格以及各个标记如何相互关联 are/were。

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