Javascript 如何对D3中的节点进行排序,以便清除连接路径?
Javascript 如何对D3中的节点进行排序,以便清除连接路径?,javascript,algorithm,sorting,d3.js,data-visualization,Javascript,Algorithm,Sorting,D3.js,Data Visualization,我正在绘制一张图表,以显示两组之间的关系。 为了说明这个问题,让我们假设这两个群体是城市和超级英雄,对于每个城市,我们展示哪个超级英雄访问过它,反之亦然 我现在的代码 将鼠标悬停在每个节点上以查看其名称并高亮显示其连接的节点 如您所见,我在顶部有一行,每个城市有节点,底部有一行,每个超级英雄有节点,两行之间的路径显示了关系: 正如你所看到的,在大多数情况下,路径都很长,并且相互交叉很多。这使得数字图像在视觉上非常混乱 我确信,如果我以更智能的方式对城市和/或超级英雄进行排序,图表将更加清晰。
我正在绘制一张图表,以显示两组之间的关系。
为了说明这个问题,让我们假设这两个群体是城市和超级英雄,对于每个城市,我们展示哪个超级英雄访问过它,反之亦然 我现在的代码 将鼠标悬停在每个节点上以查看其名称并高亮显示其连接的节点 如您所见,我在顶部有一行,每个城市有节点,底部有一行,每个超级英雄有节点,两行之间的路径显示了关系: 正如你所看到的,在大多数情况下,路径都很长,并且相互交叉很多。这使得数字图像在视觉上非常混乱 我确信,如果我以更智能的方式对城市和/或超级英雄进行排序,图表将更加清晰。
(这对当前的城市秩序和超级英雄的秩序都没有意义,为了说明这一点,我在加载图表之前将这两个数组洗牌) 我的问题:
如何对城市和/或英雄进行排序,以便两者之间的联系更加清晰(路径不会那么长,路径不会相互交叉,等等)。
有没有一个众所周知的算法?可能是D3中的辅助函数?或者可能我使用了错误的图表类型。可以使用其他可视化类型,这样可以更清楚地显示这些信息。脑海中浮现出一个网格,但它在视觉上可能不如你目前所看到的那样令人兴奋: (迈克·博斯托克): 这种类型的可视化可以很容易地被添加以显示角色和位置重叠。栅格的镜像不会与位置/角色轴一起出现。但是,如果节点和链接是可取的,我们当然可以使用它
取消可视化的角度设置 传统上,我会将这种类型的图表进一步混淆。这会使学校的测验过于复杂化,以期说服老师,让我相信怀疑比追踪路径更容易。很容易使这些网络在视觉上无法追踪,这表明,如果你的可视化变得更详细,这可能不是一种理想的风格。但是,为了弥补我以前制作不可读网络的过失,让我们来分析一下我们所拥有的 当然,有一种很好的算法可以计算出绘制图表所需的最小路径长度,将孤立的网络彼此分离,并在x轴上将源和目标尽可能靠近地对齐。尽管这可能会将所有高度连接的节点放在一起,导致可视化的中心看起来像一盘意大利面条 但是,由于我懒惰,我宁愿让图表做某种自组织。幸运的是,在d3中我们有一个部队布局 让我们从总体布局开始,一排英雄和一排城市。我们可以设置一些力和参数来发挥作用,而不是随机化每个节点的位置并绘制连接线:
- 固定y坐标,以便保留行
- 高度重视链接距离,以便将链接节点紧密地拉在一起
- 从节点之间的轻微吸引开始,允许混合,然后慢慢开始迫使它们分开
- 随着布局的发展,增加碰撞半径,使节点的间距相等
var simulation = d3.forceSimulation()
// set optimal distance to be 1 pixel between source and target:
.force("link", d3.forceLink().id(function(d) { return d.name; }).distance(1))
// set the distance at which forces apply to be limited to some distance,
// make nodes attracted to each other to start:
.force("charge", d3.forceManyBody().distanceMax(cities.length+1/width).strength(10))
// try to keep nodes centered:
.force("center", d3.forceCenter(width / 2, height / 2))
// slow decay time, increases time to simulation end
.alphaDecay(0.01);
function ticked() {
var force = this;
var alpha = force.alpha(); // current alpha
var padding = 20; // minimum distance from sides of visualization
var targetSeparation = (width-padding*2)/(cities.length*2) // ideal separation between nodes on x axis.
// if we are late in the simulation, change collide radius to ideal separation
// also change the charge between nodes to repulsion
if (alpha < 0.5) {
force.force( "collide",d3.forceCollide((0.5 - alpha)*2*targetSeparation) )
.force("charge", d3.forceManyBody().distanceMax(targetSeparation).strength((0.5 - alpha) * 50))
}
勾选的函数(){
var force=这个;
var alpha=force.alpha();//当前alpha
var padding=20;//与可视化侧面的最小距离
var targetSeparation=(宽度填充*2)/(cities.length*2)//x轴上节点之间的理想间距。
//如果我们是我
function ticked() {
var force = this;
var alpha = force.alpha(); // current alpha
var padding = 20; // minimum distance from sides of visualization
var targetSeparation = (width-padding*2)/(cities.length*2) // ideal separation between nodes on x axis.
// if we are late in the simulation, change collide radius to ideal separation
// also change the charge between nodes to repulsion
if (alpha < 0.5) {
force.force( "collide",d3.forceCollide((0.5 - alpha)*2*targetSeparation) )
.force("charge", d3.forceManyBody().distanceMax(targetSeparation).strength((0.5 - alpha) * 50))
}