Crossfilter divide one dimension by the range selected in another
Sorry the Title is a little vague. I have a data set of events with a date d["d"] and a time d["hour"] field. The time is originally in seconds, but this has been changed server side to 144 10 minute bins.
Now to the problem. I have created two dimensions, a date dimension and a time dimension. I have a barchart of the number of events for each day, and then another barchart for number of events in each 10 minute window during a day. I can see how my events are changing over many months, as well as see what the "average" day looks like.
The issue is the time of day bar chart is simply sums up the number of events in the selected date range. What i really what is the time graph to be the average day for the selected date range, ie divided by the number of days selected in the date barchart.
I have attempted to do this using the reduce functionallity shown below, whilst it works it is not quite what i want.
var ndx = crossfilter(sessiondata);
var dateDim = ndx.dimension(function(d) { return d["d"]; });
var hourDim = ndx.dimension(function(d){return d["hour"];});
// var uidDim = ndx.dimension(function(d) { return d["uid"];});
var numEventsByDate = dateDim.group();
var numEventsByHour = hourDim.group().reduce(
function(p,d){
p.timebin++
if( d.d in p.days) p.days[d.d]++;
else {
p.days[d.d] = 1;
p.dayCount++;}
p.averagetime = p.timebin/p.dayCount
return p;
},
function(p,d){
p.timebin--
p.days[d.d]--;
if(p.days[d.d] == 0){
delete p.days[d.d];
p.dayCount--;}
p.averagetime = p.timebin/p.dayCount
return p;
},
function() {
return{dayCount:0,
days:{},
timebin:0,
averagetime:0};
});
var all = ndx.groupAll();
var minDate = dateDim.bottom(1)[0]["d"];
var maxDate = dateDim.top(1)[0]["d"];
var timeChart = dc.barChart("#figure1");
var hourChart = dc.barChart("#figure3");
timeChart
.width(750)
.height(250)
.margins({top: 10, right: 50, bottom: 30, left: 50})
.dimension(dateDim)
.group(numProjectsByDate)
.transitionDuration(500)
.x(d3.time.scale().domain([minDate, maxDate]))
.xUnits(d3.time.days)
.elasticY(true)
.xAxisLabel("Months")
.yAxis().ticks(4);
hourChart
.width(730)
.height(300)
.margins({top: 10, right: 50, bottom: 30, left: 50})
.dimension(hourDim)
.group(numProjectsByHour)
.valueAccessor(function (d){return d.value.averagetime})
.transitionDuration(500)
.centerBar(true)
.gap(65)
.x(d3.scale.linear().domain([0,24]))
.xUnits(function(){return 10;})
.elasticY(true)
.xAxisLabel("Hours of the Day")
.yAxis().ticks(4);
The issue is that each timebin is divided by a different number of days, ie a period of time that has a low number of events in say 2am-2:10am. This period may have zero events occur in it on some days, and therefore those days of zero will not contribute to the average. What i really what to do is divide all time bins by the same number, which is the number of days selected in the date graph. Is there a simple way of doing this that i am missing
I'd recommend not calculating averages in your group. Just track the components and then calculate the average in your valueAccessor
. When you do things this way, you can also figure out the divisor properly in your value accessor by grabbing the actual filter off of the timeChart and using that to divide your group totals.
So your valueAccessor
on your hourChart would end up looking something like this:
.valueAccessor(function (d){
var numberOfDays = 100 // Or whatever your unfiltered number of days is.
if(timeChart.filters().length === 1) {
// There is a filter in place
var firstDate = timeChart.filters()[0][0]; // Inclusive
var lastDate = timeChart.filters()[0][1]; // Not inclusive!
// Calculates the number of days between. Adjust +/-1 depending on your needs
var numberOfDays = d3.time.days(firstDate, lastDate).length
}
return d.value.timebin / numberOfDays;
})
Given this you can just make numEventsByHour
a simple count dimension and adjust the valueAccessor
as required based on this.
上一篇: 用交叉过滤器取消场地的所有值?