使用 dc.js 的可编辑散点图
editable scatter chart using dc.js
什么是制作散点图的好方法,可以通过单击操作在散点图中编辑数据?
我们的想法是发现图中数据中的异常值并过滤图中的值,而不是必须更改源数据。
更好的方法是从交叉过滤器中删除数据,但仅过滤的解决方案是可以接受的。
我想出了一个使用当前 dc.js (beta 32) 的解决方案。
它不支持画笔(需要 .brushOn(false)
)- 我会在增强请求中解释为什么这需要对 dc.js 进行一些更改。
但它确实支持点击点来切换它们,以及重置 link。 (点击背景重置也是可以的,这里没有实现。)
我们要做的是使用标准 dc.js 过滤器签名定义我们自己的 ExcludePointsFilter
:
function compare_point(p1, p2) {
return p1[0] === p2[0] && p1[1] === p2[1];
}
function has_point(points, point) {
return points.some(function(p) {
return compare_point(point, p);
});
}
function ExcludePointsFilter(points) {
var points2 = points.slice(0);
points2.filterType = 'ExcludePointsFilter';
points2.isFiltered = function(k) {
return !has_point(points2, k);
};
return points2;
}
每次单击一个点时,我们都会计算一组新的点,并替换过滤器:
scatterPlot.on('pretransition.exclude-dots', function() { #1
// toggle exclusion on click
scatterPlot.selectAll('path.symbol') #2
.style('cursor', 'pointer') // #3
.on('click.exclude-dots', function(d) { // #4
var p = [d.key[0],d.key[1]];
// rebuild the filter #5
var points = scatterPlot.filter() || [];
if(has_point(points, p))
points = points.filter(function(p2) {
return !compare_point(p2, p);
});
else
points.push(p);
// bypass scatterPlot.filter, which will try to change
// it into a RangedTwoDimensionalFilter #6
scatterPlot.__filter(null)
.__filter(ExcludePointsFilter(points));
scatterPlot.redrawGroup();
});
});
解释:
- 每次渲染或重绘图表时,我们都会在任何转换开始之前对其进行注释
- Select所有的点,也就是
path
个元素加上symbol
class
- 设置合适的光标(指针手可能不理想,但有aren't too many to choose from)
- 为每个点设置一个点击处理程序 - 使用
exclude-dots
事件命名空间以确保我们不会干扰其他任何人。
- 获取当前过滤器或启动一个新过滤器。查看当前点击的点(传递为
d
)是否在该数组中,并根据需要添加或删除它。
- 替换散点图的当前过滤器。由于散点图与
RangedTwoDimensionalFilter
紧密结合,我们需要绕过它的 filter
覆盖(以及 coordinateGridMixin
覆盖!)并一直走到 baseMixin.filter()
。是的,这很奇怪。
为了更好的衡量,我们还将更换过滤器打印机,它通常不知道如何处理点数组:
scatterPlot.filterPrinter(function(filters) {
// filters will contain 1 or 0 elements (top map/join is just for safety)
return filters.map(function(filter) {
// filter is itself an array of points
return filter.map(function(p) {
return '[' + p.map(dc.utils.printSingleValue).join(',') + ']';
}).join(',');
}).join(',');
});
这里是 fiddle 中的一个工作示例:http://jsfiddle.net/gordonwoodhull/3y72o0g8/16/
请注意,如果您随后想对排除的点执行某些操作,您可以从 scatterPlot.filter()
中读取它们 - 过滤器是带有一些注释的点数组。您甚至可以反转过滤器,然后调用 crossfilter.remove(),但我会把它留作练习。
什么是制作散点图的好方法,可以通过单击操作在散点图中编辑数据?
我们的想法是发现图中数据中的异常值并过滤图中的值,而不是必须更改源数据。
更好的方法是从交叉过滤器中删除数据,但仅过滤的解决方案是可以接受的。
我想出了一个使用当前 dc.js (beta 32) 的解决方案。
它不支持画笔(需要 .brushOn(false)
)- 我会在增强请求中解释为什么这需要对 dc.js 进行一些更改。
但它确实支持点击点来切换它们,以及重置 link。 (点击背景重置也是可以的,这里没有实现。)
我们要做的是使用标准 dc.js 过滤器签名定义我们自己的 ExcludePointsFilter
:
function compare_point(p1, p2) {
return p1[0] === p2[0] && p1[1] === p2[1];
}
function has_point(points, point) {
return points.some(function(p) {
return compare_point(point, p);
});
}
function ExcludePointsFilter(points) {
var points2 = points.slice(0);
points2.filterType = 'ExcludePointsFilter';
points2.isFiltered = function(k) {
return !has_point(points2, k);
};
return points2;
}
每次单击一个点时,我们都会计算一组新的点,并替换过滤器:
scatterPlot.on('pretransition.exclude-dots', function() { #1
// toggle exclusion on click
scatterPlot.selectAll('path.symbol') #2
.style('cursor', 'pointer') // #3
.on('click.exclude-dots', function(d) { // #4
var p = [d.key[0],d.key[1]];
// rebuild the filter #5
var points = scatterPlot.filter() || [];
if(has_point(points, p))
points = points.filter(function(p2) {
return !compare_point(p2, p);
});
else
points.push(p);
// bypass scatterPlot.filter, which will try to change
// it into a RangedTwoDimensionalFilter #6
scatterPlot.__filter(null)
.__filter(ExcludePointsFilter(points));
scatterPlot.redrawGroup();
});
});
解释:
- 每次渲染或重绘图表时,我们都会在任何转换开始之前对其进行注释
- Select所有的点,也就是
path
个元素加上symbol
class - 设置合适的光标(指针手可能不理想,但有aren't too many to choose from)
- 为每个点设置一个点击处理程序 - 使用
exclude-dots
事件命名空间以确保我们不会干扰其他任何人。 - 获取当前过滤器或启动一个新过滤器。查看当前点击的点(传递为
d
)是否在该数组中,并根据需要添加或删除它。 - 替换散点图的当前过滤器。由于散点图与
RangedTwoDimensionalFilter
紧密结合,我们需要绕过它的filter
覆盖(以及coordinateGridMixin
覆盖!)并一直走到baseMixin.filter()
。是的,这很奇怪。
为了更好的衡量,我们还将更换过滤器打印机,它通常不知道如何处理点数组:
scatterPlot.filterPrinter(function(filters) {
// filters will contain 1 or 0 elements (top map/join is just for safety)
return filters.map(function(filter) {
// filter is itself an array of points
return filter.map(function(p) {
return '[' + p.map(dc.utils.printSingleValue).join(',') + ']';
}).join(',');
}).join(',');
});
这里是 fiddle 中的一个工作示例:http://jsfiddle.net/gordonwoodhull/3y72o0g8/16/
请注意,如果您随后想对排除的点执行某些操作,您可以从 scatterPlot.filter()
中读取它们 - 过滤器是带有一些注释的点数组。您甚至可以反转过滤器,然后调用 crossfilter.remove(),但我会把它留作练习。