自然语言处理《活着》

程序实现的功能

根据自然语言处理基本内容,请选择一本喜欢的小说,对该小说进行分析。分析该小说的分词,词频,词性,词云,小说人物出场次数排序,小说中食物排序(这个得有,我喜欢吃),小说人物关系等等。


前言

自然语言处理小说文本,各个功能以函数形式实现,并在主程序中调用。比如分词功能,词频词性统计保存txt功能,统计人名_词频并保存txt功能,读取txt文件生成柱状图可视化功能,分词词频词性可视化功能,关系图可视化功能,pyecharts词云可视化功能,建立人工手动自定义词典功能(程序中有写,但最后没有使用,去掉注释后是可以使用的),jieba绘制词云_人名可视化并保存png图片功能。


一、设计思想

1、jieba简介:

        jieba是基于Python的中文分词工具,通过安装jieba第三方库,并使用jieba,从而实现对中文分词的处理。并且jieba安装非常方便,上手相对比较轻松,速度也比较快。它还具有支持三种分词模式、支持繁体分词、支持自定义词典和MIT授权协议的优点。

2、功能函数简介:

# 停词文档,排除停用词
def stopwordslist(filepath):

# 生成词频词性文件
def getWordTimes1():

# 分词生成人物词频
def wordFreq1(filepath, topn1):

# 绘制分词的词频词性可视化图
def create_wordproperties():

# 分词生成人物词频并写入文档
def wordFreq2(filepath, topn):

# 可视化人物出场次数方法
def creat_people_view():

# 生成《活着》中主要人物的词云
def creat_wordcloud():

# 使用pyecharts 的方法生成词云
def creat_wordcloud_pyecharts():

# 生成人物关系图
def creat_relationship():

二、准备工作

1.引入库

代码如下(示例):

import jieba
import re
# 导入random包
import random
import codecs
import networkx as nx
# 获取分词
import jieba.posseg as pesg
from pyecharts import options as opts
# 获取柱状图
from pyecharts.charts import Bar
# 获取词云图
from pyecharts.charts import WordCloud
# 词云第三方库导入
import numpy as np
import wordcloud
from PIL import Image
import  matplotlib.pyplot as plt

2.前期准备

2.1准备小说原文、用户字典、停用词的txt文档以及词云用到的图片

 

该处使用的huo_zhe.txt文件为小说《活着》原文,可自行网上查找下载并保存与txt文档中。

词云背景图也自行在网上查找自己心仪且和文章有关的图片并下载即可。

注意:文档编码应为UTF-8,否则编译会报错。

三、源码

import jieba  # 引用用户词典中使用
import re
# 导入random包
import random
import codecs
# 导入networkx包
import networkx as nx

import jieba.posseg as pseg  # 获取词性
from pyecharts import options as opts
# 获取柱状图
from pyecharts.charts import Bar
# 获取词云图
from pyecharts.charts import WordCloud

# 词云第三方库导入
import numpy as np
import wordcloud
from PIL import Image
import matplotlib.pyplot as plt
# from wordcloud import WordCloud

# 设置主要人物的个数
visualTop = 10  # 可视化人物图人数
mainTop = 20  # 人物词云图人物数
peopleTop = 5  # 人物关系图

# jieba.load_userdict("text/huozhe_dict.txt")  # 《活着》中主要人物的用户词典

# 获取《活着》小说原文文本,并读取文件
fn = open('text/huo_zhe.txt', encoding="utf-8")
string_data = fn.read()  # 读出整个文件
fn.close()  # 关闭文件

# 文本预处理
pattern = re.compile(u'\t|\n|\.|-|:|;|\)|\(|\?|"')  # 定义正则表达式匹配模式
txt = re.sub(pattern, '', string_data)  # 将符合模式的字符去除
print('预处理完毕')

# 停词文档
def stopwordslist(filepath):
    stopwords = [line.strip() for line in open(filepath, 'r', encoding='utf-8').readlines()]
    return stopwords
stopwords = stopwordslist('text/tingyong.txt')

