
本文介绍使用 python 的 defaultdict 和 counter 组合结构,高效统计带词性标签的文本中每个单词的总频次及其在各词性(pos)上的分布,并生成结构化输出。
本文介绍使用 python 的 defaultdict 和 counter 组合结构,高效统计带词性标签的文本中每个单词的总频次及其在各词性(pos)上的分布,并生成结构化输出。
在自然语言处理的预处理任务中,常需解析形如 word/POS 的标注文本(如 Penn Treebank 格式),并按单词粒度聚合其所有出现次数及对应的词性标签频次。核心挑战在于:同一单词可能具有多种词性(如 allow 可作动词 VB 或动词原形 VBP),需支持嵌套计数——即对每个单词,独立记录每种 POS 标签的出现次数。
推荐采用 collections.defaultdict(Counter) 作为主数据结构:
- 外层
defaultdict以单词为键,自动为新单词初始化一个空Counter; - 内层
Counter高效累积该单词下各 POS 标签的频次,支持.total()获取总频次、.items()迭代标签-计数对。
以下是完整可运行示例:
from collections import defaultdict, Counter
input_filename = "pos_tagged.txt"
word_pos_counts = defaultdict(Counter)
with open(input_filename, "r") as f:
for line in f:
line = line.strip()
if not line: # 跳过空行
continue
try:
word, pos = line.rsplit('/', 1) # 仅从右侧分割一次,避免单词含 '/' 时出错
word_pos_counts[word][pos] += 1
except ValueError:
# 忽略格式异常行(如无 '/' 的行)
continue
# 按单词字母序输出(可选,提升可读性)
for word in sorted(word_pos_counts):
counts = word_pos_counts[word]
pos_parts = [f"{pos} {cnt}" for pos, cnt in counts.items()]
print(f"{word}\t{counts.total()}\t" + "\t".join(pos_parts))关键注意事项:
- 使用
line.strip()清除换行符和首尾空白,防止rsplit因多余空格失败; - 显式检查
if not line可替代部分ValueError异常处理,使逻辑更清晰; - 输出时建议按单词排序(
sorted(word_pos_counts)),确保结果可复现; - 若需写入文件而非打印,将
print(...)替换为output_file.write(... + '\n')即可; -
Counter.total()是 Python 3.10+ 新增方法,若使用旧版本,请用sum(counts.values())替代。
该方案时间复杂度为 O(N),空间占用合理,兼具可读性与扩展性,是处理此类多层级计数任务的标准实践。

















