如何选择合适的数据结构统计文本单词总频次及对应词性标签频次
如何选择合适的数据结构统计文本单词总频次及对应词性标签频次
嘿,这个问题我之前处理语料库的时候刚好碰到过!其实用嵌套的字典结构(或者借助Python标准库collections里的工具简化代码)就能完美解决,既清晰又容易扩展。
先给你理清楚思路:我们需要的是一个「单词→(总出现次数 + 各POS标签的出现次数)」的映射,所以最直接的就是用外层字典存单词,每个单词对应的值要么是一个包含总次数和POS统计的子字典,要么直接用Counter来存POS的计数(再单独存总次数,或者从POS计数里求和也行)。
方案1:用基础嵌套字典(不依赖额外模块)
如果不想引入额外模块,用普通字典就能实现,逻辑也很直观:
首先初始化一个字典,每个单词对应的条目包含总次数和POS标签的计数:
# 结构:{单词: {"total": 总出现次数, "pos": {POS标签: 次数}}} word_pos_stats = {} with open(input_filename, "r") as f: for line in f: line = line.strip() if not line: # 直接跳过空行,比捕获异常更直观 continue try: # 从右边分割一次,避免单词里包含/的情况(虽然语料库一般不会有) word, pos = line.rsplit('/', 1) pos = pos.strip() # 去掉POS标签里可能的换行符或空格 # 如果单词还没在统计字典里,初始化它的结构 if word not in word_pos_stats: word_pos_stats[word] = {"total": 0, "pos": {}} # 更新总次数 word_pos_stats[word]["total"] += 1 # 更新对应POS标签的次数,用get方法处理首次出现的标签 word_pos_stats[word]["pos"][pos] = word_pos_stats[word]["pos"].get(pos, 0) + 1 except ValueError: # 处理不符合word/POS格式的行,比如格式错误的行 print(f"跳过无效行: {line}") pass
接下来就是把统计结果写入输出文件,注意要把POS标签按出现次数降序排列(和你给的示例格式一致):
with open(output_filename, "w") as out_f: # 遍历每个单词的统计数据 for word, stats in word_pos_stats.items(): total_count = stats["total"] # 把POS标签和次数按次数从多到少排序 sorted_pos = sorted(stats["pos"].items(), key=lambda x: -x[1]) # 拼接成需要的行格式:单词 + 总次数 + 每个POS标签+次数的组合 line_parts = [word, str(total_count)] for pos, cnt in sorted_pos: line_parts.extend([pos, str(cnt)]) # 用制表符分隔,写入文件 out_f.write("\t".join(line_parts) + "\n")
方案2:用collections模块简化代码
如果想让代码更简洁,推荐用collections里的defaultdict和Counter,它们能帮你省去很多初始化的判断:
from collections import defaultdict, Counter # 外层字典:单词 → 内层Counter统计各POS标签的次数 word_pos_counter = defaultdict(Counter) # 单独存单词的总出现次数(或者也可以从Counter的values求和得到) word_total_counts = defaultdict(int) with open(input_filename, "r") as f: for line in f: line = line.strip() if not line: continue try: word, pos = line.rsplit('/', 1) pos = pos.strip() # Counter直接支持标签计数的自增 word_pos_counter[word][pos] += 1 word_total_counts[word] += 1 except ValueError: print(f"跳过无效行: {line}") pass
输出部分和之前逻辑一致,只是取数据的方式稍有不同:
with open(output_filename, "w") as out_f: for word, total in word_total_counts.items(): # 对POS标签按次数降序排序 sorted_pos = sorted(word_pos_counter[word].items(), key=lambda x: -x[1]) line_parts = [word, str(total)] for pos, cnt in sorted_pos: line_parts.extend([pos, str(cnt)]) out_f.write("\t".join(line_parts) + "\n")
为什么这个结构合适?
这个嵌套的映射结构完全匹配你要统计的层级:
- 外层字典直接对应「单词→它的所有统计数据」的关系,方便快速查找每个单词的信息;
- 内层的POS计数字典(或Counter)刚好对应「每个标签出现多少次」的需求,和你输出格式里的后续列完全对应;
- 后续输出时,只需要把这些层级的数据依次取出拼接,就能完美生成你要的输出格式。
另外,如果你想更简洁,甚至可以把总次数从POS计数的总和里计算出来(sum(word_pos_counter[word].values())),这样可以省去单独的word_total_counts字典,不过单独存总次数会稍微提升一点效率,尤其是处理大语料库的时候。
备注:内容来源于stack exchange,提问作者djeigi
相关产品推荐
相关产品推荐