# 去除词性为nr,但不是人名的词
excludes = {'明白', '塞满', '道德', '阳光', '田野', '田埂', '毛巾', '张大嘴巴', '支支吾吾', '农忙', '殷勤', '牛耕田', '和尚', '牛正',
            '布满', '光耀', '祖宗', '鬼混', '松花蛋', '岳父', '红卫兵', '常和龙', '乌龟王八', '齐齐', '小山坡', '天亮', '千秋', '张嘴',
            '房地契', '叶子', '小脸蛋', '老祖宗', '巴掌', '胡来', '格格笑', '白发', '刚刚开始', '金黄', '小姐', '郎中', '黄衣服', '小石子',
            '胡同', '连里', '哈哈笑', '包围住', '农忙', '张望', '越来越近', '段时间', '天太黑', '冷风吹', '天亮', '老太婆', '黑洞洞', '向南走',
            '白馒头', '折回来', '柳枝', '胡同', '红红', '格格直', '雨一淋', '霍霍', '空旷', '那阵子', '宰羊', '花布', '雷公', '胡闹',
            '戴绿', '太师椅', '小家伙', '蒙蒙亮', '羊全', '木盖', '红旗', '原以为', '太久', '西北风', '宁静', '姜汤', '蓝布', '天刚亮',
            '任凭', '房契', '子里', '张大', '柳树', '红颜色', '拉进来', '安静', '牛羊', '那木盖', '小红旗', '立大功', '红布', '金山',
            '广福桥', '青青', '天气晴朗', '羊少', '山珍海味', '米香', '米汤', '冰冷冷', '淌出', '冷清', '柴禾', '觉察到', '钱塞', '敬烟',
            '东张西望', '麻利', '来凤', '都还没', '钱花', '冷冰冰', '红袖章', '张得', '白白'
            }

# 通过键值对的形式存储词语及其出现的次数
counts1 = {}   # 存放词性词频
counts2 = {}  # 存放人物词频

# 生成词频词性文件
def getWordTimes1():
    cutArticle = pseg.cut(txt)

    for w in cutArticle:
        if w.word in stopwords or w.word == None:
            continue
        else:
            real_word = w.word+'--'+w.flag   # 输出形式为:词语--词性
        counts1[real_word] = counts1.get(real_word, 0) + 1

getWordTimes1()  # 调用生成词频词性方法

items1 = list(counts1.items())
# 进行降序排列 根据词语出现的次数进行从大到小排序
items1.sort(key=lambda x: x[1], reverse=True)

# 导出数据
# 分词生成人物词频
def wordFreq1(filepath, topn1):
    with codecs.open(filepath, "w", "utf-8") as f:
        for i in range(topn1):
            word, count = items1[i]
            f.write("{}:{}\n".format(word, count))

# 生成词频文件
wordFreq1("result/《活着》分词词频词性.txt", 200)

# 将txt文本里的数据转换为字典形式
fr1 = open('result/《活着》分词词频词性.txt', 'r', encoding='utf-8')
dic1 = {}
keys1 = []  # 用来存储读取的顺序
for line in fr1:
    # 去空白,并用split()方法返回列表
    v1 = line.strip().split(':')
    # print("v",v)   # v ['家珍', '574']
    # 拼接字典 {'家珍', '574'}
    dic1[v1[0]] = v1[1]
    keys1.append(v1[0])
fr1.close()

list_name1 = list(dic1.keys())  # 小说中人物名称
list_name_times1 = list(dic1.values())  # 提取字典里的数据作为绘图数据

# 绘制分词的词频词性可视化图
def  create_wordproperties():
    bar1 = Bar()
    bar1.add_xaxis(list_name1[0:visualTop])  # x轴表示人物名称
    bar1.add_yaxis("词语出现次数", list_name_times1)   # y轴表示人物名称出现的次数
    bar1.set_global_opts(title_opts=opts.TitleOpts(title="《活着》分词词频词性可视化图", subtitle="分词词频词性top10"),
                        xaxis_opts=opts.AxisOpts(axislabel_opts={"rotate": 45}))
    bar1.set_series_opts(label_opts=opts.LabelOpts(position="top"))

    # 生成 html 文件
    bar1.render("result/《活着》分词词频词性可视化图.html")

