d3.js - dc.js 使用 crossfilter 将回归图合并到现有的散点图中

标签 d3.js dc.js crossfilter

我正在使用 dc.js 和 crossfilter.js 创建一个 d3 仪表板,我想知道如何将回归线实现到响应过滤的散点图中。

我一直在玩几个重新添加回归线的例子,但我一直没有成功提取和合并代码。

我在数学上没有问题,而是如何从维度访问过滤后的数据,然后如何将回归线添加到过滤后的散点图(以便回归线也响应 future 过滤)。

jsFiddle Demo

var data = [
{"record":"record","date":"date","cars":"cars","bikes":"bikes"},
{"record":"1","date":"01/05/2012","cars":"1488.1","bikes":"49.73"},
{"record":"2","date":"02/05/2012","cars":"1374.29","bikes":"52.44"},
{"record":"3","date":"03/05/2012","cars":"1353.01","bikes":"47.92"},
{"record":"4","date":"04/05/2012","cars":"1420.33","bikes":"50.69"},
{"record":"5","date":"05/05/2012","cars":"1544.11","bikes":"47.47"},
{"record":"6","date":"06/05/2012","cars":"1292.84","bikes":"47.75"},
{"record":"7","date":"07/05/2012","cars":"1318.9","bikes":"48.64"},
{"record":"8","date":"08/05/2012","cars":"1686.3","bikes":"50.9"},
{"record":"9","date":"09/05/2012","cars":"1603.99","bikes":"53.44"},
{"record":"10","date":"10/05/2012","cars":"1420.1","bikes":"53.29"},
{"record":"11","date":"11/05/2012","cars":"1410.8","bikes":"54.06"},
{"record":"12","date":"12/05/2012","cars":"1374.62","bikes":"51.24"},
{"record":"13","date":"13/05/2012","cars":"1279.53","bikes":"53.96"},
{"record":"14","date":"14/05/2012","cars":"1330.47","bikes":"49.5"},
{"record":"15","date":"15/05/2012","cars":"1377.61","bikes":"52.32"},
{"record":"16","date":"16/05/2012","cars":"1302.12","bikes":"51.96"},
{"record":"17","date":"17/05/2012","cars":"1326.9","bikes":"49.86"},
{"record":"18","date":"18/05/2012","cars":"1181.55","bikes":"50.25"},
{"record":"19","date":"19/05/2012","cars":"1493.75","bikes":"51.24"},
{"record":"20","date":"20/05/2012","cars":"1463.9","bikes":"50.88"},
{"record":"21","date":"21/05/2012","cars":"1370.16","bikes":"51.09"},
{"record":"22","date":"22/05/2012","cars":"1403.3","bikes":"51.67"},
{"record":"23","date":"23/05/2012","cars":"1277.65","bikes":"49.3"},
{"record":"24","date":"24/05/2012","cars":"1361.94","bikes":"50.47"},
{"record":"25","date":"25/05/2012","cars":"1400.8","bikes":"51.55"},
{"record":"26","date":"26/05/2012","cars":"1289.09","bikes":"47.17"},
{"record":"27","date":"27/05/2012","cars":"1258.39","bikes":"52.12"},
{"record":"28","date":"28/05/2012","cars":"1288.71","bikes":"49.28"},
{"record":"29","date":"29/05/2012","cars":"1511.86","bikes":"50.73"},
{"record":"30","date":"30/05/2012","cars":"1300.38","bikes":"52.39"},
{"record":"31","date":"31/05/2012","cars":"1455.19","bikes":"49.53"},
{"record":"32","date":"01/06/2012","cars":"1311.89","bikes":"50.37"},
{"record":"33","date":"02/06/2012","cars":"1368.64","bikes":"50.87"},
{"record":"34","date":"03/06/2012","cars":"1360.05","bikes":"50.51"},
{"record":"35","date":"04/06/2012","cars":"1382.56","bikes":"49.67"},
{"record":"36","date":"05/06/2012","cars":"1304.15","bikes":"47.6"},
{"record":"37","date":"06/06/2012","cars":"1271.57","bikes":"50.22"},
{"record":"38","date":"07/06/2012","cars":"1442.38","bikes":"50.8"},
{"record":"39","date":"08/06/2012","cars":"1406.38","bikes":"53.14"},
{"record":"40","date":"09/06/2012","cars":"1724.16","bikes":"49.66"},
{"record":"41","date":"10/06/2012","cars":"1931.05","bikes":"53"},
{"record":"42","date":"11/06/2012","cars":"1669.47","bikes":"53.71"},
{"record":"43","date":"12/06/2012","cars":"1794.06","bikes":"51.78"},
{"record":"44","date":"13/06/2012","cars":"1625.98","bikes":"51.58"},
{"record":"45","date":"14/06/2012","cars":"1371.51","bikes":"52.36"},
{"record":"46","date":"15/06/2012","cars":"1418.05","bikes":"47.64"},
{"record":"47","date":"16/06/2012","cars":"1431","bikes":"53.14"},
{"record":"48","date":"17/06/2012","cars":"1527.21","bikes":"48.63"},
{"record":"49","date":"18/06/2012","cars":"1320.95","bikes":"51.7"},
{"record":"50","date":"19/06/2012","cars":"1396.93","bikes":"52.92"}
];
tSel1 = "cars";
tSel2 = "bikes";

data.forEach(function (d) {
	d[tSel1] = +d[tSel1];
	d[tSel2] = +d[tSel2];
});

var facts = crossfilter(data);

var allDimension = facts.groupAll();
var scatterDimension = facts.dimension(function(d) {return [+d[tSel1], +d[tSel2]];});
var scatterGroup = scatterDimension.group().reduceSum(function(d) { return d[tSel1]; });

