定义,匹配如下形式的{话语},认为是会“话”内容,其他认为非会话内容:
[冒号] [左双引号] {话语}[右双引号]
先看看结果:
1.整本书会话内容与非会话内容对比:
2. 按说话的句数(每个配对的双引号算一句)统计Top10:
3. 按说话的总字数统计Top10
基本实现思路如下:
1. 下载纯文本的《红楼梦》文件,作简单的数据清洗
将西文的冒号、双引号替换为中文的冒号、双引号;
2. 提取话语,将符合话语规则的文本提取出到一个List
3.调用NPL工具包,逐句分析包含话语的上下文,找到说话的“主语”,
让主语认领每句话语。
经过比较,选型了FNLP,教程链接附上:https://github.com/xpqiu/fnlp/wiki/QuickTutorial
使用过程中,发现语义分析结果与Demo有出入,第一个标点常常被定性为词语,不明白为啥。
经过人工检验结果,发现很多人名不能正确识别,官方没有较细致的文档。
只查到可以加载自定义字典,我定义了一些人物名到 data/dict_name.txt下,并调用API加载。
贾妃 人名 丰儿 人名 尤氏 人名 贾赦 人名 金荣 人名 吴良 人名 柳氏 人名 贾芹 人名 贾珍 人名 金桂 人名 麝月 人名 晴雯 人名 元春 人名 惜春 人名 迎春 人名 探春 人名 尤二姐 人名 尤三姐 人名 紫鹃 人名 秋纹 人名 袭人 人名 空空道人 人名 士隐 人名 薛蟠 人名 林之孝 人名 女尼 人名 有人 人名 妙玉 人名 何三 人名 鸳鸯 人名 李纹 人名 湘云 人名 长史 人名 薛蝌 人名 北静王 人名 西平王 人名 王爷 人名 赵堂官 人名 衙役 人名 倪二 人名 倪家母女 人名 雨村 人名 道士 人名 巧姐 人名 巧姐儿 人名 凤姐 人名 凤姐儿 人名 秦氏 人名 贾瑞 人名 宝玉 人名 黛玉 人名 李嬷嬷 人名 薛姨妈 人名 雪雁 人名 宝钗 人名 贾蓉 人名 李纨 人名 秦钟 人名 贾母 人名 贾琏 人名 贾兰 人名 贾芸 人名 贾环 人名 平儿 人名 邢夫人 人名 王夫人 人名 众人 人名 贾蔷 人名 丫头 人名 贾政 人名 和尚 人名 程日兴 人名 王仁 人名 婆子 人名 道
4.别名问题
贾妃 元春 贾元春 凤姐 凤姐儿 巧姐 巧姐儿
自己处理了别名映射,计数时将别名下的计数做了累加,定义在 data/map_same.txt中。
5. 工具类
package org.hl; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.fnlp.nlp.cn.CNFactory; import org.fnlp.nlp.parser.dep.DependencyTree; import org.fnlp.util.exception.LoadModelException; public class ChatParser { String fpath; String dict; String dict_same; String charSet; String str_all; int len_total; int len_chat; int len_sentence; List<String> ls_embrace; List<String> ls_sentence; List<String> ls_who; ArrayList<Entry<String,Integer>> who_said_sentence; ArrayList<Entry<String,Integer>> who_said_word; ArrayList<Entry<String,Integer>> who_said_per; CNFactory fm=null; HashMap<String,Integer> m_cout_sentence; HashMap<String,Integer> m_cout_word; HashMap<String,Integer> m_cout_per; HashMap<String,String> m_same=null; ChatParser(String fpath,String charSet,String dict,String fsame) throws Exception{ InputStreamReader insReader = new InputStreamReader( new FileInputStream(fpath), charSet); this.dict = dict; this.charSet=charSet; BufferedReader br = new BufferedReader(insReader); String line = new String(); StringBuffer sbuf = new StringBuffer(); while ((line = br.readLine()) != null) { sbuf.append(line); System.out.println(line); } br.close(); str_all=sbuf.toString(); len_total = str_all.length(); ls_embrace =new ArrayList<String>(); ls_sentence =new ArrayList<String>(); ls_who =new ArrayList<String>(); loadSame(fsame); } public String getSameKey(String key){ if(m_same==null) return key; String mk = m_same.get(key); if(mk==null) return key; else return mk; } public void loadSame(String fpath) throws Exception{ if(fpath==null) return; m_same = new HashMap<String,String>(); InputStreamReader insReader = new InputStreamReader( new FileInputStream(fpath), charSet); BufferedReader br = new BufferedReader(insReader); String line = new String(); while ((line = br.readLine()) != null) { String[] words = line.split("\\s+"); for(int i=0,len=words.length; i<len; i++){ m_same.put(words[i], line); } } br.close(); } public void wash(){ StringBuffer sbuf = new StringBuffer(); StringBuffer bf = null; len_chat=0; len_sentence=0; int pos_start=0; for(int i=0,len=str_all.length(); i<len;i++){ char ch = str_all.charAt(i); if(ch==':'|| ch==':'){ pos_start=i; }else if((ch=='“' || ch=='\"') && bf==null){ //排除非人言的双引号 if(i-pos_start>3) continue; bf = new StringBuffer(); sbuf.append('“'); }else if ((ch=='”' || ch=='\"')&& bf!=null){ String str_bf = bf.toString(); len_chat +=str_bf.length(); ls_sentence.add(str_bf); bf = null; sbuf.append('”'); ls_embrace.add(sbuf.toString()); sbuf = new StringBuffer(); }else{ if(bf!=null) bf.append(ch); else sbuf.append(ch); } } len_sentence=ls_sentence.size(); } public void parse() throws Exception{ long tm_start = new Date().getTime(); CNFactory fm = CNFactory.getInstance("models"); if(this.dict!=null) fm.loadDict(dict); String who=null; HashMap<String,Integer> mc_sentence = new HashMap<String,Integer>(); HashMap<String,Integer> mc_char = new HashMap<String,Integer>(); for(int k=0,klen=ls_embrace.size();k<klen; k++){ String str_input=ls_embrace.get(k); String[][] wc = fm.tag(str_input); DependencyTree dt =fm.parse2T(wc[0], wc[1]); //System.out.println(str_input); //System.out.println(dt); ArrayList<List<String>> ls = dt.toList(); for(int i=0,len=ls.size();i<len; i++ ){ List<String> cur = ls.get(i); if(//cur.get(3).equals("主语") && cur.get(1).equals("人名")){ who = cur.get(0); } } ls_who.add(who); System.out.println(who+":"+ls_sentence.get(k)); } long tm_span = new Date().getTime()-tm_start; System.out.println("-----------------------parse span:"+tm_span+"-----------------------------"); System.out.println(len_chat+"/"+(len_total-len_chat)+"/"+len_total+":"+ls_embrace.size()); } public void count_sort(){ long tm_start = new Date().getTime(); m_cout_sentence = new HashMap<String,Integer>(); m_cout_word = new HashMap<String,Integer>(); m_cout_per = new HashMap<String,Integer>(); for(int k=0,klen=ls_who.size();k<klen; k++){ String who = getSameKey(ls_who.get(k)); String sentence = ls_sentence.get(k); int c1=1; int c2=sentence.length(); if(m_cout_sentence.containsKey(who)){ c1+=m_cout_sentence.get(who); c2+=m_cout_word.get(who); } //System.out.println(who+":"+c1+":"+c2+":"+sentence); m_cout_sentence.put(who, c1); m_cout_word.put(who, c2); } Iterator iter = m_cout_word.entrySet().iterator(); while (iter.hasNext()) { Map.Entry<String,Integer> entry = (Map.Entry) iter.next(); String key = entry.getKey(); int val = entry.getValue()/m_cout_sentence.get(key); m_cout_per.put(key, val); } long tm_span = new Date().getTime()-tm_start; System.out.println("-----------------------count span:"+tm_span+"-----------------------------"); who_said_sentence= new ArrayList<Entry<String,Integer>>(m_cout_sentence.entrySet()); who_said_word= new ArrayList<Entry<String,Integer>>(m_cout_word.entrySet()); who_said_per= new ArrayList<Entry<String,Integer>>(m_cout_per.entrySet()); Collections.sort(who_said_sentence, new Comparator<Map.Entry<String, Integer>>() { public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) { return (o2.getValue() - o1.getValue()); } }); Collections.sort(who_said_word, new Comparator<Map.Entry<String, Integer>>() { public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) { return (o2.getValue() - o1.getValue()); } }); Collections.sort(who_said_per, new Comparator<Map.Entry<String, Integer>>() { public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) { return (o2.getValue() - o1.getValue()); } }); System.out.println("-----------------------top sentence----------------------------"); int TOP =10; int pos=0; for(Entry<String,Integer> e : who_said_sentence) { if(pos<TOP){ System.out.println("{ label:'"+e.getKey() + "',value:" + e.getValue()+"},"); } else{ System.out.println("{ label:'"+e.getKey() + "',value:" + e.getValue()+"}"); break; } pos++; } pos=0; System.out.println("-----------------------top word----------------------------"); for(Entry<String,Integer> e : who_said_word) { if(pos<TOP){ System.out.println("{ label:'"+e.getKey() + "',value:" + e.getValue()+"},"); } else{ System.out.println("{ label:'"+e.getKey() + "',value:" + e.getValue()+"}"); break; } pos++; } System.out.println("-----------------------top per----------------------------"); for(Entry<String,Integer> e : who_said_per) { if(pos<TOP){ System.out.println("{ label:'"+e.getKey() + "',value:" + e.getValue()+"},"); } else{ System.out.println("{ label:'"+e.getKey() + "',value:" + e.getValue()+"}"); break; } pos++; } } public static void main(String[] args) throws Exception { // TODO Auto-generated method stub ChatParser cp = new ChatParser("data/t1.txt", "utf-8","data/dict_name.txt","data/map_same.txt"); cp.wash(); cp.parse(); cp.count_sort(); } }
6. 分析结果的饼状图展示, 选用了d3.js的一个pie组件,非常好用
只需要给出json格式的数据,布局完全委托给该组件了。
<script> var pie = new d3pie("pie", { header: { title: { text: "对话/非对话", fontSize: 30 } }, data: { content: [ { label: "对话", value: 404217 }, { label: "非对话", value: 448962} ] } }); </script>
7. 性能
在IMac下,耗时毫秒数-----------------------parse span:84332-----------------------------
NLP的组件还不够理想,为了减轻其负担,双引号内的内容替换为空,再交给它处理的。
虽然如此,看着成群的对话风卷残云一半滚屏,有趣!
贾蓉:这样的大排场,我打量拿着妖怪给我们瞧瞧到底是些什么东西,那里知道是这样收罗,究竟妖怪拿去了没有? 贾珍:糊涂东西,妖怪原是聚则成形,散则成气,如今多少神将在这里,还敢现形吗!无非把这妖气收了,便不作祟,就是法力了。 贾珍:头里那些响动我也不知道,就是跟着大老爷进园这一日,明明是个大公野鸡飞过去了,拴儿吓离了眼,说得活象.我们都替他圆了个谎,大老爷就认真起来.倒瞧了个很爇闹的坛场。 贾赦:只怕是谣言罢.前儿你二叔带书子来说,探春于某日到了任所,择了某日吉时送了你妹子到了海疆,路上风恬浪静,合家不必挂念.还说节度认亲,倒设席贺喜,那里有做了亲戚倒提参起来的.且不必言语,快到吏部打听明白就来回我。 贾琏:才到吏部打听,果然二叔被参.题本上去,亏得皇上的恩典,没有交部,便下旨意,说是失察属员,重征粮米,苛虐百姓,本应革职,姑念初膺外任,不谙吏治,被属员蒙蔽,着降三级,加恩仍以工部员外上行走,并令即日回京.这信是准的.正在吏部说话的时候,来了一个江西引见知县,说起我们二叔,是很感激的,但说是个好上司,只是用人不当,那些家人在外招摇撞骗,欺凌属员,已经把好名声都弄坏了.节度大人早已知道,也说我们二叔是个好人.不知怎么样这回又参了.想是忒闹得不好,恐将来弄出大祸,所以借了一件失察的事情参的,倒是避重就轻的意思也未可知。 贾琏:先去告诉你婶子知道,且不必告诉老太太就是了。 王夫人:打听准了么?果然这样,老爷也愿意,合家也放心.那外任是何尝做得的!若不是那样的参回来,只怕叫那些混帐东西把老爷的性命都坑了呢!
源码参见附件,FNLP的m文件比较大,自己参考教程的下载链接下。
相关推荐
《中国石油大学(北京)红楼梦研究第一次在线作业》 《红楼梦》是中国古典小说的最高峰,该作品是清代的作品。作者曹雪芹姓曹名霑,字芹圃,号雪芹。《红楼梦》的作者历来有分歧,如下列人物中被人说是本书作者的有...
网页设计制作作业-红楼梦主题 刘亚群@德州学院 本作品欢迎分发和修改,但请保留此说明 在线预览此作品:http://my.micsite.net/Memory/dzu/web/20121230/ 作者网站:http://www.micsite.net/ 作者博客:...
Dream_of_the_Red_Kmeans.py :基于python实现红楼梦聚类分析的主程序 Dream_of_the_Red_Mansion.txt : 红楼梦txt KMeansCluster_Class.py :自己编写的KMeans程序 Red_Mansion_Dictionary.txt : 红楼梦人物名,...
红楼梦数据库,access数据库存储的红楼梦。
《红楼梦》是中国古典文学的巅峰之作,由清代作家曹雪芹创作,后经高鹗续写而成。这部小说以贾、王、史、薛四大家族为背景,细致地描绘了封建社会的各个方面,尤其以贾宝玉和林黛玉的爱情悲剧为主线,展现了一幅生动...
通过python及其jieba三方库,筛选关键词,整合《红楼梦》人物出场排名,也可整合《红楼梦》词汇使用情况,从而分析《红楼梦》的角色戏份及用语习惯。
《红楼梦》是中国古典小说的巅峰之作,由清代作家曹雪芹创作。这部小说以贾宝玉、林黛玉、薛宝钗等人物的生活为主线,描述了封建社会的衰落和贵族家庭的兴衰史。全书共120回,其内容之丰富、人物之鲜活、艺术成就之...
在“机器学习红楼梦Python代码”这个项目中,我们主要探讨的是如何运用机器学习技术来解决一个文学领域的实际问题,即鉴别《红楼梦》后四十回的作者是否为曹雪芹。这个任务涉及到自然语言处理(NLP)、文本特征提取...
红楼梦脂评汇校本 v3.0.exe
《基于社会网络分析的红楼梦人物分析》项目是一个深入探究中国古代文学巨著《红楼梦》中人物关系的研究实践。在这个项目中,主要运用了数据结构、有向图理论,并借助MFC(Microsoft Foundation Classes)库在Visual ...
选用图模型设计与表示《红楼梦》人物关系网,并以文件形式保存相关信息;运用社会网络分析技术与算法对红楼梦人物关系网进行分析,获取有意义的结果,并以图形方式呈现;提供对人物属性与人物关系的查询功能。
《红楼梦》是中国古典文学的一颗璀璨明珠,其作者曹雪芹的创作背景和身份一直备受学术界的关注。在这个数学建模的问题中,我们探讨的是如何通过数学和统计方法来判断《红楼梦》前80回与后40回是否出自同一作者之手。...
《红楼梦》是一部中国古典文学巨著,而"红楼梦中优秀的flash课件"则是将这部经典作品与现代多媒体技术结合的产物。这些课件利用Adobe Flash软件制作,将文本、图像、音频等多种元素融合,为学习者提供了一种生动、...
《红楼梦》是中国古代四大名著之一,由清代作家曹雪芹创作,被誉为中国古代小说的巅峰之作。Imc连环画则是将这部经典文学作品转化为适合手机阅读的电子形式,让读者能够在移动设备上轻松享受阅读的乐趣。 Imc连环画...
通过命名实体识别获取到的《红楼梦》中的所有人名。共120行,代表120章,一行代表一章中出现的所有人名。
在本项目中,我们将利用Python编程语言来统计经典名著《红楼梦》中前20位出场次数最多的人物。首先,我们需要获取《红楼梦》的文本数据,通常这些数据以纯文本格式存储,例如在名为“红楼梦.txt”的文件中。Python...
基于知识图谱的《红楼梦》人物关系可视化及问答系统