相信大家近些年一定解除了很多形形色色的词云,那么那些亮眼的词云是如何制作的呢?我们是不是也可以用 Python 制作一个呢?
俗话说,知其所往方可往(我瞎编的ヽ(・ω・´メ))。我们首先要明白其原理,知晓其流程,才能达到我们的目的。
显然,词云是以文本里的某一词汇的出现频率为依据,将词汇用不同大小的字体展现出来的一个文本处理过程,最后以一定的形态输出,我们称其为图像处理过程。那么,我们就知道了其分为两步:
按照语言类型的不同,我们将其分类为拼音类文本(以英文文本为代表)和象形类文本(以中文文本为代表)。两者的文本分割处理略有不同,在此分类讨论。
对于英文文本来说,其文本的分割大致可分为以下几个步骤:
由于中文文本中的单词不是通过空格或者标点符号进行分割,因此中文及其类似语言存在一个重要的分词问题,即如何正确地划分一段文本所出现的词汇。
例如最出名的一段话下雨天留客天留我不留,你会如何划分呢???
在系统命令行输入pip install jieba
即可
jieba 库包含的主要函数如下:
jieba.lcut(s)
:精确模式,返回一个列表类型jieba.lcut(s,cut_all=True)
:全模式,返回一个列表类型jieba.lcut_for_search(s)
:搜索引擎模式,返回一个列表类型jieba.add_word(w)
:向分词词典中添加新词 w数据展示方式多样,传统的统计图表虽然科学但略显古板。尤其对于文本来说,直观简单又充满艺术感的设计效果给人带来的不仅是文本的分析结果,更是美的享受。
词云以词语为基本单元,根据其在文本中出现的频率设计不同大小以形成视觉上的不同效果,形成“关键词云层”,从而使读者对文本主旨一目了然。
wordcloud 库是专门用于根据文本生成词云的 Python 第三方库,常用且有趣。
在系统命令行中输入pip install wordcloud
即可
wordcloud 库的核心是 WordCloud 类,所有的功能都封装在其中。
使用时需要实例化一个 WordCloud 类的对象,并调用其 generate(text) 方法将 text 文本转化为词云。WordCloud 类在创建时有一系列可选参数,用于配置词云图。其常用参数如下所示:
WordCloud 对象创建常用参数
font_path
:指定字体文件及其路径,默认为系统字体库 C:\Windows\Fontswidth
:图片宽度,默认400像素height
:图片高度,默认200像素mask
:词云形状,默认为方形图min_font_size
:词云中最小字体号,默认4号font_step
:字号步进间隔,默认为5max_font_size
:词云中最大字体号,默认自动调节max_words
:词云图中最大词数,默认200stopwords
:被排除词列表,词云不予显示background_color
:图片背景颜色,默认黑色WordCloud 类常用方法
generate(txt)
:由 txt 文本生成词云to_file(filename)
:将词云图保存为名为 filename 的图片,路径可自行设置wordcloud 库 默认以空格或标点为分隔符对目标文件进行处理,因此特别适合直接处理英文文本。代码如下:
# -*-coding:utf-8-*-
from wordcloud import WordCloud
txt = open("C:/Users/SHOHOKU/Desktop/hamlet.txt","r").read()
wordcloud = WordCloud(font_path="AdobeKaitiStd-Regular.otf",width=911,height=305,max_words=36).generate(txt)
wordcloud.to_file("C:/Users/SHOHOKU/Desktop/hamlet.png")
效果如图所示:
当然,你既然读到这里了,我这么一个好心的人肯定会送你一份礼物。想了想,送你一份英文文本分割的程序吧,相信以后会用的到:
# -*-coding:utf-8-*-
# 文本预处理
def gettext():
txt = open("C:/Users/SHOHOKU/Desktop/hamlet.txt","r").read() #读取文本
txt = txt.lower() #统一大小写
symbol = [",",".","?","/","<",">",'''""''',"''",\
":",";","]","[]","{}","","+","=","-","_","()",\
"*","&","^","%","$","#","@","!","~","`"]
for s in symbol:
txt = txt.replace(s,"") #以空格代替特殊符号
return txt
# 文本处理
text = gettext() #加载文本
words = text.split() #以空格切分文章
# 词频统计
counts = {} #创建字典类型用来统计词汇及其频数
for word in words:
counts[word] = counts.get(word,0) + 1 #对于已有词汇,返回其序数并+1;对于新词汇,返回0并+1
# 结果展示
items = list(counts.items()) #将字典类型转换为列表类型,便于展示结果
items.sort(key=lambda x:x[1],reverse = True) #按照第二列对结果进行降序排序
for i in range(10): #展示出现次数最高的前10个单词
word,count = items[i]
print("{:<10}{}".format(word,count))
需求:统计三国演义中出现频率前10的人物,并生成相应词云。
第一步:人物出场频率统计
# -*-coding:utf-8-*-
# 文本预处理
def gettext():
txt = open("C:/Users/SHOHOKU/Desktop/threekingdoms.txt","r",encoding="utf-8").read()
return txt
# 文本分割
import jieba
text = gettext()
words = jieba.lcut(text)
# 词频统计
counts = {}
for word in words:
if len(word)==1: #姓名通常不为一字,故做简化处理
continue
else:
counts[word] = counts.get(word,0) + 1
# 结果展示
items = list(counts.items())
items.sort(key = lambda x:x[1],reverse=True)
for i in range(25):
word,count = items[i]
print("{:<10}{}".format(word,count))
结果如图所示:
我们发现,结果中存在着一些问题:有的和人名并无关系,将军这类词很模糊,而且像玄德曰,玄德,刘备其实是同一人物等。这是中文所特有的多语义性造成的。所以我们进行中文文本词频统计时,要适当地扩大探索范围,观察其展示结果的不足并加以改进,像DNA分子一样螺旋上升,才可以到达希望的顶峰。
我们试着改进其文本,以便能更好地满足我们的需求,经过不断尝试,我们可以获得一个大致结果:
# -*-coding:utf-8-*-
# 文本预处理
def gettext():
txt = open("C:/Users/SHOHOKU/Desktop/threekingdoms.txt","r",encoding="utf-8").read()
return txt
excludes = ["将军","荆州","却说","二人","不可","不能","如此",\
"商议","如何","军士","主公","军马","左右","次日","大喜","引兵","天下","于是","东吴","今日","魏兵",\
"不敢","陛下","一人","人马","不知","都督"]
# 文本分割
import jieba
text = gettext()
words = jieba.lcut(text)
# 词频统计
counts = {}
for word in words:
if len(word) == 1: #姓名不可能为一个字,舍弃之
continue
if word=="丞相" or word=="孟德":
rword="曹操"
if word=="诸葛亮" or word=="孔明曰":
rword = "孔明"
if word=="关公" or word=="云长":
rword = "关羽"
if word=="玄德" or word=="玄德曰":
rword = "刘备"
if word=="翼德":
rword = "张飞"
if word=="子龙":
rword = "赵云"
else:
rword = word
counts[rword] = counts.get(rword,0)+1
for word in excludes:
del counts[word]
# 结果展示
items = list(counts.items())
items.sort(key = lambda x:x[1],reverse=True)
for i in range(25):
word,count = items[i]
print("{:<10}{}".format(word,count))
结果如图所示:
我们发现改进过后的代码已经可以大致达到我们的目的,但通过去除词来重新分割文本的方法耗时费力,并不怎么提倡。如果有更好的方法,欢迎与笔者交流。
第二步:在文本分割的基础上利用空格分割文本,然后利用 wordcloud 库输出即可
# -*-coding:utf-8-*-
import jieba
from wordcloud import WordCloud
from scipy.misc import imread
img = imread("c:/Users/SHOHOKU/Desktop/chinamap.jpg")
excludes = ["将军","荆州","却说","二人","不可","不能","如此",\
"商议","如何","军士","主公","军马","左右","次日","大喜","引兵","天下","于是","东吴","今日","魏兵",\
"不敢","陛下","一人","人马","不知"]
def gettext():
f = open("C:/Users/SHOHOKU/Desktop/threekingdoms.txt","r",encoding="utf-8").read()
words = jieba.lcut(f)
ls = []
for i in words:
if len(i)==1 or i in excludes:
continue
elif i in ["丞相","孟德"]:
ls.append("曹操")
elif i in ["孔明曰","孔明"]:
ls.append("诸葛亮")
elif i in ["玄德曰","玄德"]:
ls.append("刘备")
elif i in ["关公","云长"]:
ls.append("关羽")
elif i in ["翼德"]:
ls.append("张飞")
elif i in ["子龙"]:
ls.append("赵云")
elif i in ["都督"]:
ls.append("周瑜")
else:
ls.append(i)
return " ".join(ls)
txt = gettext()
w = WordCloud(font_path="STXINGKA.TTF",width=911,height=305,mask=img,max_words=10,background_color="white").generate(txt)
w.to_file("c:/Users/SHOHOKU/Desktop/April.png")