使用Python正则表达式计算文档中单词的频率

6
创建了一个 Python 模块,它可以读取文件、去除停用词,并输出一个 Python 字典,其中包含单词及其频率(即在文档中出现的次数)。
def run():
filelist = os.listdir(path)
regex = re.compile(r'.*<div class="body">(.*?)</div>.*', re.DOTALL | re.IGNORECASE)
reg1 = re.compile(r'<\/?[ap][^>]*>', re.DOTALL | re.IGNORECASE)
quotereg = re.compile(r'&quot;', re.DOTALL | re.IGNORECASE)
puncreg = re.compile(r'[^\w]', re.DOTALL | re.IGNORECASE)
f = open(stopwordfile, 'r')
stopwords = f.read().lower().split()
totalfreq = {}

filewords = {}
htmlfiles = []
for file in filelist:
    if file[-5:] == '.html':
        htmlfiles.append(file)

for file in htmlfiles:
    f = open(path + file, 'r')
    words = f.read().lower()
    words = regex.findall(words)[0]
    words = quotereg.sub(' ', words)
    words = reg1.sub(' ', words)
    words = puncreg.sub(' ', words)
    words = words.strip().split()

    for w in stopwords:
        while w in words:
            words.remove(w)

     freq = {}
    for w in words:
       if w in freq:
           totalfreq[w] = totalfreq[w] + 1
           freq[w] = freq[w] + 1
       else:
           totalfreq[w] = 1
           freq[w] = 1
           filewords[file] = freq
    
  
    print totalfreq

这个命令可以打印出文件中所有“非停用词”以及它们在文件中出现的频率:输出结果如下:

