Create a running sum graph in dc.js - sum

I am trying to create a running sum in crossfilter to use with dc.js.
I have a set of records like the following :
records = [{"date":"2014-01-01","field1":"value1","field2":"value11","value_field":-20},
{"date":"2014-01-02","field1":"value2","field2":"value12","value_field":100},
{"date":"2014-01-03","field1":"value1","field2":"value11","value_field":-10},
{"date":"2014-01-04","field1":"value2","field2":"value12","value_field":150},
]
So far I have created a barGraph which plays nicely with the other dimensions but I would like to be able to show an line graph of the theoretical field runnning_total (along the dimension date).
To have it done in crossfilter would allow me to then group using the fieldx fields and easily get the same running total graph but restricted to a subgroup of values using dc.js.
Any help is welcome.

Since you are grouping across date (as per your date dimension), the reduce() function would be used to perform aggregations grouped by date, as per the highlighted cells in my Mickey Mouse example below:
With a running total you'd need to perform an entirely different operation, looping down the rows:
You can aggregate the data and then append the running total field as follows. I've also included an example of how to calculate an average value, using the reduce function:
records = [{ "date": "2014-01-01", "field1": "value1", "field2": "value11", "value_field": -20 },
{ "date": "2014-01-02", "field1": "value2", "field2": "value12", "value_field": 100 },
{ "date": "2014-01-03", "field1": "value1", "field2": "value11", "value_field": -10 },
{ "date": "2014-01-04", "field1": "value2", "field2": "value12", "value_field": 150 }
];
var cf = crossfilter(records);
var dimensionDate = cf.dimension(function (d) {
return d.date;
});
function reduceAdd(p, v) {
p.total += v.value_field;
p.count++;
p.average = p.total / p.count;
return p;
}
function reduceRemove(p, v) {
p.total -= v.value_field;
p.count--;
p.average = p.count ? p.total / p.count : 0;
return p;
}
function reduceInitial() {
return {
total: 0,
count: 0,
average: 0,
};
}
var average = dimensionDate.group().reduce(reduceAdd, reduceRemove, reduceInitial).all();
var averageWithRunningTotal = appendRuningTotal(average);
function appendRuningTotal(average) {
var len = average.length,
runningTotal = 0;
for (var i = 0; i < len; i++) {
runningTotal += average[i].value.total;
average[i].RunningTotal = runningTotal;
}
return average;
}
And this returns the following:
{"key":"2014-01-01","value":{"total":-20,"count":1,"average":-20},"RunningTotal":-20}
{"key":"2014-01-02","value":{"total":100,"count":1,"average":100},"RunningTotal":80}
{"key":"2014-01-03","value":{"total":-10,"count":1,"average":-10},"RunningTotal":70}
{"key":"2014-01-04","value":{"total":150,"count":1,"average":150},"RunningTotal":220}

Well I know the op already built a solution but after struggling with for a while I was able to crack it, so posting it here if someone else searches for it.
using the cumulative for the following: https://groups.google.com/forum/#!topic/dc-js-user-group/W9AvkP_dZ0U
Running Sum:
var _group = dim.group().reduceSum(function(d) {return 1;});
var group = {
all:function () {
var cumulate = 0;
var g = [];
_group.all().forEach(function(d,i) {
cumulate += d.value;
g.push({key:d.key,value:cumulate})
});
return g;
}
};
for Trailing Twelve Month calculation:
var _group = dateDim.group().reduceSum(function(d) {return d.revenue;});
var group = {
all:function () {
var g = [];
_group.all().forEach(function(d,i) {
var cumulate = 0;
var monthcount =0;
var dt =new Date( d.key);
var ct = new Date(d.key);
ct.setFullYear(ct.getUTCFullYear() -1);
_group.all().forEach(function(d2,i) {
var dt2 = new Date(d2.key);
if(dt2 <= dt && dt2 > ct){
cumulate += d2.value;
monthcount++;
}
})
if(monthcount>=11){
console.log( ' Dt ' + dt + ' ' + cumulate + ' months ' + monthcount);
g.push({key:d.key,value:cumulate})
}
});
return g;
}
};

Related

How to add a subtotal column in Datatables

