数据分析案例-文本挖掘与中文文本的统计分析

简介: 数据分析案例-文本挖掘与中文文本的统计分析

项目背景介绍

       四大名著,又称四大小说,是汉语文学中不可多得的作品。这四部著作历久不衰,其中的故事、场景,已经深深地影响了国人的思想观念、价值取向。四部著作都有很高的艺术水平,细致的刻画和所蕴含的思想都为历代读者所称道。


       本次将以小说HLM为例,介绍中文文本的统计分析和文本发掘等方面的基本知识。


数据准备

关于小说内容,读者可自行搜集。


项目流程

1.读取小说内容

# 获取HLM文本数据
with open('HLM.txt','r',encoding='utf-8')as f:
    text = f.read()

2.统计词频

import jieba
# 分词并统计词频
def wordFreq(text,topn):
    words = jieba.lcut(text.strip()) # 对文本进行分词操作
    counts = {}
    for word in words:  # 统计每个词出现的频率,存放在字典counts中
        if len(word) == 1:  # 如果该词的长度为1,则跳过,不参与统计。
            continue
        counts[word] = counts.get(word,0) + 1
    items = list(counts.items())
    items.sort(key=lambda x:x[1],reverse=True)  # 按照词频进行排序
    f = open('HLM_词频.txt','w',encoding='utf-8')
    for i in range(topn):  # topn表示要取的词的个数,将频率最高的topn个词及其频率数存放在文件中
        word,count = items[i]
        f.writelines("{}\t{}\n".format(word,count))
    f.close() 
wordFreq(text,20)  # 这里我们提取出频率最高的前20个词



出现频率最高的词语是“宝玉”,出现了3772次,宝玉当仁不让的是《HLM》的一号主角。后面紧跟的高频词是“什么”,“一个”,这些也都是无意义的词语,需要我们进一步去除。


3.去除停用词

这里我们只需要加载停用词库,统计词频的时候加上判断即可。

# 分词并统计词频
def wordFreq(text,topn):
    words = jieba.lcut(text.strip()) # 对文本进行分词操作
    # 加载停用词库
    stopwords = [line.strip() for line in open('停用词库.txt','r',encoding='utf-8').readlines()]
    counts = {}
    for word in words:  # 统计每个词出现的频率,存放在字典counts中
        if len(word) == 1:  # 如果该词的长度为1,则跳过,不参与统计。
            continue
        elif word not in stopwords:  # 如果该词不在停用词列表stopwords中,才参与统计
            counts[word] = counts.get(word,0) + 1
    items = list(counts.items())
    items.sort(key=lambda x:x[1],reverse=True)  # 按照词频进行排序
    f = open('HLM_词频.txt','w',encoding='utf-8')
    for i in range(topn):  # topn表示要取的词的个数,将频率最高的topn个词及其频率数存放在文件中
        word,count = items[i]
        f.writelines("{}\t{}\n".format(word,count))
    f.close() 


观察输出结果,不难发现“凤姐”和“凤姐儿”应该是一个人物,包括后面的“黛玉”和“林黛玉”,“林妹妹”也是一个人,“宝玉”以及“宝二爷”也应该一起统计。


所以我们还需要处理小说中同一人物,不同称呼的情况。对上面的代码进行改进。


# 分词并统计词频
def wordFreq(text,topn):
    words = jieba.lcut(text.strip()) # 对文本进行分词操作
    # 加载停用词库
    stopwords = [line.strip() for line in open('停用词库.txt','r',encoding='utf-8').readlines()]
    counts = {}
    for word in words:  # 统计每个词出现的频率,存放在字典counts中
        if len(word) == 1:  # 如果该词的长度为1,则跳过,不参与统计。
            continue
        elif word not in stopwords:  # 如果该词不在停用词列表stopwords中,才参与统计
            if word == '凤姐儿':
                word = '凤姐'
            elif word == '林黛玉' or word == '林妹妹':
                word = '黛玉'
            elif word == '二爷':
                word = '宝玉'
            counts[word] = counts.get(word,0) + 1
    items = list(counts.items())
    items.sort(key=lambda x:x[1],reverse=True)  # 按照词频进行排序
    f = open('HLM_词频.txt','w',encoding='utf-8')
    for i in range(topn):  # topn表示要取的词的个数,将频率最高的topn个词及其频率数存放在文件中
        word,count = items[i]
        f.writelines("{}\t{}\n".format(word,count))
    f.close() 


