使用D3.js的3维(X,Y和Z)图

时间:2014-01-10 11:33:08

标签: javascript d3.js svg graph 3d

我正在搜索 3维(x,y和z)的图表,并使用 D3.js 。请告诉我,如果有任何数据可视化网站,我可以找到这样的图表,或者d3js.org上有一个我错过了什么。

7 个答案:

答案 0 :(得分:66)

由VividD和Lars Kotthoff联系起来的3D散点图可能是你所要求的最好的例子,但我会反对并暗示你可能会提出错误的问题。

尝试在平面屏幕上模拟三个空间尺寸始终是不完美的,并且难以读取数据。但是,在D3中绘制三个不同的数据维度非常容易。您可以为两个数据变量使用水平和垂直布局,然后为第三个变量使用大小,形状,颜色或阴影。

如果所有三个数据变量最好用连续数字表示,那么最好的方法是使用气泡散点图,其中三个显示尺寸是水平位置,垂直位置和气泡大小。

这是一个例子,它还使用在线交互式组件添加通过运动显示的第四个维度:
Bubble ScatterplotBubble Scatterplot -- click for original

您说您的三个维度是客户产品内容。我不知道“内容”是什么类型(数量或类别),但我很确定“客户”和“产品”是类别。

下面是一个示例,其中使用两个分类维度来布置表格,然后表格的每个单元格都包含一个由第三个数字维度确定大小的圆圈。如果您的第三个变量是一个类别,您可以使用一个形状来指示哪个“内容”类型(如果有)与每个“客户”和“产品”配对一起使用:
Bubble Matrix
Bubble Matrix -- click for original

这是另一个,其中第三个维度是按颜色而不是按大小显示的。颜色代表连续变量,但您可以轻松选择一组高对比度颜色来表示类别:
Colour MatrixColour Matrix -- click for original

当然,一个普通的堆积条形图是显示两个类别和数量的另一种方式:
Stacked Bar Graphs Stacked Bar Graphs -- click for original

您不必停在三个数据变量上。如果两个数据变量是您不介意分类到类别中的类别或数字,您可以使用“小多重”方法绘制四个变量,您可以在其中创建表示分类变量的表,然后重复一个图表。每个表格单元格中的其他两个变量。

像这样:
Scatterplot MatrixScatterplot Matrix -- click for original

或者这个(周和星期几是数据的两个维度,类别/金额是另外两个):
Pie Chart Small Multiples
Pie Chart Small Multiples -- click for original

我希望能为您提供很多想法......

答案 1 :(得分:39)

一种类似于你所追求的例子:

3D scatter plot

enter image description here

请注意,此示例使用X3DOM,这是一种相当新的尝试,用于标准化HTML中的3D渲染,并非所有浏览器都支持。


关于在X3DOM中使用D3.js的一些其他测试示例:

X3DOM in callback test

D3 X3DOM event test

在D3 Google小组上搜索X3DOM。


另一种可能有趣的方法是使用D3.js和Three.js,就像这里:

Showing GPS tracks in 3D with three.js and d3.js


一般来说,D3.js更倾向于数据可视化而不是科学可视化,这意味着它没有广泛支持显示3D空间(除了显示地理3D数据,D3.js支持优秀方式,但这不是什么需要)。

例如(此示例与您的示例没有直接关系,仅用于解释):D3提供了用于树木的2D绘制的算法,但是没有提供用于树木的3D放置以及随后在树上进行这种放置的任何装置。 2D屏幕。


如果您不仅限于D3.js,也许您可​​以使用其他库来更轻松,更快地实现相同目标,这些库是专为与您类似的目的而编写的。例如,您可以使用Pre3D。看一下这个example。 Pre3D不使用X3DOM,只是2D画布上的纯HTML渲染。我认为它的3D图形的动画(旋转)比第一个D3 / X3DOM例子中的那些更平滑。


希望这个答案可以帮到你。

答案 2 :(得分:8)

我能找到的最好的例子(非常简单)是:

Highcharts (三维分散)

http://www.highcharts.com/demo/3d-scatter-draggable