# 得到分词和出现次数
def getWordTimes2():
    # 分词,返回词性
    poss = pseg.cut(txt)
    for w in poss:
        if w.flag != 'nr' or len(w.word) < 2 or w.word in excludes:
            continue  # 去除单个字的词语,(即当分词长度小于2)或该词词性不为nr(人名)时认为该词不为人名
        # 统一人名称呼
        elif w.word == '福贵' or w.word == '老人' or w.word == '福贵呵' or w.word == '和福贵':
            real_word = '福贵'
        elif w.word == '家珍' or w.word == '少奶奶' or w.word == '千金':
            real_word = '家珍'
        elif w.word == '和凤霞' or w.word == '姐姐' or w.word == '向凤霞' or w.word == '比凤霞' or w.word == '问凤霞' or w.word == '拉凤霞' \
            or w.word == '凤霞正' or w.word == '凤霞汗' or w.word == '凤霞累' or w.word == '连凤霞' or w.word == '时凤霞' or w.word == '凤霞生':
            real_word = '凤霞'
        elif w.word == '徐有庆' or w.word == '儿子' or w.word == '有庆拉' or w.word == '有庆总'or w.word == '有庆正'or w.word == '比有庆'\
            or w.word == '有庆刚' or w.word == '比有庆':
            real_word = '有庆'
        elif w.word == '和春生':
            real_word = '春生'
        elif w.word == '和二喜' or w.word == '黑二喜':
            real_word = '二喜'
        elif w.word == '龙二听' or w.word == '龙二那' or w.word == '龙二玩' or w.word == '龙二死' or w.word == '龙二后':
            real_word = '龙二'
        elif w.word == '王喜后':
            real_word = '王喜'
        elif w.word == '孙子':
            real_word = '苦根'
        else:
            real_word = w.word
        counts2[real_word] = counts2.get(real_word, 0) + 1


getWordTimes2()
items2 = list(counts2.items())
# 进行降序排列 根据词语出现的次数进行从大到小排序
items2.sort(key=lambda x: x[1], reverse=True)


# 导出数据
# 分词生成人物词频(写入文档)
def wordFreq2(filepath, topn):
    with codecs.open(filepath, "w", "utf-8") as f:
        for i in range(topn):
            word, count = items2[i]
            f.write("{}:{}\n".format(word, count))

# 生成词频文件
wordFreq2("result/《活着》人名_词频.txt", 20)  # 获取词频数排列前20的人物名称及其对应词频

# 将txt文本里的数据转换为字典形式
fr = open('result/《活着》人名_词频.txt', 'r', encoding='utf-8')
dic = {}
keys = []  # 用来存储读取的顺序
for line in fr:
    # 去空白,并用split()方法返回列表

    v = line.strip().split(':')
    # print("v",v)   # v ['家珍', '574']
    # 拼接字典 {'家珍', '574'}
    dic[v[0]] = v[1]
    keys.append(v[0])  # 向列表末尾添加元素
fr.close()
# 输出前几个的键值对
print("人物出现次数TOP", mainTop)
print(list(dic.items())[:mainTop])

# 绘制人物出场次数图
# 人名列表 (用于人物关系图,pyecharts人物出场次数图)
list_name = list(dic.keys())  # 人名
list_name_times = list(dic.values())  # 提取字典里的数据作为绘图数据

# 可视化人物出场次数方法
def creat_people_view():
    # 使用Bar构建柱状图
    bar = Bar()
    # 添加x的数据
    bar.add_xaxis(list_name[0:visualTop])
    # 添加y轴数据
    bar.add_yaxis("人物出场次数", list_name_times)
    bar.set_global_opts(title_opts=opts.TitleOpts(title="《活着》人物出场次数可视化图", subtitle="《活着》人物出场次数TOP10"),
                        xaxis_opts=opts.AxisOpts(axislabel_opts={"rotate": 45}))
    bar.set_series_opts(label_opts=opts.LabelOpts(position="top"))  # 使标签位于上方

    # 生成相应的 html 文件
    bar.render("result/《活着》人物出场次数可视化图.html")


# 生成《活着》中主要人物的词云
def creat_wordcloud():
    path_txt = 'result/《活着》人名_词频.txt'
    path_img = "text/life-bar.png"
    f = open(path_txt, 'r', encoding='UTF-8').read()
    background_image = np.array(Image.open(path_img))
    # 结巴分词,生成字符串,如果不通过分词,无法直接生成正确的中文词云,感兴趣的朋友可以去查一下,有多种分词模式
    # Python join() 方法用于将序列中的元素以指定的字符连接生成一个新的字符串。
    cut_text = " ".join(jieba.cut(f))
    cut_text_list = list(cut_text.split())  # 修改1,用split分隔,才能变成列表
    # print(cut_text_list)

    # 用于全文词云绘制时可以使用
    # exclude=['和', '的', '是', '也', ',', ':', "。", "为"]
    # for word in cut_text_list:  # 修改 2,外层循环活着词条列表,内层判定该词条是否出现在禁忌列表中,如果出现,则删除该词条
    #    if word in exclude:
    #      cut_text_list.remove(word)
    # print(cut_text_list)
    cut_text = " ".join(cut_text_list)
    wc = wordcloud.WordCloud(
            # 设置字体,不然会出现口字乱码,文字的路径是电脑的字体一般路径,可以换成别的
            font_path="C:/Windows/Fonts/simfang.ttf",
            background_color="white",
            max_words=300,  # 设置最大显示的字数
            max_font_size=80,  # 设置字体最大值
            random_state=50,  # 设置有多少种随机生成状态,即有多少种配色方案
            # mask参数=图片背景,必须要写上,另外有mask参数再设定宽高是无效的
            mask=background_image).generate(cut_text)

    # 生成颜色值(若此部分不注释,词云的字体颜色将会是背景图片的颜色)
    # image_colors = ImageColorGenerator(background_image)
    # plt.imshow(wordcloud.recolor(color_func=image_colors))

    # 下面代码表示显示图片 (无上面生成颜色值代码,词云的字体颜色是随机的)
    # 保存图片
    wc.to_file('result/《活着》词云_人名.png')
    plt.figure("wc")
    plt.imshow(wc)
    plt.axis("off")
    plt.show()

