Logarithmic charts in crossfilter - javascript

I'm charting various lists of data into histograms using crossfilter. Some of the item's values are a lot higher than others and I'm keen to plot the histograms in a logarithmic fashion.
Is there a way I could convert all the summed item counts into logarithmic values after reduceSum has been called? I'm keen to add in something like
Math.log(d.count) / Math.LN10;
into the following:
var crossfiltered_data = crossfilter(data),
all = crossfiltered_data.groupAll(),
item_labels = crossfiltered_data.dimension(function(d) { return d.name; }),
items_group = hour.group().reduceSum(function(d) {
return d.count
}),
charts = [
barChart(on_range_change)
.dimension(item_labels)
.group(items_group)
.x(d3.scale.linear()
.domain([domain_start, domain_end])
.rangeRound([0, 240])),
],
chart = d3.selectAll(target)
.data(charts)
.each(function(chart) {
chart .on("brush", renderAll)
.on("brushend", renderAll);
}),
render = function(method) {
d3.select(this).call(method);
},
renderAll = function(event) {
chart.each(render);
};

Why not just use a log scale for output? See d3.scale.log. Assuming you're using the barChart on the crossfilter page, you'd just change this line to use a log scale instead of a linear one.

Related

Slices in pie chart stay the same size when data changes (version=current as of this post)

I have a pie chart that is loaded using the Google JSON schema for charts. The JSON data is created using ajax. All the Google charts work as designed except for one issue with the pie chart.
Example Pie chart data.
Assume that when the pie chart first loads there are four data elements with values that make up four slices in the pie chart:
A = 410
B = 420
C = 900
D = 540
410 + 420 + 900 + 540 = 2270
410/2270 = 0.1806167400881057 = 65.02°
420/2270 = 0.1850220264317181 = 66.61°
900/2270 = 0.3964757709251101 = 142.73°
540/2270 = 0.2378854625550661 = 85.64°
The first time the pie chart is loaded, slice percents and labels with the amounts look fine.
The problem is when the Pie chart data changes. None of the Pie slices change size. The slice data labels change, but the slice degrees do not:
Example updated data
If data changed From: 410 + 420 + 900 + 540 = 2270 To: 410 + 420 + 9 + 540 = 1379
410/1379 = 0.2973168963016679 = 107.03°
420/1379 = 0.3045685279187817 = 109.65°
9/1379 = 0.0065264684554025 = 2.35°
540/1379 = 0.3915881073241479 = 140.97°
So, if the value of "C" changes from 900 to 9, the degree of change is not reflected in slice "C" (and slices A, B, and D) even though there is a considerable percentage difference. I have included an image of this scenario, however, with different data.
The only time the pie slice sizes change is when the slice is clicked to drill down, but there it's the same formatted json file, just different values. I have compared the json data both on load and after the data has changed and find no reason why the pie chart slice sizes should not also change. The only thing I can deduce is I'm doing something wrong with the json or drawing the chart, or the SVG is cached, or the pie chart does not update slice size.
json data
{ "cols":[{"id":"","label":"Title","pattern":"","type":"string"},{"id":"","label":"Dollars","pattern":"","type":"number"},{"id":"","label":"","pattern":"","type":"string","p":{"role":"style"}}],
"rows":[{"c":[{"v":"A","f":"label a"},{"v":410.0,"f":"$410.00"},{"v":"fill-color: #00805D","f":null}]},
{"c":[{"v":"B","f":"label b"},{"v":420.0,"f":"$420.00"},{"v":"fill-color: #00805D","f":null}]},
{"c":[{"v":"C","f":"label c"},{"v":900.00,"f":"$900.00"},{"v":"fill-color: #78EAD3","f":null}]},
{"c":[{"v":"D","f":"label d"},{"v":540.00,"f":"$540.00"},{"v":"fill-color: #F88451","f":null}]}]}
<div id="chart_div"></div>
function drawPieChart(data) {
$('#chart_div').empty();
$('#chart_div').css('cursor','default')
var options = chartOptions();
var v_savings_suffix="";
if(paoDetails.chart.mode.state === "sav") {
v_savings_suffix = " (Savings)";
}
var _options = {
title: v_chart_title + ' ' + v_savings_suffix
}
Object.assign( options, _options );
var wrapper = new google.visualization.ChartWrapper({
chartType: 'PieChart',
dataTable: data,
options: options,
containerId: 'chart_div'
});
// Must wait for the ready event in order to
// request the chart and subscribe to 'onmouseover'.
google.visualization.events.addListener(wrapper, 'ready', onReady);
function onReady() {
google.visualization.events.addOneTimeListener(wrapper.getChart(), 'select', selectHandler);
}
wrapper.draw();
function selectHandler() {
var options=null;
var selectedSlice = wrapper.getChart().getSelection()[0];
if (selectedSlice) {
// get fund code from selected slice
var fundCode = wrapper.getDataTable().getValue(selectedSlice.row, 0);
if(fundCode !== "NAV") {
options = wrapper.getOptions();
if(options.slices) {
paoDetails.chart.mode.breakdown.fundKey = fundCode;
var slice = options.slices;
var keys = Object.keys(slice).map(Number);
if(keys) {
paoDetails.chart.mode.breakdown.show = !paoDetails.chart.mode.breakdown.show;
paoDetails.chart.mode.funds = !paoDetails.chart.mode.funds;
recalcExemp();
}
wrapper.setOptions(options);
wrapper.draw();
}
}
}
}
} // end drawPieChart
The root pie slice issue is not resolved. This may be not a Google pie chart bug entirely, but after playing with the data I can only conclude that the pie chart needs to be refreshed somehow. I will look at the options to clear percentages in my pie chart with each data update. Perhaps if I call my data load 2x, setting the percentages to 0 on the first call and loading data on the second. Ugly but may get me off this issue. Help is appreciated.
I noticed that the pie slice percentages do not change at all when the
pie data changes.
The pie slice percentage for the green slice changed only when I changed the value of the variable while at a debugging breakpoint. I think the issue is that the pie chart percentages are not updating even though the total value of the data has changed.
To note: The chart container is within bootstrap col. The chart JavaScript Literal data is returned from PHP.

