Vue + D3 动态可视化图实现之二:词云图
GTD数据分析及可视化项目的第二张图表,项目总体介绍见这篇文章。最终效果实现数据集统计组合只有攻击,目标,武器类型乘以发生次数,死亡人数6种组合,故使用category编号1-6代表6种统计类型。攻击,目标,武器类型在原数据集中有各自编码,在type列记录。词云图制作制作词云图需要用d3的一个第三方库d3-cloud,该库可使用npm安装。引用:import * as cloud from "d3
GTD数据分析及可视化项目的第二张图表,项目总体介绍见这篇文章。
最终效果
实现
数据集
统计组合只有攻击,目标,武器类型乘以发生次数,死亡人数6种组合,故使用category编号1-6代表6种统计类型。攻击,目标,武器类型在原数据集中有各自编码,在type列记录。
词云图制作
制作词云图需要用d3的一个第三方库d3-cloud,该库可使用npm安装。引用:
import * as cloud from "d3-cloud";
HTML部分,设置两个下拉框和两个起止年份数字输入框和一个生成按钮。
<div id="word-cloud">
<div id="selectSection">
<select name="" id="category">
<option value="attacktype">攻击类型</option>
<option value="targettype">目标类型</option>
<option value="weapontype">武器类型</option>
</select>
<input id="yearbegin" type="number" min="1970" max="2018" value="1970">
<input id="yearend" type="number" min="1970" max="2018" value="2018">
<select name="" id="metric">
<option value="count">发生次数</option>
<option value="killed">死亡人数</option>
</select>
<button @click="onGenerate" >生成</button>
</div>
<div id="word-cloud-graph"></div>
</div>
@click事件调用onGenerate函数,根据下拉框组合得到1-6的编号,和起止年份共同作为参数调用procData函数。procData函数中根据这3个参数进行筛选,然后按指标排序。为了美观,只取排名前8的种类做图。实践中发现不同种类的比例过于悬殊,例如第一名的爆破物袭击可能是第二名的10倍以上,因此词的大小不能严格按比例,而是按排名映射到一个自定义的比例。我这里使用的是:
var sizeMap = [100, 60, 60, 50, 50, 40, 40, 30]
词云图的实现我查到了一个很巧妙的做法:用JS的函数闭包,封装一个生成词云的对象,看代码。
function wordCloud(selector) {
var svg = d3.select(selector).append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var fill = d3.scaleOrdinal(d3.schemeTableau10);
function draw(words) {
var cloud = svg.selectAll(".cloud")
.data(words, d => d.text)
cloud.enter()
.append("text")
.attr("class", "cloud")
.style("font-family", "Helvetica")
.style("fill", (d, i) => fill(i))
.attr("text-anchor", "middle")
.attr('font-size', 1)
.text(d => d.text);
cloud
.transition()
.duration(600)
.attr('font-size', d => d.size + "px")
.attr("transform", function(d) {
return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
})
.style("fill-opacity", 1);
cloud.exit()
.transition()
.duration(200)
.style('fill-opacity', 1e-6)
.attr('font-size', 1)
.remove();
}
return {
update: function(words) {
cloud().size([width, height])
.words(words)
.padding(5)
.rotate(() => ~~(Math.random() * 2) * 90 * ((rot++) % 2))
.font("Helvetica")
.fontSize(d => d.size)
.on("end", draw)
.start();
}
}
}
在外部调用时就很简单了,先传入根节点生成一个词云对象,然后调用其update函数,传入数据更新即可。
// init函数中
myWordCloud = wordCloud('#word-cloud-graph');
// onGenerate函数中
myWordCloud.update(procData(type, yearBegin, yearEnd))
源码
见项目总体介绍底部项目链接。本图源码为src/components/WordCloud.vue文件。
更多推荐
所有评论(0)