2017-06-29 53 views
3

我正在对由于PDF到txt转换错误,某些时候合并在一起的文本进行文本分析。所以,我想匹配字符串,而不是匹配单词。Python 3.5 - 获取计数器报告零频率项目

例如,我有字符串:

mystring='The lossof our income made us go into debt but this is not too bad as we like some debts.' 

我搜索

key_words=['loss', 'debt', 'debts', 'elephant'] 

输出应该是这样的形式:

Filename Debt Debts Loss Elephant 
mystring 2 1  1 0 

我作品中的代码好吧,除了一些小故障:1)它没有报告零频词的频率(所以'大象'不会出现在输出t:2)key_words中单词的顺序似乎很重要(即。我有时会为“债务”和“债务”分别得到1个计数,有时它只会报告2个“债务”,而且“债务没有报告。如果我设法将变量名称“打印”到数据集中,我可以接受第二点...但不知道如何。

下面是相关的代码。谢谢! PS。不用说,它不是最优雅的一段代码,但我正在慢慢学习。

bad=set(['debts', 'debt']) 

csvfile=open("freq_10k_test.csv", "w", newline='', encoding='cp850', errors='replace') 
writer=csv.writer(csvfile) 
for filename in glob.glob('*.txt'): 

    with open(filename, encoding='utf-8', errors='ignore') as f: 
     file_name=[] 
     file_name.append(filename) 

     new_review=[f.read()] 
     freq_all=[] 
     rev=[] 

     from collections import Counter 

     for review in new_review: 
     review_processed=review.lower() 
     for p in list(punctuation): 
      review_processed=review_processed.replace(p,'') 
      pattern = re.compile("|".join(bad), flags = re.IGNORECASE) 
      freq_iter=collections.Counter(pattern.findall(review_processed))   

     frequency=[value for (key,value) in sorted(freq_iter.items())] 
     freq_all.append(frequency) 
     freq=[v for v in freq_all] 

    fulldata = [ [file_name[i]] + freq for i, freq in enumerate(freq)] 

    writer=csv.writer(open("freq_10k_test.csv",'a',newline='', encoding='cp850', errors='replace')) 
    writer.writerows(fulldata) 
    csvfile.flush() 

回答

1

您只需预先初始化计数器,这样的事情:

freq_iter = collections.Counter() 
freq_iter.update({x:0 for x in bad}) 
freq_iter.update(pattern.findall(review_processed)) 

一关于的好消息是你实际上不需要预先初始化它 - 你可以做c = Counter(); c['key'] += 1,但是如果你愿意的话,没有什么能阻止你将某些值初始化为0。

对于debt/debts的事情 - 这只是一个没有明确指出的问题。你在做什么想要这种情况下做的代码?如果您希望它匹配匹配的最长模式,则需要将列表最长排序,这将解决它。如果你想同时报告,你可能需要做多个搜索并保存所有结果。

已更新,以增加一些信息,为什么它找不到debts:这与正则表达式findall其他更多。 re.findall总是寻找最短的比赛,而且一旦它找到一个,它不包括在随后的比赛:

In [2]: re.findall('(debt|debts)', 'debtor debts my debt') 
Out[2]: ['debt', 'debt', 'debt'] 

如果你真的想找到每一个字的所有情况下,你需要做的他们分别:

In [3]: re.findall('debt', 'debtor debts my debt') 
Out[3]: ['debt', 'debt', 'debt'] 

In [4]: re.findall('debts', 'debtor debts my debt') 
Out[4]: ['debts'] 

但是,也许你真正需要的是。在这种情况下,使用\b运营商需要一个字符:

In [13]: re.findall(r'\bdebt\b', 'debtor debts my debt') 
Out[13]: ['debt'] 

In [14]: re.findall(r'(\b(?:debt|debts)\b)', 'debtor debts my debt') 
Out[14]: ['debts', 'debt'] 

我不知道这是否是你想要的或不...在这种情况下,它能够区分debtdebts正确,但它错过了debtor,因为它只匹配一个子字符串,我们不问它。

根据您的使用情况,您可能需要查看词干文本......我相信在nltk中有一个非常简单(仅用于一次,因此我不会尝试发布示例。 ..这个问题Combining text stemming and removal of punctuation in NLTK and scikit-learn可能是有用的),它应该减少debt,debtsdebtor所有到相同的根词debt,并为其他单词做类似的事情。这可能会或可能不会有帮助;我不知道你在做什么。

+0

但是,在计数器中使用零值时要小心。如果你使用计数器进行一些算术运算,那么[键和值可以默默丢失](https://stackoverflow.com/q/21887125/674039)。 – wim

+0

谢谢。我必须通读完整的清单,看看我是否保留单数/复数。为了我自己的利益,为什么Counter没有发现列表中所有字符串的出现,但只保持最短(即“债务”与“债务”)? –

+0

谢谢@wim,很高兴知道。 –

1

像你想:

mystring='The lossof our income made us go into debt but this is not too bad as we like some debts.' 
key_words=['loss', 'debt', 'debts', 'elephant'] 
for kw in key_words: 
    count = mystring.count(kw) 
    print('%s %s' % (kw, count)) 

或者单词:

from collections import defaultdict 
words = set(mystring.split()) 
key_words=['loss', 'debt', 'debts', 'elephant'] 
d = defaultdict(int) 
for word in words: 
    d[word] += 1 

for kw in key_words: 
    print('%s %s' % (kw, d[kw])) 
+1

在第2部分中,您可以将'dict()'更改为'defaultdict(int)'以摆脱内部'if'语句。 – woodpav

+0

已编辑!我不知道这个伎俩,有用! ;) –

+0

谢谢。我一回到我的电脑就会测试这个。 –