Filtering null values from dimensions for a DC.JS series chart

I'm trying to create multiple times series charts using DC.JS from one dataset with multiple dimensions
The problem I'm having is that each dimension has null values in them
date,value,#transactionLocalServiceperformancedashboard,Averagetimeonpage,Channelbreakdownfortransactions,Numberofuserenquiries,Pageviews,Reasonsforrejectedapplications,UniqueVisitors,"Userswhousetransaction,abandonoruselegacyroutewhenstartinganonlineapplication",Visits
05/02/17,1.2,,Average time on page,,,,,,,
05/02/17,105,For telephone,,Number of transactions phone,,,,,,
05/02/17,110,For post,,Number of transactions face to face,,,Number of incomplete,,Use legacy route,
05/02/17,165,For face to face,,Number of transactions online new transaction,,,,,Use new transaction,
05/02/17,178,,,,,,,Unique visitors,,
05/02/17,198,,,,,,,,,Visits
After lots and lots of searching I've come up with this jsfiddle
function remove_empty_bins(source_group) {
return {
all:function () {
return source_group.all().filter(function(d) {
return d.value !== "";
});
}
};
};
var coerce_row = function(d){
// console.log(d.VerifyLocalServiceperformancedashboard)
return {
time: d.date,
field: remove_empty_bins(d.VerifyLocalServiceperformancedashboard),
count: +d.count,
};
};
var dataset = data.map(coerce_row);
https://jsfiddle.net/zilnhoj/d8mqpyun/2/
I'm basically trying to filter out the nulls of each dimension and chart that dimension.
Is this possible with DC.JS or do I need to get a separate csv file for each dimension and load them in for each chart - I have the series chart working on a single dimension

Losing data when rendering rowchart using dc.js

I lose data when creating a dc.js rowchart.
var ndx = crossfilter(data);
var emailDimemsion = ndx.dimension(function(d) {
return d.email;
});
var emailGroup = emailDimemsion.group().reduce(
function(p, d) {
++p.count;
p.totalWordCount += +d.word_count;
p.studentName = d.student_name;
return p;
},
function(p, d) {
--p.count;
p.totalWordCount -= +d.word_count;
p.studentName = d.student_name;
return p;
},
function() {
return {
count: 0,
totalWordCount: 0,
studentName: ""
};
});
leaderRowChart
.width(600)
.height(300)
.margins({
top: 0,
right: 10,
bottom: 20,
left: 5
})
.dimension(emailDimemsion)
.group(emailGroup)
.elasticX(true)
.valueAccessor(function(d) {
return +d.value.totalWordCount;
})
.rowsCap(15)
.othersGrouper(false)
.label(function(d) {
return (d.value.studentName + ": " + d.value.totalWordCount);
})
.ordering(function(d) {
return -d.value.totalWordCount
})
.xAxis()
.ticks(5);
dc.renderAll();
The fiddle is here, https://jsfiddle.net/santoshsewlal/6vt8t8rn/
My graph comes out like this:
but I'm expecting my results to be
Have I messed up the reduce functions somehow to omit data?
Thanks
Unfortunately there are two levels of ordering for dc.js charts using crossfilter.
First, dc.js pulls the top N using group.top(N), where N is the rowsCap value. Crossfilter sorts these items according to the group.order function.
Then it sorts the items again using the chart.ordering function.
In cases like these, the second sort can mask the fact that the first sort didn't work right. Crossfilter does not know how to sort objects unless you tell it what field to look at, so group.top(N) returns some random items instead.
You can fix your chart by making the crossfilter group's order match the chart's ordering:
emailGroup.order(function(p) {
return p.totalWordCount;
})
Fork of your fiddle: https://jsfiddle.net/gordonwoodhull/0kvatrb1/1/
It looks there is one student with a much longer word count, but otherwise this is consistent with your spreadsheet:
We plan to stop using group.top in the future, because the current behavior is highly inconsistent.
https://github.com/dc-js/dc.js/issues/934
Update: If you're willing to use the unstable latest version, dc.js 2.1.4 and up do not use group.top - the capping is determined by chart.ordering, capMixin.cap, and capMixin.takeFront only.