{"周六": 1, "爱尔兰": 1, "家庭": 1, "给予": 1, "年份": 2, "周末": 1, "史蒂夫": 1, "客人": 1, "问题": 1, "在": 2, "努力": 1, "合作伙伴": 1, "灭绝": 1, "服装": 1, "儿童": 4, "乌坦斯": 1, "27": 1, "筹款": 1, "壁橱": 1, "多毛的": 2, "制造": 1, "汉弗莱斯": 1, "亲戚": 1, "动物园": 5, "濒危": 1, "周日": 1, "特别": 1, "回答": 1, "公众": 1, "意识": 1, "计划": 1, "活动": 1, "里奥纳": 1, "猩猩": 4, "计划": 1, "莱昂尼": 1, "人名": 1, "自由的": 2, "手": 1, "野生的": 1, "独立的": 1, "部分": 1, "准备": 1, "揭示": 1, "一天": 1, "男人": 1, "图片": 1, "基恩": 1, "动物": 1, "14": 1, "凯文": 1, "16": 1, "32": 1, "年龄": 1, "西布": 1, "都柏林": 2, "饲养员": 1, "面对": 1, "穆杰尔": 1, "红色的": 2, "猩猩": 1, "物种": 1, "进入": 1, "努力": 1, "显示": 1, "上午11点": 1, "涌入": 1, "下午3点": 1} {"最新": 1, "出生": 2, "猩猩": 1, "月份": 1, "史蒂夫": 1, "问题": 1, "乌坦斯": 1, "儿童": 4, "工作人员": 1, "聚光灯": 1, "27": 1, "基于": 1, "担忧": 1, "周日": 1, "下午3点": 1, "最终": 1, "4": 1, "梅芙": 1, "意识": 1, "给予": 1, "活动": 1, "长颈鹿": 1, "Facebook": 1, "准备": 1, "背景": 1, "培养": 1, "一天": 1, "首次亮相": 1, "罗斯柴尔德": 1, "饲养员": 1, "电子邮件": 1, "步骤": 1, "上午11点": 1, "页面": 1, "图片": 1, "出生": 1, "结果": 1, "年份": 2, "周六": 1, "特别": 1, "壁橱": 1, "多毛的": 2, "部分": 1, "本内特": 2, "妈妈": 3, "穆杰尔": 1, "条件": 1, "公众": 1, "红色的": 2, "演出": 1, "猩猩": 4, "自由的":

但我需要将来自多个文件或大量文件的两个总数相加,以给出所有文件中单词“zoo”的总计数。第一个文件中 zoo=5,第二个文件中 zoo=3,总计=8。

我似乎无法弄清楚如何对许多文件而不仅仅是一个文件进行单词计数。

有任何想法吗?!

4个回答

3

'<\/?[ap][^>]*>'中的反斜杠是无用的,因为'/'不是特殊字符。

'[^\w]'等同于'\W'。顺便说一下,'[^\w]+'比一个'[^\w]'更有效率。

re.DOTALL对于r'<\/?[ap][^>]*>'是无用的,因为这个RE中没有点号。

如果你使用words = f.read().lower()将字母转换为小写,那么你就不需要re.IGNORECASE了。

替换的正则表达式可以放在一个RE中:reg123 = re.compile(r'(</?[ap][^>]*>|&quot;|\W+)')

file不是文件名的好名称,它会覆盖现有的内置函数名称。

通过生成器表达式来替换代码行以获得htmfiles更好。

我不明白words = regex.findall(words)[0]中的'[0]'是什么意思。

你也可以在用于替换为' '的RE中分组停用词。

stopwords = '|'.join(f.read().lower().split())

需要包含在替换的正则表达式中

filewords[file] = freq 的缩进有问题

.

我建议你以下改进; 我没有测试过,因为我没有要处理的文件。它肯定不完美。如果有不清楚的地方,请问。

def run():

    from collection import difaultdict

    with open(stopwordfile, 'r') as f:
        stopwords = '|'.join(f.read().lower().split())

    regex = re.compile(r'.*<div class="body">(.*?)</div>.*', re.DOTALL)
    reg123 = re.compile(r'(</?[ap][^>]*>|&quot;|\W+|'+stopwords+')')

    totalfreq = defaultdict(int)
    filewords = {}

    for filename in (fn for fn in os.listdir(path) if fn[-5:] == '.html'):
        with open(path + filename, 'r') as f:
            ch = regex.findall(f.read().lower())[0]
            ch = reg123.sub(' ', ch)
            words = ch.strip().split()

        freq = defaultdict(int)
        for w in words:
            totalfreq[w] += 1
            freq[w] += 1
        filewords[filename] = freq

    print totalfreq

我不是很理解你的问题。请提供更多详细信息。

感谢您提供的改进解决方案,您能解释一下defaultdict是什么或者它来自哪里吗?它只是在字典中保存当前的非停用词吗?谢谢。 - jenniem001
@jenniem001 defaultdict 是类型为“dict”的子类,从模块collections中导入。d = defaultdict(int)创建一个字典d,它的行为与普通字典相同,但还具有以下行为:当k不是d的键时,在表达式中使用d[k]会在任何后续执行之前触发在字典d中创建项k:0。如果d被定义为defaultdict(list),则创建的项为**k:[ ]**等。使用defaultdict可以减少代码中的行数,使其更易读。 - eyquem

2

使用fileinput模块,您可以轻松处理多个文件。


0
一个可能的解决方案是:
result = {}
for d in dictionaries:
  for k,v in d.iteritems():
    result[k] = result.get(k,0) + v

for k,v in result.iteritems():
  print('total occurences of {0}: {1}'.format(k,v))

...其中dictionaries只是输入文件中每个单词频率映射的列表。


什么是单词到频率映射? 您能解释一下您的答案吗?我对程序的这部分有点困惑。谢谢。 - jenniem001
通过单词到频率映射,我指的是您上面发布的字典,即将一个单词映射到文件中出现次数的数字。我的代码将所有单个文件的数据合并在一起。 - Alexander Gessler

0
假设files是您拥有的每个文件的频率列表,请尝试以下操作:
from itertools import groupby, chain
total = dict(
              (key, sum(c[1] for c in vals))
              for key, vals in 
              groupby(
                  sorted(
                      chain(
                          *(f.items() for f in files)
                      )
                  ), 
                  lambda x: x[0]
              )
            )

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接