Reduce multiple indirectly-specified fields using crossfilter

我们两清 提交于 2020-05-16 17:54:24

问题


I have a CSV dataset that I am exploiting with dc.js (crossfilter).

Date, Country 1,Country 2,Country 3,Country 4,Country 5,Country 6,Target country (...) 2014/12/11, USA, France, UAE, (...), Iraq

The thing I am trying to do is to plot a row chart with one row per country. Here's my solution as of today:

  var countries = ndx.dimension(function(d) {
    var list = [];
    list.push(d["Country 1"]);
    if (d["Country 2"]) {list.push(d["Country 2"]);};
    if (d["Country 3"]) {list.push(d["Country 3"]);};
    if (d["Country 4"]) {list.push(d["Country 4"]);};
    if (d["Country 5"]) {list.push(d["Country 5"]);};
    if (d["Country 6"]) {list.push(d["Country 6"]);};
    return list;
  });
  var countriesGroup = countries.group().reduceSum(function(d) {
    return d.totalNumberOfStrikes;
  });;
   countryChart
    .width(400).height(500)
    .group(countriesGroup)
    .dimension(countries)
    .ordering(function(d){ return -d.value });

But, as you can see, it doesn't push uniques in the list array. Which causes stupid results, as each combination of countries in the CSV rows creates a new item in the list.

What I want is to have a list containing each unique country, and then plot the thing in the row chart.

Can you help? Thank you very much!


回答1:


Based on later conversation in another question and the dc.js users group, here's a better reduction that keeps the data as it is:

var strikingCountriesGroup = xScaleDimension.group().reduce(
    function(p, v) { // add
        countryFields.forEach(function(c) {
            if(v[c]) p[v[c]] = (p[v[c]] || 0) + v.totalNumberOfStrikes;
        });
        return p;
    },
    function(p, v) { // remove
        countryFields.forEach(function(c) {
            if(v[c]) p[v[c]] = p[v[c]] - v.totalNumberOfStrikes;
        });
        return p;
    },
    function() { // initial
        return {};
    }
);

Although that may look like a big tangle of brackets, the idea is that the fields v[c], where c is "Country 1", "Country 2"... in the original data set, indirectly specify the fields that you want to create in the reduction.

We are reducing into the map p from the value v. We loop over the country fields, and for each c, if v has an entry for c, we add or subtract v.totalNumberOfStrikes from p[v[c]]. We have to be careful if the value doesn't already exist: the expression || 0 defaults a value to zero if it is undefined.

Then, we can create the stacks dynamically like this (sorting by value):

  var reducedCountries = strikingCountriesGroup.all()[0].value;
  var countries = d3.keys(reducedCountries).sort(function(a, b) {
      return reducedCountries[b] - reducedCountries[a];   
  });

  // we have to special-case the first group, see https://github.com/dc-js/dc.js/issues/797
  var first = countries.shift();
  strikingCountries
      .group(strikingCountriesGroup, first, 
         function(d) { 
             return d.value[first];
         });
  // rest
  countries.forEach(function(c) {    
      strikingCountries
          .stack(strikingCountriesGroup, c, 
             function(d) { 
                 return d.value[c];
             });
  });

Fiddle here: http://jsfiddle.net/gordonwoodhull/gfe04je9/11/




回答2:


Probably the easiest way to do this is to flatten your array, so you just have Date, Country, Target in your source. Something like (untested):

var dest = [];
var countries = ["Country 1", "Country 2", ...]
source.forEach(function(d) {
    countries.forEach(function(c) {
        dest.push({Date: d.Date, Country: c, Target: d.Target});
    });
});

And then pass dest to crossfilter instead of your original data.

The advantage of doing it this way is that now when you click on rows in the chart, you can filter the rest of the charts by an individual country. Since crossfilter only filters by row, there is no other way (without serious trickery) to filter by individual country without inadvertently filtering other countries that share those rows.



来源:https://stackoverflow.com/questions/27348262/reduce-multiple-indirectly-specified-fields-using-crossfilter

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