寻找《红楼梦》十大话唠-程序员宅基地

技术标签: 饼图  NLP  红楼梦  HTML5  会话  统计  D3.js  

 

定义,匹配如下形式的{话语},认为是会“话”内容,其他认为非会话内容:

[冒号] [左双引号] {话语}[右双引号] 

 

先看看结果:

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文件比较大,自己参考教程的下载链接下。

 
 

 

 



  

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/cat4w/article/details/84689948

智能推荐

Web服务器压力测试工具http_load、webbench、ab、Siege使用教程-程序员宅基地

文章浏览阅读474次。一、http_load程序非常小,解压后也不到100Khttp_load以并行复用的方式运行,用以测试web服务器的吞吐量与负载。但是它不同于大多数压力测试工具,它可以以一个单一的进程运行,一般不会把客户机搞死。还可以测试HTTPS类的网站请求。下载地址:http://soft.vpser.net/test/http_load/http_load-12mar2006.t

端口被占用 更改端口_通用事件接收端口更改-程序员宅基地

文章浏览阅读913次。通过命令指定端口:> 命令后面追加 -Dhttp.port=端口号_通用事件接收端口更改

iOS/Android SQLite 全文检索——FTS (Full Text Search)_sqlite fts 实现原理-程序员宅基地

文章浏览阅读9.8k次,点赞6次,收藏17次。前言我们的APP部分功能为了满足用户离线使用搜索的场景,使用了内置SQLite数据库的方式,随着内容的日益丰富,数据库记录快速增多,导致搜索速度明显变慢,为了提升搜索速度,给我们的数据做了全文检索的支持,在3W+的数据下,搜索速度由原来的数秒提升至几十到几百毫秒(设备不同,搜索效率存在差别)。一、基本概念概述 全文检索是从文本或数据库中,不限定数据字段,自由地搜索出消息的技术。 运行全文检索任_sqlite fts 实现原理

ALSA声卡驱动中的DAPM详解之六:精髓所在,牵一发而动全身-程序员宅基地

文章浏览阅读135次。设计dapm的主要目的之一,就是希望声卡上的各种部件的电源按需分配,需要的就上电,不需要的就下电,使得整个音频系统总是处于最小的耗电状态,最主要的就是,这一切对用户空间的应用程序是透明的,也就是说,用户空间的应用程序无需关心那个部件何时需要电源,它只要按需要设定好音频路径,播放音频数据,暂停或停止,dapm框架会根据音频路径,完美地对各种部件的电源进行控制,而且精确地按某种顺序进行,防止...

repost:ubuntu16.04 耍帅快捷键_怎样用终端耍帅-程序员宅基地

文章浏览阅读202次。ubuntu16.04 耍帅快捷键https://blog.csdn.net/xiaoqu001/article/details/78721772 关于Firfox的:Ctrl + t : 新建标签(Tab)Ctrl + w : 关闭当前标签(Tab)Alt + F4 : 关闭当前窗口(也就是关闭了所有Tab)Ctrl + Shift + w : 关闭当前窗口(与Alt + ..._怎样用终端耍帅

UnityEffects(3)之闪电链_unity 闪电链-程序员宅基地

文章浏览阅读1.6w次,点赞14次,收藏48次。今天来分享一下Untiy中实现闪电链的方法,实际在项目中使用,效果不错。国际惯例:工程放在:https://github.com/aceyan/UnityEffects 使用unity5.4场景是Scene/(3)ChainLightning 使用了unity的lineRender来模拟闪电的效果: 主要功能都在UVChainLightning这个脚_unity 闪电链

随便推点

SLO 落地方案:VALET_google valet模式定义slis指标集; valet : volume、availabili-程序员宅基地

文章浏览阅读1.4k次。1、VALET 定义1.1 容量(流量,Volume)服务可以处理多少业务量?处理的记录数量1.2 可用性(Availability)服务是否在需要时可用?在一定时间内完成工作的频率(百分比)1.3 延迟(Latency)在使用服务时,它是否快速响应?任务运行所需的时间1.4 错误(Errors)在使用服务时,是否会出错?无法处理的记录1.5 工单(Tickets)该服务请求是否需要人工干预才能完成?操作员必须手动修复数据和重新处理任务的次数2、SLO 落_google valet模式定义slis指标集; valet : volume、availability、latency、error

网页宽度自动适应手机屏幕宽度的方法_怎么让width设置为800px在手机上显示完整-程序员宅基地

文章浏览阅读10w+次,点赞10次,收藏34次。在网页的中增加以上这句话,可以让网页的宽度自动适应手机屏幕的宽度。其中:width=device-width :表示宽度是设备屏幕的宽度initial-scale=1.0:表示初始的缩放比例minimum-scale=0.5:表示最小的缩放比例maximum-scale=2.0:表示最大的缩放比例user-scalable=yes:表示用户是否可以调整缩放比例 如果_怎么让width设置为800px在手机上显示完整

FPGA-超声波避障小车(ego1)_ego1测距-程序员宅基地

文章浏览阅读4k次,点赞11次,收藏65次。基于FPGA的超声波避障小车,利用ego1的100HZ时钟,我们可以自己定义不同占空比的PWM来控制电机的转速和舵机的角度,我们可以通过自己写计时器获得超声波来回所需的时间来测量距离,根据距离的远近返回来控制电机的转速以及舵机转动的角度,我们也可以加上openmv识别色块的功能,来模拟小车识别红绿灯控制小车的出发和停止。结合以上描述,我们可以通过FPGA来实现一个颜色控制启动和停止的利用超声波测距来避障的小车。如下图根据项目任务要求,我们可以大致把目标任务分为以下几点:1.利用计数..._ego1测距

某县农业网被挂马 Trojan.Win32.KillAV.bca/Trojan-Downloader.Win32.Geral.ix_trojan:win32/wacatac.a!ml-程序员宅基地

文章浏览阅读3k次。某县农业网被挂马 Trojan.Win32.KillAV.bca/Trojan-Downloader.Win32.Geral.ix endurer 原创2009-05-05 第1版 打开某县农业网,Maxthon提示要安装ActiveX控件。 检查网页代码,发现:/---<script src=hxxp://***.w**vg0**.cn></script>---/#1_trojan:win32/wacatac.a!ml

iOS UIScrollView内容向下偏移20问题处理_ios scrollview自己偏移了20-程序员宅基地

文章浏览阅读4.2k次。控制器已经设置好,仍旧偏移的话就要iOS11self.automaticallyAdjustsScrollViewInsets = NO;还有iOS11考虑// iOS 11 ScrollView if (@available(iOS 11.0, *)) { UIScrollView.appearance.contentInsetAdjustmentBehavio..._ios scrollview自己偏移了20

异步FIFO设计_4.4 fifo控制模块逻辑设计-程序员宅基地

文章浏览阅读1.9k次,点赞2次,收藏14次。目录1. 功能2. 架构2.1. 顶层模块 async_fifo参数描述2.2. 伪双口RAM dual_port_ram参数描述2.3. 写逻辑 wr_logic参数描述2.4. 读逻辑 rd_logic参数描述3. 时序4. 代码异步FIFO常用于实现多bit数据的跨时钟域传输。1. 功能可实现快到慢、慢到快的跨时钟域多bit数据传输,具体功能介绍如下:、● 可对FIFO深度、宽度进行参数化自定义● 能够实现数据的异步读写功能,且读出的数据是先入先出的顺序● 能够指示FIFO空、满状态。同_4.4 fifo控制模块逻辑设计

推荐文章

热门文章

相关标签