利用CountVectorizer词袋模型,如何对中文语料库词频统计?
发布于 作者:苏南大叔 来源:程序如此灵动~CountVectorizer
被称之为词袋模型,它来自于sklearn
,日常用于词频统计。苏南大叔在之前也写过有关CountVectorizer
词频统计的文章。不过,现在对CountVectorizer
又有了新的理解,所以,这里再次写文章总结对CountVectorizer
新的理解。
苏南大叔的“程序如此灵动”技术博客,记录苏南大叔的代码经验总结。本文测试环境:win10
,python@3.11.0
,scikit-learn@1.2.2
。
语料库分词
如果是英文这种以空格做单词分割的语种,是不需要做这一步分词的处理的。但是,如果是中文这种没有天然空格做分割的语料库,还是需要使用:结巴分词/snownlp
/其它分词工具,进行分词处理的。分词结果可以使用空格或者英文逗号或中文逗号进行分割。
参考文章:
下面的代码中,语料库变量是苏南大叔手工分词的结果:
corpus = [
"老许 是 一只 黑色 猫咪",
"虎子,是,一只,黑色,狗子",
"哈吉米,是,一只,黑色,猫咪",
"老许 非常 可爱",
"虎子,同样,非常,可爱",
"哈吉米,也,很,可爱",
] # 语料库,空格或者中英文逗号分割,都是可以的。
初始化词袋模型
下面出场的是本文的主角CountVectorizer
词袋模型,它有很多参数。不过,大多数都暂时用不到。可以直接初始化:
from sklearn.feature_extraction.text import CountVectorizer
cv = CountVectorizer() # 词袋模型
最常用的就是一个stop_words
,停用词列表,可以过滤掉一些无用的词儿,以便于更加精准的了解数据。比如:
from sklearn.feature_extraction.text import CountVectorizer
cv = CountVectorizer(stop_words=["一只", "非常"]) # 词袋模型
然而,经过实际运行会发现:一些单字的词儿(比如语料库中的“是/也/很”),即使没有在stop_words
中设置,也会被过滤掉。那么,如果您介意的话,可以通过下面的参数,把这些单字的中文词儿找回来。这个正则表达式,对其它一些非中文语种可能不是很合适。所以,视需求进行修改即可。
from sklearn.feature_extraction.text import CountVectorizer
cv = CountVectorizer(token_pattern="[\u4e00-\u9fa5_a-zA-Z0-9]{1,}") # 词袋模型
填充数据进行转换
cv_fit = cv.fit_transform(corpus) # (0, 6) 1 (0, 0) 1
或者下面这样调用,也是可以的,两者等效。
cv.fit(corpus)
cv_fit = cv.transform(corpus) # (0, 6) 1 (0, 0) 1
特征及词汇表
print(cv.get_feature_names_out(), len(cv.get_feature_names_out()))
# 模型默认认为:单字的词儿,无法构成feature特征
# ['一只' '可爱' '同样' '哈吉米' '狗子' '猫咪' '老许' '虎子' '非常' '黑色'] 10
print(cv.vocabulary_, len(cv.vocabulary_))
# 词汇表就是:{特征:索引}
# {'老许': 6, '一只': 0, '黑色': 9, '猫咪': 5, '虎子': 7, '狗子': 4, '哈吉米': 3, '非常': 8, '可爱': 1, '同样': 2} 10
从结果可以看到,这个结果是自动过滤了那些单字的词儿(token
)的。所以,可能的原因是:
- 设置了停用词
stop_words
,包含了目标词汇。 - 没有设置
token_pattern
,没有把目标词汇包含进来。
print(cv.get_stop_words())
stop_words
是自己设置的,为啥还要再求解一次呢?没有设置的话,返回None
,也就是大家通常上理解的Null
。
cv_fit 格式及类型
print(cv_fit,type(cv_fit)) # (0, 6) 1 (0, 0) 1 <class 'scipy.sparse._csr.csr_matrix'>
输出:
(0, 6) 1
(0, 0) 1
(0, 9) 1
(0, 5) 1
(1, 0) 1
(1, 9) 1
(1, 7) 1
(1, 4) 1
(2, 0) 1
(2, 9) 1
(2, 5) 1
(2, 3) 1
(3, 6) 1
(3, 8) 1
(3, 1) 1
(4, 7) 1
(4, 8) 1
(4, 1) 1
(4, 2) 1
(5, 3) 1
(5, 1) 1 <class 'scipy.sparse._csr.csr_matrix'>
对于cv_fit
的输出的正确理解,还是需要结合词汇表cv.vocabulary_
的结果的。词汇表cv.vocabulary_
输出是:
{'老许': 6, '一只': 0, '黑色': 9, '猫咪': 5, '虎子': 7, '狗子': 4, '哈吉米': 3, '非常': 8, '可爱': 1, '同样': 2}
- 其格式是:
(语料库的id,词汇表的索引) 当前语料库id里面,该词汇出现了几次
。 cv_fit
类型是一个没见过的新类型:<class 'scipy.sparse._csr.csr_matrix'>
。
例如,cv_fit
的条目(2, 0) 1
:
2
表示语料库索引为2的语料(就是第三条语料),"哈吉米,是,一只,黑色,猫咪"。0
根据词汇表可以知道:是“一只”。1
,就是表示“一只”这个token
在"哈吉米,是,一只,黑色,猫咪"语料中,出现了一次。
cv_fit.toarray()
下面的就是苏南大叔自己写的分析代码了,主要功能就是对官方函数输出的结果,再组合解释了一下。cv_fit.toarray()
是每条语料化身为词汇表索引的结果,对应索引位置上显示计数值。
print(cv_fit.toarray()) # [[1 0 0 0 0 1 1 0 0 1] [1 0 0 0 1 0 0 1 0 1]]
[1 0 0 0 0 1 1 0 0 1]
翻译回来的预料是:一只,,,,,猫咪,老许,,,黑色
。1
表示:对应的词儿是出现的词儿出现了一次。0
表述没有出现。
所以,它可能另外的表现形式是:[2,0,3,5,6,1]
这样其它的数字组合。
print(cv_fit.toarray().sum(axis=0)) # 每个词在所有文档中的词频 [3 3 1 2 1 2 2 2 2 3]
如果把数据理解到excel
里面,就很好理解cv_fit.toarray().sum(axis=0)
,就是账单方向的求和。
- 数据表的每行是第一条语料库。
- 数据表的每列是词汇表里面的每个词汇(
token
)。
数据分析最终结果
_cnt = cv_fit.toarray().sum(axis=0)
_dic = cv.vocabulary_
res = {v: k for k, v in _dic.items()}
res2 = [(k, res[k]) for k in sorted(res.keys())]
# print(res)
# print(res2)
for key, value in res2:
print(value, ",词频是:", _cnt[key])
输出:
一只 ,词频是: 3
可爱 ,词频是: 3
同样 ,词频是: 1
哈吉米 ,词频是: 2
狗子 ,词频是: 1
猫咪 ,词频是: 2
老许 ,词频是: 2
虎子 ,词频是: 2
非常 ,词频是: 2
黑色 ,词频是: 3
这里涉及了下面两篇文章:
完整代码
可能存在的问题 .get_feature_names_out()
cv.get_feature_names_out()
,在老版本里面,其函数原型是:cv.get_feature_names()
。
所以,如果还使用以前的文章里面的代码的话,会得到如下错误提示信息:
AttributeError: 'CountVectorizer' object has no attribute 'get_feature_names'. Did you mean: 'get_feature_names_out'?
解决方案就是:给get_feature_names()
函数换个名字,换成get_feature_names_out()
。
结语
苏南大叔的更多sklearn
机器学习的经验文章,请点击下面的链接:
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。