d3.js - 使用不同颜色的 dc.js 显示原始(有条件的)刷过的未刷过的交叉过滤条
问题描述
假设我们有以下 crossfilter / dc.js 应用程序:
虽然这很好,但用户在刷牙时会失去“参考”人口。我想要图表x
, y
, z
, 并a
在刷其他图表时保留“基础”条。也许像这样的不同颜色:
我相信这可能需要更新dc.renderAll()
功能,但我什至不知道如何开始。
以下是使用托管为 gist 的 .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>
解决方案
所以是的,dc.js 和 crossfilter 都没有直接支持这个用例,并不是人们不想要它。
Crossfilter 仅支持一个活动过滤器,并且无法让您找出未应用过滤器时的总数。
编辑:现在有一个官方示例,其中清理了代码以响应后续代码审查问题。下面的讨论仍然很好地描述了该技术,但示例中的代码重复性较低。
感谢您提供可重复的示例。我把你的原始代码放在一个小提琴中。
这有点乏味,但获得所需效果的一种方法是将每个图表更改为两个条形图的组合。我们将第二个子图表设为红色并将其隐藏,直到某些内容被过滤。(更准确地说,只要没有过滤任何内容,我们就会隐藏它。)
我们将使第一个(蓝色)子图始终显示初始值,不受任何过滤器的影响。
创建复合图表如下所示:
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
于每个子图表 - 看起来像一个错误。)
问题的核心是如何让底层的蓝图不发生变化。我们可以为此使用 假组。这个想法是这个对象就像一个组,除了它只返回组在页面加载时的内容:
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)
})
推荐阅读
- python - 使用python在列中返回值而不是按行从文件中打印行
- android - 有没有办法在没有 AVD 信用的情况下模拟移动连接?
- java - 我可以在不破坏向后兼容性的情况下将 Java 中的常量从 int 更改为 byte 吗?
- html - 视差背景不重复
- c++ - 在此示例中如何避免代码重复?
- r - 在 R 上生成一些真正糟糕的图像
- python - 如何在 Python 中编写代码以避免动态内存分配,允许禁用 GC?
- mysql - 将变量与查询结果中的第 n 行进行比较时,MySQL IF 语句的正确语法是什么?
- python - 如何获取opencv中显示的图像坐标
- amazon-web-services - 如何将我的自定义 ADFS 属性映射到 AWS Cognito 用户池属性?