4.绘制词云图

这里我们调用获取词频函数,获取TOP500,最后绘制词云图


# 绘制词云
import matplotlib.pyplot as plt
import wordcloud
import imageio
wordFreq(text,500)  # 获取TOP500的词频
word_cloud_text = open('HLM_词频.txt','r',encoding='utf-8').read()
bg_pic = imageio.imread('star.jpg') # 读入形状图片
wc = wordcloud.WordCloud(font_path=r'C:\Windows\Fonts\simhei.ttf',
                            background_color='white',
                            width=1000,
                            max_words=200,
                            mask=bg_pic,  # mask参数设置词云形状
                            height=860,
                            margin=2
                            ).generate(word_cloud_text)
wc.to_file('HLMcloud_star.png')  # 保存图片



5.章回处理

导入Python中的正则表达式(re模块),通过正则匹配,找到文中所有“第**回”形式的字符。“\u4e00”和“\u9fa5”是unicode编码,并且正好是中文编码的开始和结束的两个值,所以这个正则表达式可以用来判断字符串中是否包含中文。

# 章回处理
import re
chapter = re.findall('第[\u4e00-\u9fa5]+回',text)
lst_chapter = []
for x in chapter:  # 去除重复的章节
    if x not in lst_chapter and len(x)<=5:
        lst_chapter.append(x)
print(lst_chapter)


获取每一回起始和结束数据

lst_start_chapterIndex = []
for x in lst_chapter:  # 找出每一回在原文中的起始位置
    lst_start_chapterIndex.append(text.index(x))
lst_end_chapterIndex = lst_start_chapterIndex[1:]+[len(text)]  # 找出每一回在原文中的结束位置,本回的结束位置就是下一回的起始位置。最后一回的结束位置就是全文的结束。zip将每一回的起始和结束位置拼成一个元组,存放在lst_chapterindex列表中。
lst_chapterIndex = list(zip(lst_start_chapterIndex,lst_end_chapterIndex))
print(lst_chapterIndex)

5.1HLM之“刘姥姥三进荣国府”


统计在每一回中刘姥姥的出场次数并用折线图可视化。


# 统计刘姥姥出现的次数
cnt_liulaolao = []
for i in range(120):
    start = lst_chapterIndex[i][0]
    end = lst_chapterIndex[i][1]
    cnt_liulaolao.append(text[start:end].count('刘姥姥'))
plt.rcParams['font.sans-serif'] = ['SimHei'] #解决中文显示
plt.rcParams['axes.unicode_minus'] = False   #解决符号无法显示
plt.figure(figsize=(15,6))
plt.plot(range(120),cnt_liulaolao,label='刘姥姥出场次数')
plt.title('《HLM》——刘姥姥三进荣国府',fontdict={'fontsize':14})
plt.xlabel('章节数',fontdict={'fontsize':14})
plt.ylabel('出现次数',fontdict={'fontsize':14})
plt.legend()
plt.show()



5.2HLM之“哭说笑闹总关情”  


       我们尝试分析一下在小说的每一回里,“笑”和“喜”,“哭”与“悲”分别出现的次数,能不能通过这样一个小小的切入点,看一看贾府的兴衰与荣辱变化。统计每一回中“笑”和“喜”,“哭”与“悲”的出现次数。 并用折线图可视化。


# 统计情绪次数
cnt_laugh = []
cnt_cry = []
for i in range(120):
    start = lst_chapterIndex[i][0]
    end = lst_chapterIndex[i][1]
    cnt_laugh.append(text[start:end].count('笑')+text[start:end].count('喜'))
    cnt_cry.append(text[start:end].count('哭')+text[start:end].count('悲'))
