首页 文章

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

提问于
浏览
2

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

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

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

jsFiddle演示

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

    包含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/

  • 3

    我有一个功能齐全的回归示例 . 当我来到这里寻求帮助时,我正是这样做的,我找到了你的问题 . 它需要 regression.jshere) .

    这遵循戈登关于“虚假团体”的出色建议,这应该真正称为内联组,或直接组,甚至是即时组 . 这是我的:

    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-scale中的 minmax . 由于您通常会为xScale计算这些值,因此只需在此处重复使用它们即可 . 这是因为函数使用极值与 predict 方法来计算回归线的两个点 .

    可选的 filter 数据管理器用于决定是否删除x上的空值 .

    @Gordon,如何在Examples of using dc.js中包含我的回归示例?

相关问题