1

I'm trying to learn d3 via dc.js and I'm quite stuck trying to figure out how to group the line chart with only the w15sec, w30sec,...,w1hr, names and values. When the filter is applied, I'd like it show only the max value for workouts that are within the filter parameters. Here is my jsfiddle.

I've got flat cycling data that looks like this:

var data = [{"TimeStamp":"2017-09-06T12:32:04.183","Duration":3459.518,"Distance":10261,"ActivityID":175508086,"AVGPower":305.5419317525,"w15sec":499.2666666667,"w30sec":479.3333333333,"w1min":470.2666666667,"w2min":441.9416666667,"w5min":417.5166666667,"w10min":410.5616666667,"w20min":342.3141666667,"w40min":306.2033333333,"w1hr":0.0},{"TimeStamp":"2017-09-08T12:07:27.033","Duration":2106.755,"Distance":3152,"ActivityID":175647595,"AVGPower":168.8485158649,"w15sec":375.8666666667,"w30sec":327.7333333333,"w1min":271.1833333333,"w2min":261.6083333333,"w5min":0.0,"w10min":0.0,"w20min":0.0,"w40min":0.0,"w1hr":0.0},{"TimeStamp":"2017-09-07T17:11:51.577","Duration":1792.025,"Distance":4245,"ActivityID":175670859,"AVGPower":244.2495803022,"w15sec":365.2,"w30sec":342.1333333333,"w1min":328.0333333333,"w2min":290.975,"w5min":276.0566666667,"w10min":268.8316666667,"w20min":246.8858333333,"w40min":0.0,"w1hr":0.0},{"TimeStamp":"2017-09-09T10:31:21.107","Duration":15927.885,"Distance":39408,"ActivityID":175971583,"AVGPower":255.0849193803,"w15sec":405.0666666667,"w30sec":389.8666666667,"w1min":367.6666666667,"w2min":350.3916666667,"w5min":318.93,"w10min":300.345,"w20min":281.9883333333,"w40min":259.4733333333,"w1hr":0.0}];

The goal is to have the chart on the right populated with the names of the categories (w15sec, w30sec,...,w1hr) as the dimensions and the values would be the max found in the activities for each category. It looks like a line chart going from high values (w15sec) to lower values (w1hr).

It should look something like this image.

enter image description here

Thanks so much for your help!

G A S
  • 23
  • 5
  • You should probably start by switching to Crossfilter 1.4+ and using the array dimension concept. I believe this would let you group based on rmm window. Then it is a matter of doing the max calculation, which requires a custom group, or use Reductio for the max reducer. Update your fiddle to Crossfilter 1.4+ and I'll be happy to take a look! – Ethan Jewett Sep 13 '17 at 09:31
  • Thanks! Here's the updated fiddle with Crossfilter 1.4.1. http://jsfiddle.net/gasteps/fb3wsyck/3/ – G A S Sep 13 '17 at 16:40

1 Answers1

1

I think the best way to approach this is to use Reductio's multi-value group and maximum reducer to calculate the maximum for each window on your power curve in a single bucket, then create a fake group to make it appear that each of these windows is its own group "bucket".

You start by defining your dimension, some helper maps (you need to get onto a linear scale, so you need to convert your windows to numbers of seconds), and a helper dimension that you can use for filtering in the event that you want to do this:

var rmmDim = facts.dimension(function(d) {
    return true;
});

var timeMap = {
    "w15sec": 15,
  "w30sec": 30,
  "w1min": 60,
  "w2min": 120,
  "w5min": 300,
  "w10min": 600,
  "w20min": 1200,
  "w40min": 2400,
  "w1hr": 3600
}

var timeArray = ["w15sec","w30sec","w1min","w2min","w5min","w10min","w20min","w40min","w1hr"].map((d) => timeMap[d])

var rmmFilterDim = facts.dimension(function(d) {
    return timeArray;
}, true)

You then create your group using Reductio, calculating the max for each window:

var rmmGroup = rmmDim.group();
var reducer = reductio()
reducer.value('w15sec')
  .max((d) => { return d.w15sec; })
reducer.value('w30sec')
  .max((d) => { return d.w30sec; })
reducer.value('w1min')
  .max((d) => { return d.w1min; })
reducer.value('w2min')
  .max((d) => { return d.w2min; })
reducer.value('w5min')
  .max((d) => { return d.w5min; })
reducer.value('w10min')
  .max((d) => { return d.w10min; })
reducer.value('w20min')
  .max((d) => { return d.w20min; })
reducer.value('w40min')
  .max((d) => { return d.w40min; })
reducer.value('w1hr')
  .max((d) => { return d.w1hr; })

reducer(rmmGroup)

And finally you create your fake group. You need both top and all because the line chart requires them for some reason:

var fakeGroup = {
    all: function() {
    return ["w15sec","w30sec","w1min","w2min","w5min","w10min","w20min","w40min","w1hr"].map((d) => {
        return {
        key: timeMap[d],
        value: rmmGroup.top(Infinity)[0].value[d]
      }
    })
  },
  top: function() {
    return ["w15sec","w30sec","w1min","w2min","w5min","w10min","w20min","w40min","w1hr"].map((d) => {
        return {
        key: timeMap[d],
        value: rmmGroup.top(Infinity)[0].value[d]
      }
    })
  }
}

Then you can use this fake group in your power distribution chart:

PwrDistChart
  .width(960)
  .height(150)
  .margins({
    top: 10,
    right: 10,
    bottom: 20,
    left: 40
  })
  .dimension(rmmFilterDim)
  .group(fakeGroup)
  .valueAccessor((d) => {
    return d.value.max
  })
  .transitionDuration(500)
  .x(d3.scale.linear().domain([0,3600]))
  .elasticY(true)

Here is an updated version of the fiddle with all of this working: http://jsfiddle.net/fb3wsyck/5/

Ethan Jewett
  • 6,002
  • 16
  • 25
  • This is really excellent! I've made it way further up the learning curve just by going through what you've done. Thanks! I've up-voted but without rep score > 15, it won't show on the page. – G A S Sep 15 '17 at 20:54