# 可视化
plt.figure(figsize=(15,6))
plt.plot(range(120),cnt_laugh,label='笑和喜')
plt.plot(range(120),cnt_cry,label='哭和悲')
plt.title('《HLM》120回悲喜变化图',fontdict={'fontsize':14})
plt.xlabel('章节数',fontdict={'fontsize':14})
plt.ylabel('出现次数',fontdict={'fontsize':14})
plt.legend()
plt.show()


5.3HLM之平均段落数与字数


统计每一章段落数和字数并可视化。

cnt_chap = []  # 存放每一回的段落数
cnt_word = []  # 存放每一回的字符总数
for i in range(120):
    start = lst_chapterIndex[i][0]
    end = lst_chapterIndex[i][1]
    cnt_chap.append(text[start:end].count('\n'))
    cnt_word.append(len(text[start:end]))
# 可视化
plt.figure(figsize=(15,8))
plt.scatter(cnt_chap,cnt_word)
for i in range(120):
    plt.text(cnt_chap[i]-2,cnt_word[i]+100,lst_chapter[i],size=7)
plt.xlabel('章节段数',fontdict={'fontsize':14})
plt.ylabel('章节字数',fontdict={'fontsize':14})
plt.title('《HLM》120回',fontdict={'fontsize':14})
plt.show()



5.4HLM之人物社交关系网络


提取小说中的人名


分析人物关系,首先我们需要将书中涉及到的人物姓名提取出来。我们利用在之前生成的词频文件,抽取排名靠前的二十四个人物,研究他们之间的社交关系情况。简便起见,将这些人物名称存放在列表Names中。

Names=['宝玉','凤姐','贾母','黛玉','王夫人','老太太','袭人','贾琏','平儿','宝钗','薛姨妈','探春','鸳鸯','贾政','晴雯','湘云','刘姥姥','邢夫人','贾珍','紫鹃','香菱','尤氏','薛蟠','贾赦']

基于共现关系获取人物关系权重


一般我们认为,在一篇文章中的同一段出现的两个人物之间,一定具有某种关联,因此我们将每一段中的人物角色抽取出来,然后以段落为单位,统计两个角色同时出现的共现次数,并把结果存在一个二维矩阵之中。

Names=['宝玉','凤姐','贾母','黛玉','王夫人','老太太','袭人','贾琏','平儿','宝钗','薛姨妈','探春','鸳鸯','贾政','晴雯','湘云','刘姥姥','邢夫人','贾珍','紫鹃','香菱','尤氏','薛蟠','贾赦']
relations = {}
lst_para = text.split('\n')  # 按段落划分,假设在同一段落中出现的人物具有共现关系
for t in lst_para:
    for name1 in Names:
        if name1 in t:
            for name2 in Names:
                if name2 in t and name1 != name2 and (name2,name1) not in relations:
                    relations[(name1,name2)] = relations.get((name1,name2),0)+1
print(relations.items())


接下来对权重值做归一化操作,找到共现次数的最大值,然后将所有的共现次数映射到0-1之间。

maxRela = max([v for k,v in relations.items()])
relations = {k:v/maxRela for k,v in relations.items()}
print(relations)

绘制人物社交网络图


使用networkx库,绘制人物社交网络图。


import networkx as nx
plt.figure(figsize=(12,12))
G= nx.Graph()
# 根据relations的数据向G中添加边
for k,v in relations.items():
    G.add_edge(k[0],k[1],weight=v)
elarge = [(u,v)for (u,v,d) in G.edges(data=True) if d['weight']>0.6]
emidle = [(u,v)for (u,v,d) in G.edges(data=True) if (d['weight']>0.3)&(d['weight']<=0.6)]
esmall = [(u,v)for (u,v,d) in G.edges(data=True) if d['weight']<=0.3]
# 设置图形布局
pos = nx.spring_layout(G)
# 设置节点样式
nx.draw_networkx_nodes(G,pos,alpha=0.8,node_size=800)
nx.draw_networkx_edges(G,pos,edgelist=elarge,width=2.5,alpha=0.9,edge_color='g')
nx.draw_networkx_edges(G,pos,edgelist=emidle,width=1.5,alpha=0.6,edge_color='r')
nx.draw_networkx_edges(G,pos,edgelist=esmall,width=1,alpha=0.4,edge_color='b',style='dashed')
nx.draw_networkx_labels(G,pos,font_size=12)
plt.axis('off')
plt.title('《HLM》主要人物社交关系网络图')
plt.show()


