How to add multiple continuous ranges on a dimension

150 views
Skip to first unread message

getk...@gmail.com

unread,
Dec 28, 2016, 9:54:51 AM12/28/16
to dc-js user group
Does dc-js support multiple range brushes/filters on a single dimension for bar chart (or any, for that matter)?  

For example, on a dimension that has x-values from 0 to 100, I'm looking to be able execute and visualize (through brushes) the following filter: 15-30 OR 60-75.

I would try to work around this using two separate charts on redundant dimension, but my understanding is that, at best, it would just AND together the information, causing zero values to pass.

If it's not currently supported by crossfilter or dc-js, I'll gladly make the mods and submit PR's if someone can steer me in the right direction.

Thanks!

Gordon Woodhull

unread,
Dec 28, 2016, 1:15:48 PM12/28/16
to dc.js user group
That's an interesting question. No, currently dc.js does not support multiple brushes but it would be interesting to support that in the future.

I think if you poke into the code you'll find a lot of places where there is exactly one d3.brush UI object, which might not be hard to fix. There are also many, many places where the code assumes there is exactly one filter.

It looks like the default filter handler does correctly fall back to a filterFunction for this case. But it's all the places in the coordinateGridMixin and the concrete charts that I'd worry about. If you grep for filter or filters in the code you'll see. (If you call .filter() as a getter, it returns the first of the filters; .filters() returns the whole array.)

I think you'll find that everything with a continuous / quantitative scale is expecting a single filter, and anything which supports a categorical scale (bars, pie) can handle either a single range filter or a set of individual values. The base mixin code probably, mostly is agnostic about the number and types of filters.

I definitely encourage you to try it out and report back here or in github issues with any problems you run into. It might not be hard to get one individual chart working, and I'd accept a PR as long as it doesn't break other charts in the process. But a lot of brush code is in the coordinate grid mixin, so you'll definitely have to work with that too.

Crossfilter is much more efficient when using single filterRange than filterFunction. So we have special cases in the default filter handler:

    var _filterHandler = function (dimension, filters) {
        if (filters.length === 0) {
            dimension.filter(null);
        } else if (filters.length === 1 && !filters[0].isFiltered) {
            // single value and not a function-based filter
            dimension.filterExact(filters[0]);
        } else if (filters.length === 1 && filters[0].filterType === 'RangedFilter') {
            // single range-based filter
            dimension.filterRange(filters[0]);
        } else {
            dimension.filterFunction(function (d) {
                for (var i = 0; i < filters.length; i++) {
                    var filter = filters[i];
                    if (filter.isFiltered && filter.isFiltered(d)) {
                        return true;
                    } else if (filter <= d && filter >= d) {
                        return true;
                    }
                }
                return false;
            });
        }
        return filters;
    };


Kind of a rambling response, sorry. I love talking about the design of dc.js. You can find me on Gitter too.



--
You received this message because you are subscribed to the Google Groups "dc-js user group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dc-js-user-gro...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/dc-js-user-group/77a6e72c-2cb4-406c-878e-937c623fdc05%40googlegroups.com.

getk...@gmail.com

unread,
Dec 28, 2016, 5:42:17 PM12/28/16
to dc-js user group
Thanks for the fast and thorough (and slightly terrifying, after looking through the occurrences of filter in the code, as you suggested) response.  It sounds like falling back to a filterFunction is kind of the worst-case performance scenario, so I'll try to avoid that.  I'll start in crossfilter's filter/bisect code to see if I can make a filterRange with an array of ranges function efficiently and work my way out to the filter array and brushes from there.  Thanks again!  

Gordon Woodhull

unread,
Dec 28, 2016, 6:13:35 PM12/28/16
to dc-js-us...@googlegroups.com
The performance may not matter in practice. It really depends on how big your data is and how complex. You should try it out with your data before meddling with crossfilter, which is... intense code. A few of us do maintain a community fork of crossfilter, so if you figure that part out that's awesome and very welcome.

But if it's "just" thousands of rows of data and not hundreds of thousands, you might not notice. For a long time dc.js was using only filterFunction and most people were fine with it.

Personally I'd start with just (say) the bar chart and coordinate grid mixin, see how hard it is to do multiple brushes with one chart. But I'm a UI guy so that's always my take.

Ethan Jewett

unread,
Dec 29, 2016, 12:47:59 PM12/29/16
to dc-js-us...@googlegroups.com
I can confirm, using filterFunction usually works fine. We did something like this for Palladio’s timeline and based on the learning from that, one thing I would mention is that you’ll probably not want to try to use multiple d3.v3 brushes together. They won’t pick up mouse events properly, if I remember correctly. You may want to look at https://github.com/humanitiesplusdesign/d3.svg.multibrush as an example of how we did it (modified version of the brush in d3.v3 - see the license file).

That said, if filterFunction is a no-go due to data volumes and response times, we are interested in supporting more complex types of filters directly in Crossfilter. If you end up going that route, we can move the discussion over there, but my advice is the same as Gordon’s: avoid this if possible :-)

Ethan

To unsubscribe from this group and stop receiving emails from it, send an email to dc-js-user-group+unsubscribe@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "dc-js user group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dc-js-user-group+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/dc-js-user-group/ACDCFC62-216C-4147-8853-1933190C798F%40woodhull.com.

Reply all
Reply to author
Forward
0 new messages