长时间用户第一次在这里提出问题(我第一次无法使用我搜索的答案解决问题)。我用自己的数据在 JS D3 中重新创建了 uber 和弦图。我的实现可以在这里找到,尽管该地址可能不会永远保持良好,所以这里是代码(请原谅整个过程中的一些错位):
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script type="text/javascript" src="d3/d3.js"></script>
<script type="text/javascript" src="d3/d3.layout.js"></script>
<link type="text/css" rel="stylesheet" href="style.css"/>
</head>
<body>
<div id="body">
<div id="footer">
Purdue OIR Testing - Migration
<div class="hint">mouseover groups to highlight</div>
</div>
</div>
<div id="tooltip"></div>
<script type="text/javascript">
//import the data and call the draw chords function
d3.text("migrationdata.csv", function(data) {
var matrix = d3.csv.parseRows(data).map(function(row) {
return row.map(function(value) {
return +value;
});
});
d3.text("headersColors.csv", function(headerdata) {
var headersColors = d3.csv.parseRows(headerdata);
var headers = headersColors[1];
var colors = headersColors[2];
drawChords(matrix, headers, colors);
});
});
//create the chord viz
function drawChords (matrix, headers, colors){
var w = 980,
h = 800,
r1 = h / 2,
r0 = r1 - 110,
fadeOutA = 0,
fadeInA = 0.8;
var fill = d3.scale.category20c();
var chord = d3.layout.chord()
.padding(.02)
.sortSubgroups(d3.descending)
.sortChords(d3.descending);
var arc = d3.svg.arc()
.innerRadius(r0)
.outerRadius(r0 + 20);
var svg = d3.select("body").append("svg:svg")
.attr("width", w)
.attr("height", h)
.append("svg:g")
.attr("id", "circle")
.attr("transform", "translate(" + w / 2 + "," + h / 2 + ")");
svg.append("circle")
.attr("r", r0 + 20);
//.attr("fill-opacity",0);
//assign the matrix
chord.matrix(matrix);
//create the groups
var g = svg.selectAll("g.group")
.data(chord.groups)
.enter().append("svg:g")
.attr("class", "group")
.on("mouseover", mouseover)
.on("mouseout", function (d) { d3.select("#tooltip").style("visibility", "hidden") });
g.append("svg:path")
.style("stroke", function(d) { return colors[d.index]; })
.style("fill", function(d) { return colors[d.index]; })
.attr("d", arc);
g.append("svg:text")
.each(function(d) { d.angle = (d.startAngle + d.endAngle) / 2; })
.attr("dy", ".35em")
.attr("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; })
.attr("transform", function(d) {
return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")"
+ "translate(" + (r0 + 26) + ")"
+ (d.angle > Math.PI ? "rotate(180)" : "");
})
.text(function(d) { return headers[d.index]; });
var chordPaths = svg.selectAll("path.chord")
.data(chord.chords)
.enter().append("svg:path")
.attr("class", "chord")
.style("stroke", function(d) { return d3.rgb(colors[d.source.index]).darker(); })
.style("fill", function(d) { return colors[d.source.index]; })
.attr("d", d3.svg.chord().radius(r0))
.on("mouseover", function (d) {
d3.select("#tooltip")
.style("visibility","visible")
.html(chordTip(d))
.style("left", (d3.event.pageX - 100) + "px")
.style("top", (d3.event.pageY - 100) + "px");
})
.on("mouseout", function (d) { d3.select("#tooltip").style("visibility", "hidden") });
function chordTip (d) {
var p = d3.format(".1%"), q = d3.format(",.2r")
return "Migration Info:<br/>"
+ headers[d.source.index] + " → " + headers[d.target.index]
+ ": " + Math.round(d.source.value) + "<br/>"
+ headers[d.target.index] + " → " + headers[d.source.index]
+ ": " + Math.round(d.target.value) + "<br/>";
}
function groupTip (d) {
return "College Info:<br/>"
+ headers[d.index] + " : " + Math.round(d.value) + "<br/>";
}
function mouseover(d, i) {
d3.select("#tooltip")
.style("visibility", "visible")
.html(groupTip(d))
.style("top", function () { return (d3.event.pageY - 80)+"px"})
.style("left", function () { return (d3.event.pageX - 130)+"px";})
chordPaths.classed("fade", function(p) {
return p.source.index != i
&& p.target.index != i;
});
}
}
// Returns an event handler for fading a given chord group.
function fade(opacity) {
return function(d, i) {
svg.selectAll("path.chord")
.filter(function(d) { return d.source.index != i && d.target.index != i; })
.transition()
.style("stroke-opacity", opacity)
.style("fill-opacity", opacity);
};
}
</script>
</body>
</html>
这很好用,而且功能齐全。它显示了从 2012 年秋季到 2013 年秋季大学之间的学生流动(我打算在屏幕上进行更好的解释)。我的下一步是使这个可钻。因此,例如,如果您单击一所大学,我想将该大学扩展到其部门,并显示各个部门之间的和弦以及“其他”,这将是所有其他大学的组合。此外,您将能够以相同的方式深入部门以获取主要级别的详细信息。我拥有执行此操作所需的所有原始数据,并且我相当有信心知道如何在单击组时使用它来动态创建新矩阵。
我的问题是,如果我每次都将当前可视化转储为带有新数据的新可视化,那么执行我上面描述的所有操作都相当简单,但这将是一个艰难的过渡。我更喜欢这样的动画。但是,该示例仅适用于您的矩阵在转换的每一端都具有相同大小的情况。由于我想深入研究这些数据,这对我来说并不总是正确的。我可以拥有比以前更少或更多的组/和弦。我的问题是,如果我创建一个新的和弦布局,其矩阵比当前屏幕上的矩阵更大/更小,我能以某种方式平滑地为新的/不需要的组/和弦设置动画吗?如果是这样,我将如何解决这个问题,是否有任何例子?
我对 D3 还很陌生,但我正在努力快速学习,因为这里对这些类型的可视化有大量需求。如果有关于此的教程可以随意简单地链接它,我已经断断续续地研究了几天,但没有找到任何令人满意的解释如何在 d3 布局中平滑添加/删除元素。