Reputation: 2315
I want to create DC.js chart with two lines each representing average value by date.
I believe the way to do this is to use DC.js compositeChart
and create two lineCharts to put into it, and then use reducio()
group.reduce(add, remove, initial)
to get the average amounts.
My question is where does the conditional logic go? How/where do I conditionally use this:
var group = dimension.group().reduce(reduceAdd, reduceRemove, reduceInitial);
to calculate average for records with d.PPCC === "PPCC"
and another average for records with d.PPCC === "Other"
?
I have tried two ways neither of which is working for me.
This is first try:
var bounce30DaysPPCC = function(d) { if (d.PPCC === "PPCC")
{ dateDim.group().reduce(reduceAdd, reduceRemove, reduceInitial);
} else { dateDim.group().reduce(0); } };
var bounce30DaysOther = function(d) { if (d.PPCC === "Other")
{ dateDim.group().reduce(reduceAdd, reduceRemove, reduceInitial);
} else { dateDim.group().reduce(0); } };
which produces this error:
dc.js:4017 Uncaught TypeError: layer.group.all is not a function
This is second try:
var bounce30DaysPPCC = dateDim.group().reduce( function(d) { if (d.PPCC === "PPCC")
{ return reduceAdd, reduceRemove, reduceInitial; } else { return 0; }});
var bounce30DaysOther = dateDim.group().reduceSum( function(d) { if (d.PPCC === "Other")
{ return reduceAdd, reduceRemove, reduceInitial; } else { return 0; } });
which also produces an error:
Uncaught TypeError: reduceInitial is not a function
Here is my full javascript code.
<script type="text/javascript">
// get csv data
d3.csv("merged.csv", function(data) {
// get csv data and format date
var parseDate = d3.time.format("%Y-%m-%d").parse;
data.forEach(function(d) {
d.date = parseDate(d.date);
d.sessions = +d.sessions;
d.ad_requests = +d.ad_requests;
d.bounceRate = +d.bounceRate;
d.clicks = +d.clicks;
d.earnings = +d.earnings;
d.newUsers = +d.newUsers;
d.sessionDuration = +d.sessionDuration;
d.sessionsPerUser = +d.sessionsPerUser;
d.twitterSessions = +d.twitterSessions;
d.users = +d.users;
});
// setup crossfilter
var ndx = crossfilter(data);
// create dimension
var dateDim = ndx.dimension(function(d) { return d["date"]; });
// create data groups
var bounce30DaysPPCC = function(d) { if (d.PPCC === "PPCC")
{ dateDim.group().reduce(reduceAdd, reduceRemove, reduceInitial);
} else { dateDim.group().reduce(0); } };
var bounce30DaysOther = function(d) { if (d.PPCC === "Other")
{ dateDim.group().reduce(reduceAdd, reduceRemove, reduceInitial);
} else { dateDim.group().reduce(0); } };
// create min and max dates to limit chart x axis values
var minDateTime = new Date(new Date().setDate(new Date().getDate()-30));
var maxDateTime = new Date(new Date().setDate(new Date().getDate()+1));
// create chart
BounceChart30Day = dc.compositeChart("#bounce-chart-30day");
// create two line charts to put into composite chart
var ppccLine = dc.lineChart(BounceChart30Day)
.group(bounce30DaysPPCC)
var otherLine = dc.lineChart(BounceChart30Day)
.group(bounce30DaysOther)
// create composite chart
BounceChart30Day
.height(50)
.width(450)
.margins({top: 10, right: 10, bottom: 5, left: 35})
.dimension(dateDim)
.transitionDuration(500)
.brushOn(false)
.elasticY(true)
.compose([ppccLine, otherLine])
.x(d3.time.scale().domain([minDateTime, maxDateTime]))
.yAxis().ticks(3);
// create value accessor to calculate averages
BounceChart30Day.valueAccessor(function(p) {
return p.value.count > 0 ? p.value.total / p.value.count : 0; });
// create add, remove, initial reduce functions
function reduceAdd(p, v) {
++p.count;
p.total += v.bounceRate;
return p;
}
function reduceRemove(p, v) {
--p.count;
p.total -= v.bounceRate;
return p;
}
function reduceInitial() {
return {count: 0, total: 0};
}
// render chart
dc.renderAll();
</script>
Upvotes: 1
Views: 664
Reputation: 6010
You need to evaluate the data within your reduce functions. So make your reduce functions look something like this:
// create add, remove, initial reduce functions
function reduceAdd(p, v) {
if (v.PPCC === "PPCC") {
++p.PPCCcount;
p.PPCCtotal += v.bounceRate;
} else if(v.PPCC === "Other") {
++p.Othercount;
p.Othertotal += v.bounceRate;
}
return p;
}
function reduceRemove(p, v) {
if (v.PPCC === "PPCC") {
--p.PPCCcount;
p.PPCCtotal -= v.bounceRate;
} else if(v.PPCC === "Other") {
--p.Othercount;
p.Othertotal -= v.bounceRate;
}
return p;
}
function reduceInitial() {
return { PPCCcount: 0, PPCCtotal: 0, Othercount: 0, Othertotal: 0};
}
Then just create your groups as normal:
var bounce30DaysPPCC = dateDim.group().reduce(reduceAdd, reduceRemove, reduceInitial);
var bounce30DaysOther = dateDim.group().reduceSum(reduceAdd, reduceRemove, reduceInitial);
Everything else looks pretty much OK at first glance. You'll just need to update your valueAccessor
functions to get at the new property names on the groups.
Upvotes: 2