Composite chart from filtered dimension data

223 views
Skip to first unread message

Frank Mullen

unread,
Jan 31, 2017, 8:20:26 AM1/31/17
to dc-js user group
Hi, I'm trying to make a composite line chart for the finishing positions of 2 sports teams over the course of a few seasons, with 'Season' on the x-axis and 'Position'. The data looks like this:

var data = crossfilter([
{"Team": "Team A", "Season": 2014, "OverallPosition": 5},
{"Team": "Team A", "Season": 2015, "OverallPosition": 3},
{"Team": "Team A", "Season": 2016, "OverallPosition": 1},
{"Team": "Team B", "Season": 2014, "OverallPosition": 4},
{"Team": "Team B", "Season": 2015, "OverallPosition": 2},
{"Team": "Team B", "Season": 2016, "OverallPosition": 6},
])

I can do it if I separate the data into 2 datasets and then create 2 separate dimensions on 'Season' and use '.group().reduceSum(dc.pluck('OverallPosition'))' on each dimension. However, that won't work off the single dataset, because there are 2 "OverallPosition" results for each Season.

Ideally I want to filter the 'Season' dimension by 'Team', but I'm not sure if Crossfilter works like this. One solution seems to be to create a fake group but I can't see how to apply the 'Team' filter to the 'Season' dimension.

I can do a dimension on "Team" and then filter by "Team A" or "Team B", but then I can't create the chart on that dimension - it has to be on the Season dimension with Season on the x-axis and OverallPosition on the Y axis.

I'd greatly appreciate if somebody could point me in the right direction!

Thanks a lot.

Frank

Gordon Woodhull

unread,
Jan 31, 2017, 9:30:19 AM1/31/17
to dc.js user group
There are a few ways to do this (as usual) but the most straightforward approach IMO is this one:

This will produce a group where the keys are Seasons and the values are {"Team A": <position>, "Team B": <position>}

Then you'll use the valueAccessor on each child chart to pull Team A into one and Team B into the other.

Looks like this is equivalent to this SO answer I gave yesterday:



--
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/a9fcd23d-752c-43da-8ae9-5d5a3a25b986%40googlegroups.com.

Frank Mullen

unread,
Feb 2, 2017, 6:37:50 AM2/2/17
to dc-js user group
Thank you very much Gordon...

Frank Mullen

unread,
Feb 3, 2017, 8:36:56 AM2/3/17
to dc-js user group

Hi again Gordon

I have the first part working and have successfully created a group with the keys as Seasons and values as {"TeamA":<position>, "Team B": <position>}

Now I'm trying to create the composite chart, and am referring to the SO answer you linked to above, in particular this part:
----

Assume we have an array names.

compositeChart.shareTitle(false);
compositeChart.compose(
    names.map(function(name) {
        return dc.lineChart(compositeChart)
            .dimension(writtenDimension)
            .colors('red')
            .group(hoursSumGroup)
            .valueAccessor(function(kv) {
                return kv.value[name];
            })
            .title(function(kv) {
                return name + ' ' + kv.key + ': ' + kv.value;
            });
    }));
----
The only bit I don't understand is where the array 'names' comes from. My group is 'positions', and I tried to create an array called names using: var names = positions.all().map(function(d) {return d.value.key}, but that didn't work.

Thanks for your help, much appreciated

Frank

Gordon Woodhull

unread,
Feb 3, 2017, 9:26:20 AM2/3/17
to dc-js-us...@googlegroups.com
I'd just put the input array for crossfilter into its own variable (there's usually other preprocessing to be done) and then

var names = array.map(function(row) { return row.Team; });

There's lots of ways to do it but that seems the simplest. (Maybe call it teams instead of names too.)

Frank Mullen

unread,
Feb 3, 2017, 5:55:10 PM2/3/17
to dc-js user group
That's brilliant Gordon, it works perfectly. Thanks again. The only extra change I made was to include a function to make the lines different colours. My code is here for anyone who wants to see it working:

queue()
.defer(d3.json, "/livmanu/data")
.await(makeGraphs);

function makeGraphs(error, json_results) {

//Create a Crossfilter instance
var results = crossfilter(json_results);

//Define Dimensions
var yearDim = results.dimension(function(d) {
return d["Season"];
});

//Create array of objects with finishing positions for both teams by year
var positions = yearDim.group().reduce(
function(p, v) { // add
p[v["Team"]] = (p[v["Team"]] || 0) + v["OverallPosition"];
return p;
},
function(p, v) { // remove
p[v["Team"]] -= v["OverallPosition"];
return p;
},
function() { // initial
return {};
});

//Chart
var composite = dc.compositeChart("#time-chart");

//Number formatting for x-axis
var xTicks = d3.format(".0f");

//Create array of team names
var names = json_results.map(function(row) { return row.Team; });

composite
.width(800)
.height(200)
.margins({top: 10, right: 50, bottom: 30, left: 50})
.x(d3.time.scale().domain([1894, 2016]))
.y(d3.scale.linear().domain([44,0]))
.shareTitle(false)
.compose(
names.map(function(name) {
return dc.lineChart(composite)
.dimension(yearDim)
.colors(function() {
if (name == "Liverpool") {
return "red";
} else {
return "black";
}
})
.group(positions)

.valueAccessor(function(kv) {
return kv.value[name];
})
.title(function(kv) {
return name + ' ' + kv.key + ': ' + kv.value;
});
}))
       .xAxisLabel("Season")
.yAxisLabel("League Position")
.xAxis().tickFormat(xTicks);


dc.renderAll();

}
Reply all
Reply to author
Forward
0 new messages