Vis.js (各种3d选项,如条形和点)

http://visjs.org/examples/graph3d/playground/index.html

只需提供x,y和z坐标,设置你喜欢的配置,然后你就笑了。

答案 3 :(得分:6)

它看起来像是一个更恰当的说法的新例子" 3D"已经出来了。 http://bl.ocks.org/supereggbert/aff58196188816576af0

这是我做的改编:

http://bl.ocks.org/adrianturcato/cf665b7cca9f6057691a

答案 4 :(得分:5)

我创建了d3-3d,这是一个d3插件,可以帮助您可视化您的3D数据。希望这有助于实现您的目标。

enter image description here

答案 5 :(得分:1)

Stefan Nieke最近使用他的d3-3d插件做了一些很酷的事情:https://bl.ocks.org/Niekes

答案 6 :(得分:1)

这是纯 javascript + svg 解决方案

https://www.thestylistgroup.com/

let f = (x, z) => Math.cos(z/20)*20 + Math.sin(x/10)*10 + x/3*Math.atan2(z,x);

let cos = Math.cos, sin = Math.sin, xyz = 'xyz'.split(''),
    k = 500, a1 = 0, a2 = 0, far = 300, p, w, h, a,
    points = [], lines = [], s = 100;
    
for (var x = -s; x < s; x += 5) 
for (var z = -s; z < s; z += 5) 
  points.push({x, z, r:2});

for (var i = 0; i < 6; i++) lines.push([
  {x:-s, y:-s, z:-s, color:`hsl(${i*120},55%,55%)`, state:{}},
  {x:i%3==0?s:-s, y:i%3==1?s:-s, z:i%3==2?s:-s, state:{}}
]);

points.forEach(d=>d.state={fill:`rgb(${d.x+s},${(d.y=f(d.x,d.z))+s},${d.z+s})`});
pointsGroup.innerHTML=points.map((d,i)=>`<circle ind="${i}"></circle>`).join('');
linesGroup.innerHTML=lines.map(d=>`<path stroke="${d[0].color}"></path>`).join('');
let circles = pointsGroup.querySelectorAll('circle');
let paths = linesGroup.querySelectorAll('path');

function project(p) {
  let x = p.x*cos(a1) + p.z*sin(a1);
  let z = p.z*cos(a1) - p.x*sin(a1);
  let y = p.y*cos(a2) +   z*sin(a2);
  let d =   z*cos(a2) - p.y*sin(a2) + far;
  p.state.cx = (k/d)*x + w/2;
  p.state.cy = (k/d)*y + h/2;
  p.state.r = far/d*p.r;
}

function render() {
  if (a) for (var i=0; i<3; i++) 
    xyz.forEach((c, j) => lines[i][0][c] = i==j ? -s : (lines[i][1]=a)[c]);
  points.forEach(project);	
  points.sort((a, b) => a.state.r - b.state.r);
  lines.forEach(line => line.forEach(project));	
  points.forEach((d, i) => Object.entries(d.state)
      .forEach(e => circles[i].setAttribute(...e)));
  lines.forEach((l, i) => paths[i].setAttribute('d', 
     `M${l[0].state.cx} ${l[0].state.cy} L${l[1].state.cx} ${l[1].state.cy}`));
}

let evt = (t, f) => addEventListener(t, e => render(f(e)));
evt('click', e => a = points[e.target.getAttribute('ind')])
evt('wheel', e => k *= 1 - Math.sign(e.deltaY)*0.1);
evt('mouseup', e => p = null);
evt('mousedown', e => p = {x: e.x, y: e.y, a1, a2});
evt('mousemove', e => p && (a1 = p.a1-(e.x-p.x)/100) + (a2 = p.a2-(e.y-p.y)/100));
evt('resize', e=>svg.setAttribute('viewBox',`0 0 ${w=innerWidth} ${h=innerHeight}`));
dispatchEvent(new Event('resize'));
<svg id="svg" stroke-width="2"><g id="pointsGroup"></g><g id="linesGroup"></g></svg>