dc.js - rangeChart bars disappearing when filtered out

三世轮回 提交于 2019-12-08 05:38:00

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!