Chart.js Bar Chart - grouping and mapping data

I am trying to use Chart.js Bar Chart to display a set of data.
My data is weekly based so to my method I send the year and week and get the data back in 3 columns; Product, Area and Amount.
What I want is to have to Products horizontaly and in each Product I want different bars for each Area and offcourse the Amount verticaly. (Bonus: If an Area nothing in that product it should not be shown in that particular Product)
The problem is that the number of Products and the number of Areas can vary from each week. And I can't seem to find a good way to loop through the data and create the datasets the way chart.js wants.
Also tried using Underscore.js to group it but the fact that the each Area doesn't always have an amount for a spesific product seems to be causing some issues.
So I guess you have to loop through the data and map that data to another predefined array for each Area so it can match this structure somehow??
Also open for other Chart plugins, but really liked how Chart.js animates the data. And if I get this working I can probably figgure out an update method for when you change week.
To get the labels i can f.ex do this:
$.ajax({
....
success: function (d) {
var a = _.groupBy(d.data, function (d) { return d.Product });
var labels = [];
$.each(a, function (i, value) {
labels.push(i);
});
}
});
With data in this format
var myJSONData = [
{
Product: 'P1',
Area: 'A1',
Value: 12
},
...
]
You can use this function to convert it into the format Chart.js requires
var data = {
labels: [],
datasets: []
}
var colors = ['Red','Blue','Green', ...] // add as many colors as there will be areas (maximum)
myJSONData.forEach(function (e) {
// create labels
var labelIndex = data.labels.indexOf(e.Product)
if (labelIndex === -1) {
labelIndex = data.labels.length;
data.labels.push(e.Product);
// dummy entries for each dataset for the label
data.datasets.forEach(function (dataset) {
dataset.data.push(0)
})
}
// get the area dataset
var area = data.datasets.filter(function(area){
return (area.label === e.Area);
})[0]
// otherwise create it
if (area === undefined) {
area = {
label: e.Area,
// create a dummy array with an entry for each of the existing labels
data: data.labels.map(function () {
return 0;
}),
fillColor: colors[data.datasets.length]
};
data.datasets.push(area)
}
// set the value
area.data[labelIndex] = e.Value;
})
and use that to display the chart.
Fiddle - http://jsfiddle.net/jt4Lqkn3/
(Bonus: If an Area nothing in that product it should not be shown in
that particular Product)
You can't change any configuration to do this - there will be a space left for each series.
However you might want to set the strokeColor to a transparent value (e.g. strokeColor: "rgba(0, 0, 0, 0)", just below the fillColor line) and set the barStrokeWidth option to 0, so that 0 values don't show up at all on the chart (otherwise there will be thin line shown)
new Chart(ctx).Bar(data, {
barStrokeWidth: 0,
});

creating groups for decimal values using crossfilter

I have a parameter that ranges from (0-1.0). I am trying to create a dc.js bar chart, so that there are 10 bars representing (0-0.1) (0.1-0.2) and so on.
I am using crossfilter.js to create dimension and group data, but it does not seem to create groups as required.
I tried the following code
var fluctuation = ndx.dimension(function (d) {
return d.value;
});
var fluctuationGroup = fluctuation.group(function(d){
return Math.round(d*10)/10;
});
I also tried doing it another way.
var fluctuation = ndx.dimension(function (d) {
return Math.round(d.value*10)/10;
});
var fluctuationGroup = fluctuation.group(function(d){
return d;
});
Output:
Sorry folks, it was not a crossfilter problem. It was a rendering problem.
I was not using
xUnits(d3.scale.linear().domain([0, 1])) with the chart.
Please suggest if I should delete this question ? Or update it ??

Categories

Resources