可以使用其他可视化类型,它们可能会更清楚地显示此信息。我想到了一个网格,但它在视觉上可能不如你目前拥有的那种可视化那么令人兴奋:
Les Misérables [character] Co-occurance(迈克·博斯托克):
这种类型的可视化可以很容易地用于显示角色和位置的重叠。网格的镜像不会出现在位置/字符轴上。但是,如果需要节点和链接,我们当然可以使用它。
解开可视化
传统上,我会通过进一步纠缠来混淆这种类型的图表。这将涉及在学校测验中过于复杂的those spaghetti style matching questions,以期让老师相信,让我从怀疑中受益比追踪路径更容易。使这些网络在视觉上无法追踪是如此容易,这表明如果您的可视化变得更详细,这可能不是一种理想的风格。但是,为了弥补我之前在制作无法读取的网络方面的罪过,让我们梳理一下我们拥有的东西。
肯定有一种很好的算法可以计算绘制图表所需的最短路径长度,将孤立的网络彼此分开,并在 x 轴上尽可能靠近地对齐源和目标。尽管这可能会将所有高度连接的节点靠近在一起,导致可视化的中心看起来像一盘意大利面。
但是,由于我很懒,我宁愿让图表进行某种自我组织。幸运的是,在 d3 中我们有一个力布局。
让我们保持您的总体布局开始,一排英雄和一排城市。我们可以设置一些力和参数来发挥作用,而不是随机化每个节点的位置并绘制连接线:
- 修复 y 坐标以便保留行
- 高度重视链接距离,以便将链接的节点紧密地拉在一起
- 从节点之间的轻微吸引力开始以允许混合,然后慢慢开始迫使它们分开
- 随着布局的发展,增加碰撞半径,使节点等距分布
如果我们正确地做到这一点,我们会发现一个很好的可视化效果,它可以分隔孤立的网络(对于这种类型的可视化来说是必不可少的),并且在大多数情况下保持链接相对较短。
这是一个使用这种方法的随机输出示例(减半):
与随机排序节点相比,关系更清晰(除了侥幸的幸运结果),有一些链接似乎延伸得很远,但它们的数量相对有限。孤立的网络在视觉上也是分离的,这从拓扑的角度来看很重要。
为了比较,这是原始布局的随机加载(我确实出于某种原因更改了节点样式和行顺序,但我手中的啤酒杯说不更正) :
这是一个特别倒霉的随机抽奖,黑寡妇连接到x轴上的第一个和最后一个位置。
原始布局中的平均链接比强制布局中的平均链接更水平 - 更多的垂直链接通过减少交叉点的数量同时缩短链接长度来提高清晰度并减少混乱。
我们可以尝试量化部队布局带来的改进。例如,随机排序(样本大小 = 10)中的平均链接长度(测量为沿 x 轴的源和目标之间的距离)为 521 px,而强制布局排序的平均链接长度为 208 px(样本大小也是 10)。这是每个布局链接长度的直方图:
我的样本量很小,但模式应该很清楚。
好的,我已经展示了结果并说明了一般要求,但是演示一下如何。无需深入了解力图的工作原理或编码方式,这里有一个快速演示 bl.ock,下面是我使用的关键参数的快速说明:
- 设置模拟的启动属性:
一旦模拟开始,这些将被修改,但这些是很好的起点:
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))
}
- 别忘了用
d.fy固定力节点的y坐标
进一步改进
所以现在我们很纠结,但我们可以做得更好。为什么将自己限制在 2 行? 3 可能是理想的:
如果说一个有两种类型的字符要显示,这可能是理想的。幸运的是,每个英雄都有一个恶棍。最好按照自然划分进行分离,例如:女性/男性、前科学家和拥有高级学位的英雄/其他所有人、来自地球/不是来自地球、浣熊/不是浣熊......
但即使无法按照某个主题进行分离,它仍会进一步解开可视化。基于此,与我这里的第一个示例和this one (shown above) 的唯一变化是我将英雄分开(并为可视化增加了一点高度)。
如果冷却时间难以忍受(您可以在排序时对单独的节点进行某种缩放),那么您可以在不绘制它的情况下运行模拟 - 仅加载最终版本(或者甚至存储布局以供将来使用,如果它的节点和链接不会改变每个负载)。但是,对于不同的问题,这是不同类型的问题。