假设我们有以下交叉过滤器/ dc.js应用程序:
虽然这很好,但用户在刷牙时会丢失“参考”填充。我希望图表x
,y
,z
和a
在刷其他图表时保持“基础”柱。也许是这样的另一种颜色:
我相信这可能需要更新dc.renderAll()
函数,但是我什至不知道如何开始。
以下是使用.csv数据作为要点来再现此应用程序的所有代码。
<!DOCTYPE html>
<html lang="en">
<head>
<title>Data Exploration Tool MVP</title>
<meta charset="UTF-8">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"/>
<link rel="stylesheet" href="http://unpkg.com/dc@3/dc.css"/>
<style>
#data-count {
margin-top: 0;
text-align: left;
float: none;
}
table {
table-layout: fixed;
}
td {
width: 1%;
}
</style>
</head>
<body>
<div class="container-fluid" style="margin: 10px;">
<div class="row">
<h2>Data Exploration Tool</h2>
<div class="col-md-3 well well-sm">
<div class="dc-data-count" id="data-count">
<span class="filter-count"></span>
selected out of
<span class="total-count"></span>
points |
<a href="javascript:dc.filterAll(); dc.renderAll();">Reset All</a><br>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<!-- First row of charts -->
<div class="row">
<div class="col-md-3">
<div id="chart-11" style="width:100%;">
<div id="chart-11-title"></div>
<div class="reset" style="visibility: hidden;">range: <span class="filter"></span>
<a href="javascript:chart_11.filterAll();dc.redrawAll();">reset</a>
</div>
</div>
</div>
<div class="col-md-3">
<div id="chart-12" style="width:100%;">
<div id="chart-12-title"></div>
<div class="reset" style="visibility: hidden;">selected: <span class="filter"></span>
<a href="javascript:chart_12.filterAll();dc.redrawAll();">reset</a>
</div>
</div>
</div>
<div class="col-md-3">
<div id="chart-13" style="width:100%;">
<div id="chart-13-title"></div>
<div class="reset" style="visibility: hidden;">selected: <span class="filter"></span>
<a href="javascript:chart_13.filterAll();dc.redrawAll();">reset</a>
</div>
</div>
</div>
<div class="col-md-3">
<div id="chart-14" style="width:100%;">
<div id="chart-14-title"></div>
<div class="reset" style="visibility: hidden;">selected: <span class="filter"></span>
<a href="javascript:chart_14.filterAll();dc.redrawAll();">reset</a>
</div>
</div>
</div>
</div>
<!-- Second row of chart -->
<div class="row">
<div class="col-md-3">
<div id="chart-21" style="width:100%;">
<div id="chart-21-title"></div>
<div class="reset" style="visibility: hidden;">selected: <span class="filter"></span>
<a href="javascript:chart_21.filterAll();dc.redrawAll();">reset</a>
</div>
</div>
</div>
<div class="col-md-3">
<div id="chart-22" style="width:100%;">
<div id="chart-22-title"></div>
<div class="reset" style="visibility: hidden;">range: <span class="filter"></span>
<a href="javascript:chart_22.filterAll();dc.redrawAll();">reset</a>
</div>
</div>
</div>
<div class="col-md-3">
<div id="chart-23"style="width:100%;">
<div id="chart-23-title"></div>
<div class="reset" style="visibility: hidden;">selected: <span class="filter"></span>
<a href="javascript:chart_23.filterAll();dc.redrawAll();">reset</a>
</div>
</div>
</div>
<div class="col-md-3">
<div id="chart-24"style="width:100%;">
<div id="chart-24-title"></div>
<div class="reset" style="visibility: hidden;">selected: <span class="filter"></span>
<a href="javascript:chart_24.filterAll();dc.redrawAll();">reset</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.9.1/d3.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.12/crossfilter.js"></script>
<script src="http://unpkg.com/dc@3/dc.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.js"></script>
<script type="text/javascript">
'use strict';
dc.config.defaultColors(d3.schemeSet1);
var
chart_11 = dc.barChart("#chart-11"),
chart_12 = dc.barChart("#chart-12"),
chart_13 = dc.barChart("#chart-13"),
chart_21 = dc.barChart("#chart-21"),
chart_22 = dc.barChart("#chart-22"),
chart_23 = dc.barChart("#chart-23"),
data_count = dc.dataCount(".dc-data-count");
d3.csv("https://gist.githubusercontent.com/JasonAizkalns/32ece5c815f9ac5d540c41dc0825bbab/raw/362050300ddcb99f195044c00d9f26b0d7d489ca/data.csv").then(function(data) {
var var_names = ["x", "y", "z", "a", "b", "c"];
$("#chart-11-title").append(["<h5>", var_names[0], "<br>Subtitle</h5>"].join(""));
$("#chart-12-title").append(["<h5>", var_names[1], "<br>Subtitle</h5>"].join(""));
$("#chart-13-title").append(["<h5>", var_names[2], "<br>Subtitle</h5>"].join(""));
$("#chart-21-title").append(["<h5>", var_names[3], "<br>Subtitle</h5>"].join(""));
$("#chart-22-title").append(["<h5>", var_names[4], "<br>Subtitle</h5>"].join(""));
$("#chart-23-title").append(["<h5>", var_names[5], "<br>Subtitle</h5>"].join(""));
var c11_bin = 10,
c12_bin = 10,
c13_bin = 500,
c21_bin = 100,
c22_bin = 20,
c23_bin = 1000;
var ndx = crossfilter(data),
chart_11_dim = ndx.dimension(function(d) { return +d[var_names[0]]; }),
chart_12_dim = ndx.dimension(function(d) { return +d[var_names[1]]; }),
chart_13_dim = ndx.dimension(function(d) { return +d[var_names[2]]; }),
chart_21_dim = ndx.dimension(function(d) { return +d[var_names[3]]; }),
chart_22_dim = ndx.dimension(function(d) { return +d[var_names[4]]; }),
chart_23_dim = ndx.dimension(function(d) { return +d[var_names[5]]; }),
chart_11_grp = chart_11_dim.group(function(d) { return Math.floor(d / c11_bin) * c11_bin }).reduceCount(),
chart_12_grp = chart_12_dim.group(function(d) { return Math.floor(d / c12_bin) * c12_bin }).reduceCount(),
chart_13_grp = chart_13_dim.group(function(d) { return Math.floor(d / c13_bin) * c13_bin }).reduceCount(),
chart_21_grp = chart_21_dim.group(function(d) { return Math.floor(d / c21_bin) * c21_bin }).reduceCount(),
chart_22_grp = chart_22_dim.group(function(d) { return Math.floor(d / c22_bin) * c22_bin }).reduceCount(),
chart_23_grp = chart_23_dim.group(function(d) { return Math.floor(d / c23_bin) * c23_bin }).reduceCount();
var all = ndx.groupAll();
data_count.dimension(ndx)
.group(all);
var chart_11_min = +chart_11_dim.bottom(1)[0][var_names[0]],
chart_11_max = +chart_11_dim.top(1)[0][var_names[0]],
chart_12_min = +chart_12_dim.bottom(1)[0][var_names[1]],
chart_12_max = +chart_12_dim.top(1)[0][var_names[1]],
chart_13_min = +chart_13_dim.bottom(1)[0][var_names[2]],
chart_13_max = +chart_13_dim.top(1)[0][var_names[2]],
chart_21_min = +chart_21_dim.bottom(1)[0][var_names[3]],
chart_21_max = +chart_21_dim.top(1)[0][var_names[3]],
chart_22_min = +chart_22_dim.bottom(1)[0][var_names[4]],
chart_22_max = +chart_22_dim.top(1)[0][var_names[4]],
chart_23_min = +chart_23_dim.bottom(1)[0][var_names[5]],
chart_23_max = +chart_23_dim.top(1)[0][var_names[5]];
var breathing_room = 0.05;
chart_11
.dimension(chart_11_dim)
.group(chart_11_grp)
.round(dc.round.floor)
.alwaysUseRounding(true)
.x(d3.scaleLinear().domain([chart_11_min - ((chart_11_max - chart_11_min) * breathing_room), chart_11_max + ((chart_11_max - chart_11_min) * breathing_room)]))
.xUnits(function(start, end, xDomain) { return (end - start) / c11_bin; })
.controlsUseVisibility(true);
chart_12
.dimension(chart_12_dim)
.group(chart_12_grp)
.round(dc.round.floor)
.alwaysUseRounding(true)
.x(d3.scaleLinear().domain([chart_12_min - ((chart_12_max - chart_12_min) * breathing_room), chart_12_max + ((chart_12_max - chart_12_min) * breathing_room)]))
.xUnits(function(start, end, xDomain) { return (end - start) / c12_bin; })
.controlsUseVisibility(true);
chart_13
.dimension(chart_13_dim)
.group(chart_13_grp)
.round(dc.round.floor)
.alwaysUseRounding(true)
.x(d3.scaleLinear().domain([chart_13_min - ((chart_13_max - chart_13_min) * breathing_room), chart_13_max + ((chart_13_max - chart_13_min) * breathing_room)]))
.xUnits(function(start, end, xDomain) { return (end - start) / c13_bin; })
.controlsUseVisibility(true);
chart_21
.dimension(chart_21_dim)
.group(chart_21_grp)
.round(dc.round.floor)
.alwaysUseRounding(true)
.x(d3.scaleLinear().domain([chart_21_min - ((chart_21_max - chart_21_min) * breathing_room), chart_21_max + ((chart_21_max - chart_21_min) * breathing_room)]))
.xUnits(function(start, end, xDomain) { return (end - start) / c21_bin; })
.controlsUseVisibility(true);
chart_22
.dimension(chart_22_dim)
.group(chart_22_grp)
.round(dc.round.floor)
.alwaysUseRounding(true)
.x(d3.scaleLinear().domain([chart_22_min - ((chart_22_max - chart_22_min) * breathing_room), chart_22_max + ((chart_22_max - chart_22_min) * breathing_room)]))
.xUnits(function(start, end, xDomain) { return (end - start) / c22_bin; })
.controlsUseVisibility(true);
chart_23
.dimension(chart_23_dim)
.group(chart_23_grp)
.round(dc.round.floor)
.alwaysUseRounding(true)
.x(d3.scaleLinear().domain([chart_23_min - ((chart_23_max - chart_23_min) * breathing_room), chart_23_max + ((chart_23_max - chart_23_min) * breathing_room)]))
.xUnits(function(start, end, xDomain) { return (end - start) / c23_bin; })
.controlsUseVisibility(true);
dc.renderAll();
});
</script>
</div>
</body>
</html>
答案 0 :(得分:1)
是的,dc.js和crossfilter都不直接支持此用例,不是人们不想要它。
交叉过滤器仅支持一个活动过滤器,当您不应用过滤器时,您将无法找出总数。
编辑:现在有一个official example,其中的代码已针对the follow up code review question进行了清理。下面的讨论仍然很好地描述了该技术,但是示例中的代码重复性较低。
感谢可复制的示例。我put your original code in a fiddle。
这有点乏味,但是获得想要的影响的一种方法是将每个图表更改为两个条形图的composite。我们将第二个子图表设为红色并将其隐藏,直到过滤掉某些内容为止。 (更确切地说,只要未过滤任何内容,我们就会将其隐藏。)
我们将使第一个(蓝色)子图始终显示初始值,不受任何过滤器的影响。
创建合成图表如下所示:
var chart_11 = dc.compositeChart("#chart-11"),
chart_12 = dc.compositeChart("#chart-12"),
chart_13 = dc.compositeChart("#chart-13"),
chart_21 = dc.compositeChart("#chart-21"),
chart_22 = dc.compositeChart("#chart-22"),
chart_23 = dc.compositeChart("#chart-23");
chart_11
.compose([
dc.barChart(chart_11)
.dimension(chart_11_dim)
.group(chart_11_grp_copy)
.controlsUseVisibility(true)
,
dc.barChart(chart_11)
.dimension(chart_11_dim)
.group(chart_11_grp)
.colors('red')
.controlsUseVisibility(true)
.brushOn(false)
]);
(是的,您似乎必须将controlsUseVisibility
应用于每个子图表-看起来像个bug。)
问题的核心是如何使基础的蓝色图表不变。我们可以使用 fake group。想法是该对象的行为类似于组,只是它返回该组在页面加载时的内容:
function static_copy_group(group) {
var all = group.all().map(kv => ({key: kv.key, value: kv.value}));
return {
all: function() {
return all;
}
}
}
var chart_11_grp_copy = static_copy_group(chart_11_grp),
chart_12_grp_copy = static_copy_group(chart_12_grp),
chart_13_grp_copy = static_copy_group(chart_13_grp),
chart_21_grp_copy = static_copy_group(chart_21_grp),
chart_22_grp_copy = static_copy_group(chart_22_grp),
chart_23_grp_copy = static_copy_group(chart_23_grp);
是的,这是group.all()
的深层副本,因为交叉过滤器会更新所有内容。
最后,这是在未过滤任何内容时隐藏第二个图表的代码:
function any_filters() {
return [chart_11, chart_12, chart_13, chart_21, chart_22, chart_23]
.some(chart => chart.filters().length);
}
function hide_second_chart(chart) {
chart.select('.sub._1')
.attr('visibility', any_filters() ? 'visible' : 'hidden')
}
[chart_11, chart_12, chart_13, chart_21, chart_22, chart_23].forEach(function(chart) {
chart.on('pretransition', hide_second_chart)
})