Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
161 views
in Technique[技术] by (71.8m points)

dc.js - create multiple charts from data groups of the same column

How to make this dashboard

enter image description here

from this data

time,group_name,value
15/10/2017 15:36:15,group-1,1
15/10/2017 15:36:15,group-2,1
15/10/2017 15:36:15,group-2,1
15/10/2017 15:36:15,group-2,1
15/10/2017 15:36:16,group-1,1
15/10/2017 15:36:16,group-3,1
15/10/2017 15:36:16,group-1,1
15/10/2017 15:36:16,group-3,1
15/10/2017 15:36:17,group-3,1
15/10/2017 15:36:17,group-3,1
15/10/2017 15:36:17,group-1,1
15/10/2017 15:36:17,group-2,1
15/10/2017 15:36:18,group-1,1
15/10/2017 15:36:18,group-1,1
15/10/2017 15:36:18,group-2,1
15/10/2017 15:36:18,group-1,1
15/10/2017 15:36:19,group-3,1
15/10/2017 15:36:19,group-2,1
15/10/2017 15:36:19,group-2,1
15/10/2017 15:36:19,group-1,1

using dc.js?

Thank you in advance.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You can do this by first dimensioning/grouping by both the time and group columns, then using a fake group to split one group into many.

I don't have access to your data (it's better to paste your data as text when asking a question), so I whipped up an example using data from one of the dc.js examples. The data looks like this:

Expt,Run,Speed
1,1,850
1,2,740
1,3,900
1,4,1070
1,5,930
1,6,850
...
2,1,960
2,2,940
2,3,960
2,4,940
2,5,880
2,6,800

There are two important steps. First we need to create a dimension and group keyed on both the X dimension and the dimension to split by. In this case, we want Run to be the X dimension, and we want to split by Expt. We'll put Run first. The group is normal.

var runExptDim = cf.dimension(function(d) {
  return [d.Run, d.Expt];
});
var runExptGroup = runExptDim.group().reduceSum(function(d) {
  return d.Speed;
});

Now we need a fake group to split the original group by the second key. Here's a function which will allow us to construct fake groups for all the values of Expt:

function split_group(group2d) {
  return {
    subgroup: function(key) {
      return {
        all: function() {
          return group2d.all().filter(function(kv) {
            return kv.key[1] === key;
          }).map(function(kv) {
            return {key: kv.key[0], value: kv.value};
          }).sort(function(a, b) {
            return a.key - b.key;
          })
        }
      };
    }
  };
}

When a subgroup's .all() method is called, it pulls the data from the original group, filters the data using the second part of the key, and removes the second part of the key using .map().

Finally it also has to sort the data because multikeys tend to mess up the ordering, because the data is cast to strings for sorting when you use multikeys.

Grab the subgroups like this:

var splitGroup = split_group(runExptGroup);
var runGroup1 = splitGroup.subgroup(1),
  runGroup2 = splitGroup.subgroup(2),
  runGroup3 = splitGroup.subgroup(3);

Since we've got a strange dimension, it's not clear what dimension to specify to the charts. The simplest thing to do is create another dimension on the X axis:

var runDim = cf.dimension(function(d) {
  return d.Run;
});

However, this will cause the chart to filter itself. I'll probably revisit my answer later and correct this.

A chart initialization looks like this:

var line1 = dc.lineChart('#line1')
  .width(400).height(200)
  .dimension(runExptGroup)
  .group(runGroup1)
  .x(d3.scale.linear())
  .elasticX(true)
  .elasticY(true);

This doesn't cover your styling needs, but you can take a look at the sparkline example for some hints there.

Here's an example fiddle: https://jsfiddle.net/gordonwoodhull/og68rkqz/

Hope this helps!

EDIT: showing totals in number display

To display the totals of each split group, you can create a "fake groupAll" that takes the sum of values:

  function total_group(group) {
    return {
      value: () => d3.sum(group.all(), kv => kv.value)
    };
  }

The number display can take either a groupAll object with .total() method or a regular group, in which case it'll find the max of .all()

Strangely, it needs the identity value accessor when working with a groupAll. Putting it all together:

  var identity = x => x;
  
  var number1 = dc.numberDisplay('#num1')
    .valueAccessor(identity)
    .group(total_group(runGroup1));

Fork of your fiddle: https://jsfiddle.net/gordonwoodhull/aj1m9ysg/1/


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...