《项链》莫泊桑的
一、 内容
为小说阅读创建一个可视化工具,能够将小说的人物,场景,主题,地点,时间等特征进行可视化,允许读者根据自己的兴趣爱好快速定位到相关内容,提供交互手段生成导航书签并进行书签编辑。
二、 背景介绍
小说因为字数过多,读者可能难以定位到自己感兴趣的地方,也难以了解到故事的主线,通过可视化分析,使小说的内容更加容易被理解和定位,选择分析的小说是莫泊桑的《项链》。我选择了文章之中分析出的人名,地名,时间线作为书签,在页面可以阅读小说,也可以通过书签进行跳转翻页。
三、 数据处理
首先对文章进行词性的分析和分词,运用的工具为THULAC,找出文章中的人名,地名,名词,动词等关键词语,将词语提取出来之后进行词频分析,运用的为java语言,分析之后结果如下:
N:太太:18东西:14丈夫:11店:10晚会:9项链:9朋友:8首饰:8衣裳:7盒子:6时候:6妇人:5手:5事:5车子:5裙袍:4镜子:4数目:4光景:3金刚:3神气:3钱:3颈项:3妻子:3机会:3男宾:3楼:3诱惑力:3珠宝:3样子:3
NP:骆塞尔:8玛蒂尔德:3伏来士洁:2若尔日:1金当郎:1金元:1莫泊桑:1郎波诺:1侍应生:1约翰妮:1南兑尔:1金法郎:1玛蒂尔蒂:1
NS:骆塞尔:6巴黎:1塞纳河:1布列塔尼省:1故宫街:1殉教街:1威尼斯:1香榭丽舍:1
A:小:10好:8大:5女:5伤心:4老:3清:3相同:3痛苦:3精美:3新:3快活:3漂亮:3失望:2真:2一般:2吃惊:2豪华:2一样:2像样:2遗憾:2完全:2高兴:2有钱:2黑:2早:2糊涂:2对:2日常:2骇人:1
V:是:45到:32去:28没有:21可以:16说:16有:14走:13借:10找:10要:10想:9带:8梦想:8能够:8叫:8会:7做:6像:6感到:6应当:6上:5下:5满意:5钻:5看:5对:5成:4回来:4穿:4
通过对关键词的分析绘制小说可视化图像。
四、 可视化设计
1关键词可视化:
运用工具:BLUEMC
通过文章之中出现次数最多的词语了解文章答题内容,省略部分无意义代词,通过词云可以了解文章大致主题,例如主角为太太,配角有丈夫,主要围绕项链,文中人物动作较多,场景切换有晚会,店里等。
2.制作树状图
制作树状图是为了了解文章大体结构,主要用了文中前30个名词关键词,词频大的在文章之中占比更大,在内容上更加重要,为根节点,词频小一点的为其子节点,查看附近形容词为其延伸词,以此来了解文章大体的结构层次。
3.制作树形图
文章中共有六章,提取出每章的关键词运用词频进行绘制树形图,通过这个图像可视化可以了解到每一章中的主要内容,方便读者了解到每一章的内容,并且进行相关选择,
定位到每一章。
4.制作文章人物关系图
运用Echars模板,通过文章之中出现的人名的次数来区分主角,配角,然后了解人物之间的关系
5.制作柱状图
柱状图的X轴为时间线,Y轴为地点线,通过文章前五关键词出现次数,了解文章时间和地点的结合点,从而了解文章的时间线和地点线,读者可以选择跳转到自己感兴趣的时间。
6.制作词频分析图
正为名词,负为动词,通过动词与名词的结合了解到文章在此点的结构和文章的主题变化。
五、 代码实现
环境配置:D3要在wamp中运行,并且有D3的工具包,Echarts要有echarts的工具包,在网页之中有超链接直接连到相应网页
Eclipse运行THULAC工具,可以将总结到的词性和词频输出到文件当中。
用浏览器打开相应的网页可以阅读小说
人物关系图代码(Echarts):
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>人物关系图</title> <!-- 引入 echarts.js --> <script src="echarts.js"></script> </head> <body> <!-- 为ECharts准备一个具备大小(宽高)的Dom --> <div id="main" style="width: 1200px;height:650px;"></div> <script type="text/javascript"> // 基于准备好的dom,初始化echarts实例 var myChart = echarts.init(document.getElementById('main')); // 指定图表的配置项和数据 var option = { title: { text: '人物关系图', }, tooltip: {}, animationDurationUpdate: 1500, animationEasingUpdate: 'quinticInOut', label: { normal: { show: true, textStyle: { fontSize: 18 }, } }, legend: { x: "center", data: ["主角", "配角", '作者','路人'] }, series: [ { type: 'graph', layout: 'force', symbolSize: 60, focusNodeAdjacency: true, roam: true, categories: [{ name: '主角' }, { name: '配角' }, { name: '作者' }, { name: '路人' }], label: { normal: { show: true, textStyle: { fontSize: 18 }, } }, force: { repulsion: 2000 }, edgeLabel: { normal: { show: true, textStyle: { fontSize: 18 }, formatter: "{c}" } }, data: [ { name: '太太', category: 0, draggable: true, }, { name: '玛蒂尔德', category: 0 , draggable: true, }, { name: '丈夫', category: 0 , draggable: true, }, { name: '骆塞尔', category: 0, draggable: true, }, { name: '伏来士洁', category: 1, draggable: true, }, { name: '若尔日', category: 1, draggable: true, ……等人物点 ], links: [{ source: '太太', target: '丈夫', value: '夫妻', lineStyle:{ normal:{ color:'red', } } }, { source: '丈夫', target: '骆塞尔', value: '同名' }, { source: '太太', target: '玛蒂尔德', value: '同名' }, { source: '莫泊桑', target: '太太', value: '作者' }, { source: '太太', target: '伏来士洁', value: '朋友,借项链', }, ……等人物关系线 ], lineStyle: { normal: { opacity: 0.7, width: 2, //curveness: 0.1 color:'black', } } } ] }; // 使用刚指定的配置项和数据显示图表。 myChart.setOption(option); </script> </body> </html> 树状图: <!DOCTYPE html> <style> form { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; } svg { font: 10px sans-serif; } </style> <svg width="960" height="570"></svg> <form> <label><input type="radio" name="mode" value="sumBySize" checked> Size</label> <label><input type="radio" name="mode" value="sumByCount"> Count</label> </form> <script src="https://d3js.org/d3.v4.min.js"></script> <script> var svg = d3.select("svg"), width = +svg.attr("width"), height = +svg.attr("height"); var fader = function(color) { return d3.interpolateRgb(color, "#fff")(0.2); }, color = d3.scaleOrdinal(d3.schemeCategory20.map(fader)), format = d3.format(",d"); var treemap = d3.treemap() .tile(d3.treemapResquarify) .size([width, height]) .round(true) .paddingInner(1); d3.json("flare.json", function(error, data) { if (error) throw error; var root = d3.hierarchy(data) .eachBefore(function(d) { d.data.id = (d.parent ? d.parent.data.id + "." : "") + d.data.name; }) .sum(sumBySize) .sort(function(a, b) { return b.height - a.height || b.value - a.value; }); treemap(root); var cell = svg.selectAll("g") .data(root.leaves()) .enter().append("g") .attr("transform", function(d) { return "translate(" + d.x0 + "," + d.y0 + ")"; }); cell.append("rect") .attr("id", function(d) { return d.data.id; }) .attr("width", function(d) { return d.x1 - d.x0; }) .attr("height", function(d) { return d.y1 - d.y0; }) .attr("fill", function(d) { return color(d.parent.data.id); }); cell.append("clipPath") .attr("id", function(d) { return "clip-" + d.data.id; }) .append("use") .attr("xlink:href", function(d) { return "#" + d.data.id; }); cell.append("text") .attr("clip-path", function(d) { return "url(#clip-" + d.data.id + ")"; }) .selectAll("tspan") .data(function(d) { return d.data.name.split(/(?=[A-Z][^A-Z])/g); }) .enter().append("tspan") .attr("x", 4) .attr("y", function(d, i) { return 13 + i * 10; }) .text(function(d) { return d; }); cell.append("title") .text(function(d) { return d.data.id + "\n" + format(d.value); }); d3.selectAll("input") .data([sumBySize, sumByCount], function(d) { return d ? d.name : this.value; }) .on("change", changed); var timeout = d3.timeout(function() { d3.select("input[value=\"sumByCount\"]") .property("checked", true) .dispatch("change"); }, 2000); function changed(sum) { timeout.stop(); treemap(root.sum(sum)); cell.transition() .duration(750) .attr("transform", function(d) { return "translate(" + d.x0 + "," + d.y0 + ")"; }) .select("rect") .attr("width", function(d) { return d.x1 - d.x0; }) .attr("height", function(d) { return d.y1 - d.y0; }); } }); function sumByCount(d) { return d.children ? 0 : 1; } function sumBySize(d) { return d.size; } </script> 树形图: <!DOCTYPE html> <style> form { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; } svg { font: 10px sans-serif; } </style> <svg width="960" height="570"></svg> <form> <label><input type="radio" name="mode" value="sumBySize" checked> Size</label> <label><input type="radio" name="mode" value="sumByCount"> Count</label> </form> <script src="https://d3js.org/d3.v4.min.js"></script> <script> var svg = d3.select("svg"), width = +svg.attr("width"), height = +svg.attr("height"); var fader = function(color) { return d3.interpolateRgb(color, "#fff")(0.2); }, color = d3.scaleOrdinal(d3.schemeCategory20.map(fader)), format = d3.format(",d"); var treemap = d3.treemap() .tile(d3.treemapResquarify) .size([width, height]) .round(true) .paddingInner(1); d3.json("flare.json", function(error, data) { if (error) throw error; var root = d3.hierarchy(data) .eachBefore(function(d) { d.data.id = (d.parent ? d.parent.data.id + "." : "") + d.data.name; }) .sum(sumBySize) .sort(function(a, b) { return b.height - a.height || b.value - a.value; }); treemap(root); var cell = svg.selectAll("g") .data(root.leaves()) .enter().append("g") .attr("transform", function(d) { return "translate(" + d.x0 + "," + d.y0 + ")"; }); cell.append("rect") .attr("id", function(d) { return d.data.id; }) .attr("width", function(d) { return d.x1 - d.x0; }) .attr("height", function(d) { return d.y1 - d.y0; }) .attr("fill", function(d) { return color(d.parent.data.id); }); cell.append("clipPath") .attr("id", function(d) { return "clip-" + d.data.id; }) .append("use") .attr("xlink:href", function(d) { return "#" + d.data.id; }); cell.append("text") .attr("clip-path", function(d) { return "url(#clip-" + d.data.id + ")"; }) .selectAll("tspan") .data(function(d) { return d.data.name.split(/(?=[A-Z][^A-Z])/g); }) .enter().append("tspan") .attr("x", 4) .attr("y", function(d, i) { return 13 + i * 10; }) .text(function(d) { return d; }); cell.append("title") .text(function(d) { return d.data.id + "\n" + format(d.value); }); d3.selectAll("input") .data([sumBySize, sumByCount], function(d) { return d ? d.name : this.value; }) .on("change", changed); var timeout = d3.timeout(function() { d3.select("input[value=\"sumByCount\"]") .property("checked", true) .dispatch("change"); }, 2000); function changed(sum) { timeout.stop(); treemap(root.sum(sum)); cell.transition() .duration(750) .attr("transform", function(d) { return "translate(" + d.x0 + "," + d.y0 + ")"; }) .select("rect") .attr("width", function(d) { return d.x1 - d.x0; }) .attr("height", function(d) { return d.y1 - d.y0; }); } }); function sumByCount(d) { return d.children ? 0 : 1; } function sumBySize(d) { return d.size; } </script> 柱形图: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>ECharts</title> <!-- 引入 echarts.js --> <script src="echarts.js"></script> <script src="http://echarts.baidu.com/resource/echarts-gl-latest/dist/echarts-gl.min.js"></script> </head> <body> <!-- 为ECharts准备一个具备大小(宽高)的Dom --> <div id="main" style="width: 1200px;height:650px;"></div> <script type="text/javascript"> // 基于准备好的dom,初始化echarts实例 var myChart = echarts.init(document.getElementById('main')); // 指定图表的配置项和数据 var hours = ['某晚', '三天后', '第二天', '一月十八', '十二点', '一星期', '十年']; var days = ['家', '朋友家', '晚会', '出租车', '小店', '街上']; var data = [[0,0,9],[0,1,3],[1,1,5],[1,2,4],[2,3,11],[2,4,5],[3,4,5],[4,5,17],[5,6,2]]; option = { tooltip: {}, visualMap: { max: 20, inRange: { color: ['#313695', '#4575b4', '#74add1', '#abd9e9', '#e0f3f8', '#ffffbf', '#fee090', '#fdae61', '#f46d43', '#d73027', '#a50026'] } }, xAxis3D: { type: 'category', data: hours }, yAxis3D: { type: 'category', data: days }, zAxis3D: { type: 'value' }, grid3D: { boxWidth: 200, boxDepth: 80, light: { main: { intensity: 1.2 }, ambient: { intensity: 0.3 } } }, series: [{ type: 'bar3D', data: data.map(function (item) { return { value: [item[1], item[0], item[2]] } }), shading: 'color', label: { show: false, textStyle: { fontSize: 16, borderWidth: 1 } }, itemStyle: { opacity: 0.4 }, emphasis: { label: { textStyle: { fontSize: 20, color: '#900' } }, itemStyle: { color: '#900' } } }] } // 使用刚指定的配置项和数据显示图表。 myChart.setOption(option); </script> </body> </html>
六、 探索发现
通过文章的关键词可以了解到文章的大体结构,从而更加有利于读者的了解,文章的可视化使得文章故事更加立体,提取出的关键更加明了,有利于文章整体构造,通过阅读资料了解,现在已有算法可以通过文章词语了解到文章所表达的情感变换,有利于人们的阅读和分类。
七、 总结
可以对文章进行分词处理,提取出文章的关键词进行分析,通过对文章的不同词语的不同词频的分析,可以得到许多与文章内容相关的提要,使书签制作出来,更加有利于读者的阅读。