var maxY1 = d3.max(data, function(d) {return d[tSel1]});
var maxY2 = d3.max(data, function(d) {return d[tSel2]});
var maxY1Plus = maxY1 + (maxY1 * 0.1);
var maxY2Plus = maxY2 + (maxY2 * 0.1);

var minY1 = d3.min(data, function(d) {return d[tSel1]});
var minY1Minus = minY1 * 0.9;
var minY2 = d3.min(data, function(d) {return d[tSel2]});
var minY2Minus = minY2 * 0.9;

xyScatterChart = dc.scatterPlot("#scatterPlot");
xyScatterChart	
	.width(600)
	.height(400)
	.margins({top: 20, right: 20, bottom: 20, left: 60})
	.dimension(scatterDimension)
	.group(scatterGroup)
	.symbolSize(6)
	.highlightedSize(15)
	.brushOn(false)
	.excludedOpacity(0.5)
	.excludedSize(5)
	.renderHorizontalGridLines(true)
	.renderVerticalGridLines(true)

	.x(d3.scale.linear().domain([minY1Minus,maxY1Plus]))
	.y(d3.scale.linear().domain([minY2Minus,maxY2Plus]));

dc.renderAll();
dc.redrawAll();
<link href="http://dc-js.github.io/dc.js/css/dc.css" rel="stylesheet"/>
<script src="http://dc-js.github.io/dc.js/js/d3.js"></script>
<script src="http://dc-js.github.io/dc.js/js/crossfilter.js"></script>
<script src="http://dc-js.github.io/dc.js/js/dc.js"></script>
<div id="scatterPlot"></div>

引用资料:

https://groups.google.com/forum/#!topic/dc-js-user-group/HaQMegKa_U0

https://bl.ocks.org/ctufts/298bfe4b11989960eeeecc9394e9f118

最佳答案

包含 example in dc.js 会很棒,因为这是很多人都可以使用的东西。

也许我们可以一起努力?我不懂数学,但这里有一种简单的方法,可以使用复合图表显示根据聚合组计算的数据线。

首先,这是嵌入了旧散点图的复合图表:

var composite = dc.compositeChart("#composite");
composite   
    .width(600)
    .height(400)
    .margins({top: 20, right: 20, bottom: 20, left: 60})
    .dimension(scatterDimension)
    .group(scatterGroup)
  .compose([
  dc.scatterPlot(composite)
    .symbolSize(6)
    .highlightedSize(15)
    .brushOn(false)
    .excludedOpacity(0.5)
    .excludedSize(5)
    .renderHorizontalGridLines(true)
    .renderVerticalGridLines(true),
  dc.lineChart(composite)
  .group(regressionGroup(scatterGroup))
])
    .x(d3.scale.linear().domain([minY1Minus,maxY1Plus]))
    .y(d3.scale.linear().domain([minY2Minus,maxY2Plus]));

请注意,我们为复合图和散点图都提供了散点组。那只是因为复合图表需要一个组,即使它实际上并不使用它。

我们已将与坐标相关的参数移至主(复合)图表,但所有特定于散点图的参数都保留在上面。我们还在组合中添加了一个折线图,它使用 "fake group"基于散点群。

这个假群特别假,但应该足够让你入门了。由于我今天没有时间学习数学,所以我就假设第一个点和最后一个点是回归:

function regressionGroup(group) {
  return {
    all: function() {
      var _all = group.all();
      var first, last;
      for(var i=0; i < _all.length; ++i) {
        var key = _all[i].key;
        if(!isNaN(key[0]) && !isNaN(key[1])) {
          var kv = {key: key[0], value: key[1]};
          if(!first)
            first = kv;
          last = kv;
        }
      }
      return [first, last];
    }
  };
}

与所有假组一样,我们的想法是在图表要求时(而且很快)根据另一个组计算一些类似组的数据。这里的计算不是很有趣,因为你知道如何计算回归而我不知道。您需要用实际计算替换 firstlast 以及 for 循环;这一切所做的就是检查有效点并保留它找到的第一个和最后一个点。

有趣的是,散点图采用键包含 x 和 y 坐标的数据,但折线图采用键为 x、值为 y 的数据。这就是为什么我们有转换 kv = {key: key[0], value: key[1]}

后记

请注意,如果将回归指导点放在域之外,您将遇到 dc.js 错误 - the stack mixin is too aggressive about clipping points to the domain .有一个简单、丑陋的解决方法似乎在这种情况下有效:告诉折线图它有一个有序的 x 刻度,即使它没有:

var composite = dc.compositeChart("#composite"),
  lineChart;
composite   
    .width(600)
  // ...
  .compose([
  // ...
  lineChart = dc.lineChart(composite)
  .group(regressionGroup(scatterGroup))
])
lineChart.isOrdinal = d3.functor(true);

呸!但它有效!这个 hack 可能只适用于复合内部!

https://jsfiddle.net/gordonwoodhull/5tpcxov1/12/

关于d3.js - dc.js 使用 crossfilter 将回归图合并到现有的散点图中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42892232/

相关文章:

dc.js - Crossfilter 维度和组以过滤掉低于特定阈值的数据

javascript - dc-js 禁止在单击饼图时选择切片

javascript - 为什么文本不出现在 svg 矩形内

javascript - 更改 dc.js 折线图中的数据组并重新绘制它

javascript - filterAll() 和 dc.redrawAll() 不适用于 dc.pieChart

dc.js - 使用 dc.js 和 crossfilter 的面积图

javascript - 如何使用 dc.js 创建单行堆叠行图表?

javascript - d3 将文本和圆圈视为一个对象

javascript - D3 map 投影不显示 map

javascript - 未捕获的 TypeError : xScale. rangeBand 不是 d3.js 中的函数