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

时间:2017-03-19 21:03:28

标签: d3.js dc.js crossfilter

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

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

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

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

2 个答案:

答案 0 :(得分:3)

包含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]}

的原因

在这里小提琴:http://jsfiddle.net/gordonwoodhull/m8rhexoa/8/

后记

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

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

呸!但它的确有效!这个hack可能只适用于复合材料!

http://jsfiddle.net/gordonwoodhull/5tpcxov1/3/

答案 1 :(得分:0)

我有一个完整的回归函数示例。当我来到这里寻求帮助时,我正是在这样做,发现了您的问题。它需要regression.jshere)。

这是在戈登(Gordon)提出的“假小组”的极好建议之后的,该小组实际上应称为内联小组,直接小组或即时小组。这是我的:

function myRegressionGroup(group, min, max, filter = false) {
  return {
    all: function() {
      var _all = group.all();
      var first, last;
      if(filter) reg = regression.linear(_all.filter(function(k,v) {if(k.key[0]) return k.key}).map((k,v) => k.key));
      else reg = regression.linear(_all.map((k,v) => k.key));
      first = reg.predict(min);
      last = reg.predict(max)
      return [{key:first[0], value: first[1]}, {key: last[0], value: last[1]}]
    }
  };
}

请注意,此函数需要一个交叉过滤器组以及x刻度中的minmax。由于通常需要为xScale计算这些值,因此只需在此处重新使用它们即可。这是因为该函数使用predict方法的极值来计算回归线的两个点。

可选的filter数据争用器可让您决定是否删除x上的空值。

@Gordon,该如何做才能将回归示例包括在Examples of using dc.js中?