# 使用pyecharts 的方法生成词云
def creat_wordcloud_pyecharts():
    wordsAndTimes = list(dic.items())
    (
        WordCloud()
            .add(series_name="人物次数", data_pair=wordsAndTimes,
                 word_size_range=[20, 100], textstyle_opts=opts.TextStyleOpts(font_family="cursive"), )
            .set_global_opts(title_opts=opts.TitleOpts(title="《活着》词云"))
            .render("result/《活着》词云_人名.html")
    )

# 颜色生成
colorNum = len(list_name[0:peopleTop])

# print('颜色数',colorNum)
def randomcolor():
    colorArr = ['1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F']
    color = ""
    for i in range(6):
        color += colorArr[random.randint(0, 14)]
    return "#" + color

def color_list():
    colorList = []
    for i in range(colorNum):
        colorList.append(randomcolor())
    return colorList

# 解决中文乱码
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签


# 生成人物关系图
def creat_relationship():
    # 人物节点颜色
    colors = color_list()
    Names = list_name[0:peopleTop]
    relations = {}
    # 按段落划分,假设在同一段落中出现的人物具有共现关系
    lst_para = (txt).split('\n')  # lst_para是每一段
    for text in lst_para:
        for name_0 in Names:
            if name_0 in text:
                for name_1 in Names:
                    if name_1 in text and name_0 != name_1 and (name_1, name_0) not in relations:
                        relations[(name_0, name_1)] = relations.get((name_0, name_1), 0) + 1
    maxRela = max([v for k, v in relations.items()])
    relations = {k: v / maxRela for k, v in relations.items()}
    # return relations

    plt.figure(figsize=(15, 15))
    # 创建无多重边无向图
    G = nx.Graph()
    for k, v in relations.items():
        G.add_edge(k[0], k[1], weight=v)
    # 筛选权重大于0.6的边
    elarge = [(u, v) for (u, v, d) in G.edges(data=True) if d['weight'] > 0.6]
    # 筛选权重大于0.3小于0.6的边
    emidle = [(u, v) for (u, v, d) in G.edges(data=True) if (d['weight'] > 0.3) & (d['weight'] <= 0.6)]
    # 筛选权重小于0.3的边
    esmall = [(u, v) for (u, v, d) in G.edges(data=True) if d['weight'] <= 0.3]
    # 设置图形布局
    pos = nx.spring_layout(G)  # 用Fruchterman-Reingold算法排列节点(样子类似多中心放射状)
    # 设置节点样式
    nx.draw_networkx_nodes(G, pos, alpha=0.8, node_size=1300, node_color=colors)
    # 设置大于0.6的边的样式
    nx.draw_networkx_edges(G, pos, edgelist=elarge, width=2.5, alpha=0.9, edge_color='g')
    # 0.3~0.6
    nx.draw_networkx_edges(G, pos, edgelist=emidle, width=1.5, alpha=0.6, edge_color='y')
    # <0.3
    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=14)

    plt.title("《活着》主要人物关系图")
    # 关闭坐标轴
    plt.axis('off')

    # 保存图表
    plt.savefig('result/《活着》主要人物关系图.png', bbox_inches='tight')
    plt.show()

def main():

    # 生成词频词性文件
    create_wordproperties()
    # 人物出场次数可视化图
    creat_people_view()

    # 词云图
    creat_wordcloud()
    creat_wordcloud_pyecharts()
    # 人物关系图
    creat_relationship()


if __name__ == '__main__':
    main()

四、运行结果

1.结果列表

 

2、《活着》分词词频词性.txt

3、《活着》分词词频词性可视化图(排名前十)

 

4、《活着》词云_人名.png

5、《活着》人物出场次数可视化图

6、《活着》主要人物关系图 

 


总结

以上就是用python进行自然语言处理文本的全部内容,希望和大家一起进步和成长。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值