计算短语除非在Python之前有另一个短语

时间:2015-09-11 02:43:43

标签: python regex python-2.7 pandas

在Python 2.7中使用pandas我试图计算一个短语(例如,#34;非常好")出现在CSV文件中存储的文本中的次数。我有多个短语和多个文本。我使用以下代码成功完成了第一部分:

<key>NSLocationWhenInUseUsageDescription</key>
<string>The spirit of stack overflow is coders helping coders</string>

<key>NSLocationAlwaysUsageDescription</key>
<string>I have learned more on stack overflow than anything else</string>

但是,如果短语前面有不同的短语(例如,&#34;它不是&#34;),我就不想计算短语。因此我使用了负面的后观断言:

for row in df_book.itertuples():
    index, text = row
    normed = re.sub(r'[^\sa-zA-Z0-9]', '', text).lower().strip()

for row in df_phrase.itertuples():
    index, phrase = row
    count = sum(1 for x in re.finditer(r"\b%s\b" % (re.escape(phrase)), normed))
    file.write("%s," % (count))

这种方法的问题在于它记录了从df_negations数据帧中提取的每个否定值。因此,如果发现者没有找到&#34;它不是非常好&#39;&#34;,那么它将记录一个0.等等每一个可能的否定。

我真正想要的只是在没有前面的短语的情况下使用短语的总次数。换句话说,我希望每次都能算上非常好的&#34;发生,但只有当我的否定列表中没有否定之前(&#34;它不是&#34;)时才会发生。

另外,我非常高兴听到关于让流程更快地运行的建议。我有100多个短语,100多个否定和100多万个文本。

1 个答案:

答案 0 :(得分:0)

我真的不做大熊猫,但是这个俗气的非熊猫版本会根据你发给我的数据给出一些结果。

主要的复杂性是Python re模块不允许可变宽度负面的后置断言。因此,此示例查找匹配的短语,保存每个短语的起始位置和文本,然后,如果找到任何短语,则在同一源字符串中查找否定,保存否定的结束位置。为了确保否定结束位置与短语起始位置相同,我们在每次否定之后捕获空白以及否定本身。

重复调用re模块中的函数是相当昂贵的。如果你说的话有很多文字,你可能想要批量处理,例如在某些源字符串上使用'non-matching-string'.join()

import re
from collections import defaultdict
import csv

def read_csv(fname):
    with open(fname, 'r') as csvfile:
        result = list(csv.reader(csvfile))
    return result

df_negations = read_csv('negations.csv')[1:]
df_phrases = read_csv('phrases.csv')[1:]
df_book = read_csv('test.csv')[1:]

negations = (str(row[0]) for row in df_negations)
phrases = (str(re.escape(row[1])) for row in df_phrases)

# Add a word to the negation pattern so it overlaps the
# next group.
negation_pattern = r"\b((?:%s)\W+)" % '|'.join(negations)
phrase_pattern = r"\b(%s)\b" % '|'.join(phrases)

counts = defaultdict(int)

for row in df_book:
    normed = re.sub(r'[^\sa-zA-Z0-9]', '', row[0]).lower().strip()

    # Find the location and text of any matching good groups
    phrases = [(x.start(), x.group()) for x in
                    re.finditer(phrase_pattern, normed)]
    if not phrases:
        continue

    # If we had matches, find the (start, end) locations of matching bad
    # groups
    negated = set(x.end() for x in re.finditer(negation_pattern, normed))

    for start, text in phrases:
        if start not in negated:
            counts[text] += 1
        else:
            print("%r negated and ignored" % text)

for pattern, count in sorted(counts.items()):
    print(count, pattern)