emmm,这个风格好像有点不太好看,我们换个风格。


plt.figure(figsize=(8,6))
pos = nx.circular_layout(G)
# 设置节点样式
nx.draw_networkx_nodes(G,pos,alpha=0.6,node_size=800)
nx.draw_networkx_edges(G,pos,edgelist=elarge,width=2.5,alpha=0.9,edge_color='g')
nx.draw_networkx_edges(G,pos,edgelist=emidle,width=1.5,alpha=0.6,edge_color='r')
nx.draw_networkx_edges(G,pos,edgelist=esmall,width=1,alpha=0.2,edge_color='b',style='dashed')
nx.draw_networkx_labels(G,pos,font_size=12)
plt.axis('off')
plt.title('《HLM》主要人物社交关系网络图')
plt.show()

绿色权值最大,代表关系越深;红色权值处于中间,关系一般;蓝色权值最低,关系较弱。


从关系图中,我们可以看出贾宝玉社交关系又多又深,不愧是小说中的主角,社交广泛。


好了,以上就是本次分享的项目案例,如果对读者有所帮助,那就留下你的三连吧。



目录
相关文章
|
2月前
|
数据采集 数据可视化 数据挖掘
数据分析案例-汽车客户信息数据可视化分析
数据分析案例-汽车客户信息数据可视化分析
100 0
|
2月前
|
数据可视化 架构师 数据挖掘
数据分析案例-数据科学相关岗位薪资可视化分析
数据分析案例-数据科学相关岗位薪资可视化分析
53 0
|
2月前
|
数据采集 数据可视化 数据挖掘
数据分析案例-BI工程师招聘岗位信息可视化分析
数据分析案例-BI工程师招聘岗位信息可视化分析
59 0
|
4月前
|
存储 算法 数据挖掘
带你读《Apache Doris 案例集》——06 Apache Doris 助力中国联通万亿日志数据分析提速10倍(2)
带你读《Apache Doris 案例集》——06 Apache Doris 助力中国联通万亿日志数据分析提速10倍(2)
102 1
|
4月前
|
SQL 分布式计算 数据可视化
Spark SQL案例【电商购买数据分析】
Spark SQL案例【电商购买数据分析】
|
4月前
|
存储 安全 数据挖掘
带你读《Apache Doris 案例集》——06 Apache Doris 助力中国联通万亿日志数据分析提速10倍(1)
带你读《Apache Doris 案例集》——06 Apache Doris 助力中国联通万亿日志数据分析提速10倍(1)
|
2月前
|
SQL 分布式计算 数据可视化
数据分析案例-数据分析师岗位招聘信息可视化
数据分析案例-数据分析师岗位招聘信息可视化
59 0
|
2月前
|
数据可视化 搜索推荐 数据挖掘
数据分析案例-顾客购物数据可视化分析
数据分析案例-顾客购物数据可视化分析
99 0
|
8天前
|
数据采集 数据可视化 数据挖掘
Seaborn在数据分析中的应用:案例分析与实践
【4月更文挑战第17天】本文介绍了Seaborn在数据分析中的应用,它是一个基于Python的可视化库,简化了复杂数据的图表创建。通过一个销售数据分析的案例,展示了数据加载、描述性统计、相关性分析、多变量分析及高级可视化步骤。实践技巧包括数据清洗、图表选择、颜色使用、注释标签和交互性。Seaborn助力高效数据探索和理解,提升分析效率。注意,实际使用需根据数据集和目标调整,并参考最新文档。
|
4月前
|
SQL 分布式计算 数据挖掘
Spark_Day07:Spark SQL(DataFrame是什么和数据分析(案例讲解))
Spark_Day07:Spark SQL(DataFrame是什么和数据分析(案例讲解))
77 0