问题
I'm new to dc.js and trying to implement a something like the "Monthly Index Abs Move" graph in the demo at https://dc-js.github.io/dc.js/ (see document source at https://dc-js.github.io/dc.js/docs/stock.html).
ie. I'm trying to implement a line chart for "zoom in" view with a bar chart for the "zoomed out" view (rangeChart).
My problem is that when I filter a date range (eg. by using the "brushOn" the bar chart) then the bars that are filtered out disappear
The demo has this working correctly - the bars outside the date range are gray and those within the date range are blue - see screenshots.
I'm using the css file used in the demo, and I'm using very similar code (see code below), so I'm not sure why this difference.
var maxDate = new Date(1985, 0, 1);
var minDate = new Date(2200, 12, 31);
events.forEach(function (d) {
d.created = new Date(d.created);
//d.last_modified = new Date(d.last_modified);
d.hour = d3.time.hour(d.created); // precaclculate for performance
d.day = d3.time.day(d.created);
if (d.created > maxDate) {
maxDate = d.created;
}
if (d.created < minDate) {
minDate = d.created;
}
});
var ndx = crossfilter(events);
var dateDimension = ndx.dimension(dc.pluck('created'));
var chatHourDim = ndx.dimension(dc.pluck('hour'));
var chatDayDim = ndx.dimension(dc.pluck('day'));
var chatsPerHourGroup = chatHourDim.group().reduceCount();
var chatsPerDayGroup = chatDayDim.group().reduceCount();
visitorsPerHour /* dc.lineChart('#visitors-count', 'chartGroup'); */
.renderArea(true)
.width(900)
.height(200)
.transitionDuration(10)
.margins({top: 30, right: 40, bottom: 25, left: 40})
.dimension(chatHourDim)
.mouseZoomable(true)
// Specify a “range chart” to link its brush extent with the zoom of the current “focus chart”.
.rangeChart(visitorsPerDay)
.x(d3.time.scale().domain([minDate, maxDate]))
.round(d3.time.hour.round)
.xUnits(d3.time.hours)
.elasticY(true)
.renderHorizontalGridLines(true)
.legend(dc.legend().x(650).y(10).itemHeight(13).gap(5))
.brushOn(false)
.group(chatsPerHourGroup, 'Chat events per hour')
.title(function (d) {
var value = d.value;
if (isNaN(value)) {
value = 0;
}
return dateFormat(d.key) + '\n' + value + " chat events";
});
// dc.barChart("visitors-count-per-day", 'chartGroup');
visitorsPerDay.width(900)
.height(40)
.margins({top: 0, right: 50, bottom: 20, left: 40})
.dimension(chatDayDim)
.group(chatsPerDayGroup)
// .centerBar(true)
.gap(1)
.brushOn(true)
.x(d3.time.scale().domain([minDate, maxDate]))
.round(d3.time.day.round)
.alwaysUseRounding(true)
.xUnits(d3.time.days);
回答1:
The way dc.js and crossfilter ordinarily support this functionality is that a crossfilter group does not observe its own dimension's filters.
The range chart example in the stock example uses the same dimension for both charts (moveMonths
). So, when the focus chart is zoomed to the selected range in the range chart, it does filter the data for all the other charts (which you want), but it does not filter the range chart.
If you want to use different dimensions for the two charts, I can see a couple ways to get around this.
Using a fake group
Perhaps the easiest thing to do is snapshot the data and disconnect the range chart from later filters, using a fake group:
function snapshot_group(group) {
// will get evaluated immediately when the charts are initializing
var _all = group.all().map(function(kv) {
// don't just copy the array, copy the objects inside, because they may change
return {key: kv.key, value: kv.value};
});
return {
all: function() { return _all; }
};
}
visitorsPerDay
.group(snapshot_group(chatsPerDayGroup))
However, the range chart also won't respond to filters on other charts, and you probably want it to.
Same dimension, different groups
So arguably the more correct thing is to use only one time dimension for both the focus and range charts, although it kills the optimization you were trying to do on binning. A group optionally takes its own accessor, which takes the dimension key and produces its own key, which must preserve the ordering.
Seems like it was probably designed for exactly this purpose:
var dateDimension = ndx.dimension(dc.pluck('created'));
var chatsPerHourGroup = dateDimension.group(function(d) {
return d3.time.hour(d);
}).reduceCount();
var chatsPerDayGroup = dateDimension.group(function(d) {
return d3.time.day(d);
}).reduceCount();
visitorsPerHour /* dc.lineChart('#visitors-count', 'chartGroup'); */
.dimension(dateDimension)
.group(chatsPerHourGroup, 'Chat events per hour')
visitorsPerDay.width(900)
.dimension(dateDimension)
.group(chatsPerDayGroup)
I don't know if you'll notice a slowdown. Yes, JavaScript date objects are slow, but this shouldn't be an issue unless you are converting tens or hundreds of thousands of dates. It's usually DOM elements that are the bottleneck in d3/dc, not anything on the JavaScript side.
来源:https://stackoverflow.com/questions/47750837/dc-js-rangechart-bars-disappearing-when-filtered-out