Can I filter data based on an intersection (and) in crossfilter/dc.js?

非 Y 不嫁゛ 提交于 2019-12-01 22:25:32

Thank you @EthanJewett for the comments and crossfilter example in the above comments. I have been search for this off and on for many months and finally decided to concentrate exclusive to find the answer. @Gordon you might also be interested in this example.

I have been able to put together a final dc.js and crossfilter example where you can choose to filter the timepoints based on an intersection (and) or a union (or).

If you select TP-6 and TP-8 and switch between or / and you can see it work. The and option is saying that I am looking for patients that have timepoints at 6 and 8.

I have added an additional field "Gender" and another site "CCC" to the dataset.

Based on Ethan's guidance the key part that I was missing was defining an additional dimension that will be filtered when the timepoint pie chart is begin filtered as an intersection (and).

The timepoint pie chart dimension is defined below. The true argument lets the dimension know that the data is an array:

let tpDimension = ndx.dimension(function (d) {return d.timepoints;}, true);

The extra dimension is also defined as above except we do not break out the array, thus the true argument is not passed. This dimension is only used in the timepoint pie chart filterHandler.

let tp2Dimension = ndx.dimension(function (d) {return d.timepoints;});

The timepoint pie chart filter handler is overridden to use the extra dimension if the user has selected the and option.

tpPie.filterHandler(function (dimension, filters) {
    if (filters.length === 0) {
       // the empty case (no filtering)
       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 {
       // an array of values, or an array of filter objects
       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;
        });
     };

   // -------------------------------------------------------------
   // Custom code to handle intersection filtering (AND)
   if (glbTpFilterOption === 'tpAND') {
       tpIntersectFilter(filters);
   }

   return filters;
});

The actual code to perform the timepoint intersect filter is:

    function tpIntersectFilter(filters) {
        if (filters.length === 1) {
            // Do not perform the intersect test when only 1 filter selected
            tp2Dimension.filterAll();
        } else if (filters.length === 0) {
            // Since nothing is filtered we need to perform an intersect
            // based on all keys.
            tp2Dimension.filter(function (d) {
                uniqueObjs = tpGroup.all();
                for (var i = 0; i < uniqueObjs.length; i++) {
                    if (d.indexOf(uniqueObjs[i].key) == -1) return false;
                }
                return true;
            });
        } else {
            tp2Dimension.filter(function (d) {
                // Since we are looking for the intersection, test all 
                // filters, but once there isn't a match get out.
                for (var i = 0; i < filters.length; i++) {
                    if (d.indexOf(filters[i]) == -1) return false;
                }
                return true;
            });
        }
    }

Finally I need to handle when the user is switching between the options:

$('#tpFilterOptions a').on('click', function () {
    var sel = $(this).data('title');
    var tog = $(this).data('toggle');
    $('#' + tog).prop('value', sel);

    $('a[data-toggle="' + tog + '"]').not('[data-title="' + sel + '"]').removeClass('active').addClass('notActive');
    $('a[data-toggle="' + tog + '"][data-title="' + sel + '"]').removeClass('notActive').addClass('active');

    glbTpFilterOption = sel;
    if (sel === "tpOR") {
       tp2Dimension.filterAll();
    } else {
       tpIntersectFilter(tpPie.filters());
    }
    dc.redrawAll();

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