My last question was related to the sum of columns (vertically) and now I need to sum the values of the columns (horizontally)
What I've tried:
I've tried iterating all columns table.columns().data() and sum the values but I do not know how to "inject" the result of the sum in the last column.
Desired result:
Sub Total is the result of: 3+4+5+6+7+8+9+10+11+12
LIVE JS BIN
Here is your JS code with your old code. You can also see it from here
<script>
$(document).ready(function() {
var DT1 = $('#example').DataTable({
columnDefs: [{
orderable: false,
className: 'select-checkbox',
targets: 0,
}],
select: {
style: 'os',
selector: 'td:first-child'
},
order: [
[1, 'asc']
],
"language": {
"info": "Showing _START_ to _END_ of _TOTAL_ Projects",
"infoEmpty": "Showing 0 to 0 of 0 Projects",
"infoFiltered": "(filtered from _MAX_ total Projects)"
},
"pagingType": "numbers",
dom: 'rtip',
initComplete: function(settings, json) {
// calculate the sum when table is first created:
doSum();
}
});
$('#example').on('draw.dt', function() {
// re-calculate the sum whenever the table is re-displayed:
doSum();
});
$(".selectAll").on("click", function(e) {
if ($(this).is(":checked")) {
DT1.rows().select();
} else {
DT1.rows().deselect();
}
});
// This provides the sum of all records:
function doSum() {
// get the DataTables API object:
var table = $('#example').DataTable();
// set up the initial (unsummed) data array for the footer row:
var totals = ['', 'Total:', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
// iterate all rows - use table.rows( {search: 'applied'} ).data()
// if you want to sum only filtered (visible) rows:
totals = table.rows().data()
// sum the amounts:
.reduce(function(sum, record) {
for (let i = 2; i <= 12; i++) {
sum[i] = sum[i] + numberFromString(record[i]);
}
return sum;
}, totals);
// place the sum in the relevant footer cell:
for (let i = 1; i <= 12; i++) {
var column = table.column(i);
$(column.footer()).html(formatNumber(totals[i]));
}
$('#example > tbody > tr').each(function(index, tr) {
var rowSum = 0;
for (let j = 0; j < 12; j++) {
if (j >= 2) {
var tdCellValue = numberFromString(tr.cells[j].innerText);
rowSum = rowSum + tdCellValue;
}
if (j >= 11) {
tr.cells[12].innerText = rowSum;
}
}
});
$('#example > tfoot > tr').each(function(index, tr) {
var rowSum = 0;
for (let j = 0; j < 12; j++) {
if (j >= 2) {
var tdCellValue = numberFromString(tr.cells[j].innerText);
rowSum = rowSum + tdCellValue;
}
if (j >= 11) {
tr.cells[12].innerText = rowSum;
}
}
});
}
function numberFromString(s) {
return typeof s === 'string' ?
s.replace(/[\$,]/g, '') * 1 :
typeof s === 'number' ?
s : 0;
}
function formatNumber(n) {
return n.toLocaleString(); // or whatever you prefer here
}
});
</script>

Datatable sum column by row grouping and show sum result at the end of each group

I am using datatable row grouping from https://datatables.net/examples/advanced_init/row_grouping.html and it works fine.
I have following scenario:
Now I want to calculate sum(sub-total) by each grouping abd show result in row at the end of grouping as you see in image.
At the end of listing , want to show a final row of total amount.
How to accomplish that.
Js used code is:
$(function() {
var table = $('#table').DataTable({
"columnDefs": [
{ "visible": false, "targets": 1 }
],
"order": [[ 1, 'asc' ]],
"displayLength": 25,
"drawCallback": function ( settings ) {
var api = this.api();
var rows = api.rows( {page:'current'} ).nodes();
var last=null;
api.column(1, {page:'current'} ).data().each( function ( group, i ) {
if ( last !== group ) {
$(rows).eq( i ).before(
'<tr class="group" style="background-color:#F5F5F5;"><td colspan="3">'+group+'</td></tr>'
);
last = group;
}
} );
}
});
});
For the same exemple here : https://datatables.net/examples/advanced_init/row_grouping.html
I have made some changes inside "drawCallback": function ( settings ) {
Check this fiddle https://jsfiddle.net/bLykqbo6/110/
I hope it helps
"drawCallback": function ( settings ) {
var api = this.api();
var rows = api.rows( {page:'current'} ).nodes();
var last = null;
var sum = 0;
var groupColumn = 1; //index of column which you are going to group by.
var amtColumn = 3; // index of column which you are going to sum.
api.column(groupColumn, {page:'current'} ).data().each( function ( group, i ) {
if ( last !== group ) {
api.rows().data().each( function(item){
if (item[groupColumn] == group){
sum = sum + (+item[amtColumn]);
}
});
$(rows).eq( i ).before(
'<tr class="group"><td colspan="2" style="background-color: #e7e7e7;"><b>'+group+'</b></td><td style="background-color: #e7e7e7;"><b>'+ sum +'</b></td></tr>'
);
last = group;
sum = 0;
}
});
}

c3.js total count in title of pie chart

I have a question about the pie chart in c3.js.
How can I add the total count of a pie chart in the title??
var title = new Array('data1.sql','data2.sql')
var dtitle = new Array('title1','title2')
var chart = new Array('chart0', 'chart1')
for(var i = 0; i < title.length; i++){
chart[i] = c3.generate({
bindto : '#chart' + i,
size: {
height: 550,
width: 800
},
data : {
url : '/json/sql/data/test/' + title[i],
mimeType : 'json',
type : 'donut'
},
donut: {
title: dtitle[i] + ' - Total:' ,
label: {
format: function(value, ratio, id) {
return value;
}
}
}
});
}
The annoying thing here is that the title option can take a function, but the chart variable is not initialised within it so using the c3 api methods can't be done at this point.
So the best (maybe only) way is to add an onrendered callback that adds up the data as you'd need to anyways and then replace the text in the chart's title text using a spot of d3:
onrendered: function () {
var data = this.api.data();
var total = data.reduce (function (subtotal, t) {
return subtotal + t.values.reduce (function (subsubtotal,b) { return subsubtotal + b.value; }, 0);
}, 0);
d3.select(this.config.bindto + " .c3-chart-arcs-title").text("Total: "+total);
}
Edit: If you want it to keep track of a total as you hide/show series use this
var data = this.api.data.shown.call (this.api);
instead of
var data = this.api.data();

Pivot data dynamically for google line chart

I want to display "population" of various countries through the years in the same line chart. The data displayed is based on selections from a multi-select dropdown "Countries". Underlying Data Table has 3 columns:
Year, Country, Population
2012,countryA,33
2013,countryA,35
2014,countryA,40
2012,countryB,65
2013,countryB,70
2014,countryB,75
2012,countryC,15
2013,countryC,20
2014,countryC,25
I am trying to create a pivoted Data View from the underlying Data Table
The code I am using is:
function drawLineChart() {
var arr = $('#country').val();
var lineChartJson = $.ajax({
url: "../json/lineChart.json",
dataType: "json",
async: false
}).responseText;
var lineChartData = new google.visualization.DataTable(lineChartJson);
var view = new google.visualization.DataView(lineChartData);
var viewCols = [0];
for(var i = 0; i < arr.length; i++) {
var viewCols1 = [{
type: 'number',
label: arr[i],
calc: function (dt, row) {
return (dt.getValue(row, 1) == arr[i]) ? dt.getValue(row, 2) : null;
}
}];
viewCols = viewCols.concat(viewCols1);
}
view.setColumns(viewCols);
var aggCols = [{
column: 1,
type: 'number',
label: view.getColumnLabel(1),
aggregation: google.visualization.data.sum
}];
for(var i = 2; i < 4; i++) {
var aggCols1 = [{
column: i,
type: 'number',
label: view.getColumnLabel(i),
aggregation: google.visualization.data.sum
}];
aggCols = aggCols.concat(aggCols1);
}
var pivotedData = google.visualization.data.group(view, [0], aggCols);
But this does not seem to work as expected and I just get 1 Line in the chart with values for all countries added up (although I can see the legend for 3 countries)
On the other hand if I set my View columns as below, it works as expected.
view.setColumns([0, {
type: 'number',
label: arr[0],
calc: function (dt, row) {
return (dt.getValue(row, 1) == arr[0]) ? dt.getValue(row, 2) : null;
}
}, {
type: 'number',
label: arr[1],
calc: function (dt, row) {
// return values of C only for the rows where B = "bar"
return (dt.getValue(row, 1) == arr[1]) ? dt.getValue(row, 2) : null;
}
}, {
type: 'number',
label: arr[2],
calc: function (dt, row) {
return (dt.getValue(row, 1) == arr[2]) ? dt.getValue(row, 2) : null;
}
}]);
What is going wrong in the loop? Is something wrong with "concat" in the loop where I am creating View Columns? I also saw the viewCols array by using console.log and it seems to have the right elements
I was trying to follow the below post:
Creating pivoted DataView from existing google charts DataTable object
the problem has to do with scope
arr[i] is undefined within calc: function (dt, row)
here is another way to pivot the data...
google.charts.load('current', {
callback: function () {
var arr = [
'countryA',
'countryB',
'countryC'
];
var lineChartData = google.visualization.arrayToDataTable([
['Year', 'Country', 'Population'],
[2012,'countryA',33],
[2013,'countryA',35],
[2014,'countryA',40],
[2012,'countryB',65],
[2013,'countryB',70],
[2014,'countryB',75],
[2012,'countryC',15],
[2013,'countryC',20],
[2014,'countryC',25]
]);
// sort by year
lineChartData.sort([{column: 0}]);
// get unique countries
var countryGroup = google.visualization.data.group(
lineChartData,
[1]
);
// build country data table
var countryData = new google.visualization.DataTable({
cols: [
{label: 'Year', type: 'number'},
]
});
// add column for each country
for (var i = 0; i < countryGroup.getNumberOfRows(); i++) {
countryData.addColumn(
{label: countryGroup.getValue(i, 0), type: 'number'}
);
}
// add row for each year / country
var rowYear;
var rowIndex;
for (var i = 0; i < lineChartData.getNumberOfRows(); i++) {
if (rowYear !== lineChartData.getValue(i, 0)) {
rowYear = lineChartData.getValue(i, 0);
rowIndex = countryData.addRow();
countryData.setValue(rowIndex, 0, rowYear);
}
for (var x = 1; x < countryData.getNumberOfColumns(); x++) {
if (countryData.getColumnLabel(x) === lineChartData.getValue(i, 1)) {
countryData.setValue(rowIndex, x, lineChartData.getValue(i, 2));
}
}
}
// draw agg table
new google.visualization.ChartWrapper({
chartType: 'Table',
containerId: 'table-div',
dataTable: countryData
}).draw();
// draw line chart
new google.visualization.ChartWrapper({
chartType: 'LineChart',
containerId: 'chart-div',
dataTable: countryData
}).draw();
},
packages: ['corechart', 'table']
});
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="table-div"></div>
<div id="chart-div"></div>
I could figure out the problem with my code above.
"calc" is the callback function in loop. So only last value of loop variable "i" is visible within the loop.
Putting a wrapper function fixes it:
for(var i = 0; i <= arr.length; i++)(function(i) {
var viewCols1 = [{
type: 'number',
label: arr[i],
calc: function (dt, row) {
return (dt.getValue(row, 1) == arr[i]) ? dt.getValue(row, 2) : null;
}
}];
viewCols = viewCols.concat(viewCols1);
})(i);

Want to get new defects for the sprint, as want to show Defect/Story Ratio

I have build one app, which shows two things for each team based on selected releases.
Rework - Which is Completed Stories in sprint n / Completed stories in sprint n
Defect/ Story - New defects in sprint n / Completed stories in sprint n-1
But I am not able to get which are the new defects in sprint n,
I want the cumulative activated count as shown in Defect Trend Chart, which I can access from Reports.
Which is a new defect count for me.
Below is my code, where I am considering that defects which are !ACCEPTED are new defects but it's not right.
Any help or suggestions on this, how Rally shows cumulative activated count as shown in Defect Trend Chart?
_fetchByArrayOfValues:function(oids,field_name){
var filters = null;
//this.logger.log("_fetchByArrayOfValues (", model_name, ",", oids.length, ",", field_name ,")");
var deferred = Ext.create('Deft.Deferred');
filters = Ext.create('Rally.data.QueryFilter',{
property:field_name,
operator: '=',
value:oids[0]
});
for ( var i=1;i<oids.length;i++ ) {
filters = filters.or(Ext.create('Rally.data.QueryFilter',{
property:field_name,
value:oids[i]
}));
}
this.flows_by_iteration_id = {}; // key is iteration_id
this.iterations = {}; // key is iteration_id
this.iterations_defined = false;
//this.old_iterations = {};
this.addIterations();
var created = Ext.create('Rally.data.wsapi.artifact.Store', {
autoLoad: true,
models: ['UserStory','Defect'],
context: {
project: this.getContext().getProject()._ref,
projectScopeDown: true,
projectScopeUp: false
},
filters: filters,
limit: Infinity,
fetch: ['Name','Iteration','ScheduleState', 'InProgressDate', 'LastUpdateDate'],
listeners: {
load: function(store, records) {
var defectHash = {};
var storyHash = {};
Ext.Array.each(records, function(item){
var itData = this.iterations[item.data.Iteration._ref];
//var id = this.iterations[item.data.Iteration._ref]
if ( ! defectHash[itData.Ref] ) { defectHash[itData.Ref] = { "NewCount": 0, "CompletedCount":0}; }
if ( ! storyHash[itData.Ref] ) { storyHash[itData.Ref] = { "Count": 0}; }
if(item.data._type == "defect" && itData.Ref == item.data.Iteration._ref && itData.Name == item.data.Iteration.Name) {
if (item.data.ScheduleState != "Accepted") {
defectHash[itData.Ref].NewCount += 1;
} else if (item.data.ScheduleState == "Accepted") {
defectHash[itData.Ref].CompletedCount += 1;
}
//+= value.Accept;
} else if (item.data._type == "hierarchicalrequirement" && item.data.ScheduleState == "Accepted" && itData.Ref == item.data.Iteration._ref && itData.Name == item.data.Iteration.Name) {
storyHash[itData.Ref].Count += 1;
}
}, this);
this.commit_data_set = [defectHash, storyHash];
deferred.resolve(created);
},
scope: this
}
}, this);
return deferred.promise;
},