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

6 投票
4 回答
4361 浏览
提问于 2025-04-16 14:15

我创建了一个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

这个模块会打印出文件中所有的“非无意义”单词以及它们出现的频率。输出的结果看起来像这样:

{{'saturday': 1, 'irish': 1, 'family': 1, 'give': 1, 'year': 2, 'weekend': 1, 'steve': 1, 'guests': 1, 'questions': 1, 'in': 2, 'effort': 1, 'partner': 1, 'extinction': 1, 'dress': 1, 'children': 4, 'utans': 1, '27': 1, 'raise': 1, 'closet': 1, 'haired': 2, 'make': 1, 'humphreys': 1, 'relatives': 1, 'zoo': 5, 'endangered': 1, 'sunday': 1, 'special': 1, 'answer': 1, 'public': 1, 'awareness': 1, 'planned': 1, 'activities': 1, 'rhiona': 1, 'orangutans': 4, 'plans': 1, 'leonie': 1, 'orang': 1, 'yesterday': 2, 'free': 2, 'hand': 1, 'wild': 1, 'independent': 1, 'part': 1, 'preparing': 1, 'revealed': 1, 'day': 1, 'man': 1, 'picture': 1, 'keane': 1, 'animals': 1, '14': 1, 'kevin': 1, '16': 1, '32': 1, 'age': 1, 'sibu': 1, 'dublin': 2, 'keepers': 1, 'face': 1, 'mujur': 1, 'red': 2, 'orangutan': 1, 'species': 1, 'entry': 1, 'efforts': 1, 'shows': 1, '11am': 1, 'influx': 1, '3pm': 1}

{'newest': 1, 'birth': 2, 'orang': 1, 'month': 1, 'steve': 1, 'questions': 1, 'utans': 1, 'children': 4, 'staff': 1, 'limelight': 1, '27': 1, 'based': 1, 'concerned': 1, 'sunday': 1, '3pm': 1, 'finally': 1, '4': 1, 'maeve': 1, 'awareness': 1, 'gave': 1, 'activities': 1, 'giraffe': 1, 'facebook': 1, 'preparing': 1, 'background': 1, 'nurturing': 1, 'day': 1, 'debut': 1, 'rothschild': 1, 'keepers': 1, 'email': 1, 'steps': 1, '11am': 1, 'page': 1, 'picture': 1, 'born': 1, 'result': 1, 'year': 2, 'saturday': 1, 'special': 1, 'closet': 1, 'haired': 2, 'section': 1, 'bennet': 2, 'mum': 3, 'mujur': 1, 'conditions': 1, 'public': 1, 'red': 2, 'shows': 1, 'orangutans': 4, 'free': 2, 'keeper': 1, 'november': 1, 'care': 1, 'sending': 1, 'great': 1, 'origins': 1, '32': 1, 'invited': 1, 'dublin': 2, 'planned': 1, 'orangutan': 1, 'efforts': 1, 'influx': 1, 'named': 1, 'family': 1, 'delighted': 1, 'weather': 1, 'guests': 1, 'extinction': 1, 'post': 1, 'impressed': 1, 'raise': 1, 'revealed': 1, 'remained': 1, 'humphreys': 1, 'confident': 1, 'calf': 3, 'entrance': 1, 'shane': 1, 'part': 1, 'helen': 1, 'attentive': 1, 'effort': 1, 'case': 1, 'made': 2, 'animals': 1, '14': 1, '16': 1, 'ms': 1, 'wild': 1, 'savanna': 1, 'irish': 1, 'give': 1, 'resident': 1, 'suggestions': 1, 'slip': 1, 'in': 2, 'partner': 1, 'dress': 1, 'species': 1, 'kevin': 1, 'rhiona': 1, 'make': 1, 'zoo': 3, 'endangered': 1, 'relatives': 1, 'answer': 1, 'poor': 1, 'independent': 1, 'plans': 1, 'leonie': 1, 'time': 1, 'yesterday': 1, 'hand': 1, 'hickey': 1, 'weekend': 1, 'man': 1, 'sibu': 1, 'age': 1, 'steady': 2, 'face': 1, 'confinement': 1, 'african': 2, 'entry': 1, 'keane': 1, 'clarke': 2, 'left': 1}

但是我需要把多个文件中的总数加在一起,比如说“zoo”这个词在所有文件中的总出现次数。比如第一个文件中“zoo”出现了5次,第二个文件中出现了3次,那么总共就是8次。

我现在还不知道怎么才能统计多个文件中的单词,而不是一个一个文件地来。

有没有什么好的主意呢?

4 个回答

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]
              )
            )
2

fileinput模块可以让你轻松处理多个文件。

3

'<\/?[ap][^>]*>' 这个表达式中,反斜杠是多余的,因为 '/' 不是一个特殊字符。

'[^\w]' 其实就是 '\W'。顺便说一下,使用 '[^\w]+' 会比只用一个 '[^\w]' 更高效。

re.DOTALLr'<\/?[ap][^>]*>' 中没有用,因为这个正则表达式里没有点号。

如果你用 words = f.read().lower() 把字母都变成小写,那么就不需要 re.IGNORECASE 了。

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

file 这个名字不太好,因为它会覆盖掉一个已经存在的内置函数的名字。

用生成器表达式替换代码行来获取 htmfiles 会更好。

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

你也可以把停用词的组合放在用来替换成 ' ' 的正则表达式里:

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

我不太理解你的问题。请提供更多细节。

撰写回答