How to manipulate the reduce function in crossfilter in a way to create a certain array/object structure? - d3.js

I'm using crossfilter and am looking for a certain output after the reduce function:
var ndx = crossfilter(data);
var alDim = ndx.dimension(function(d) { return d.al_code_unique; });
var seatsPaxAirline = alDim.group().reduce(
function(a, d) {
a.seats += d.seats;
a.pax += d.pax;
return a;
},
function(a, d) {
a.seats -= d.seats;
a.pax -= d.pax;
return a;
},
function() {
return {seats:0, pax:0 }; }
).top(100);
seatsPaxAirline is now an array of objects like this:
[
{key: "5Y", value: {pax: 60, seats: 100}},
{key: "4Y", value: {pax: 50, seats: 90}},
{key: "3Y", value: {pax: 40, seats: 80}}
]
But I do need the following output from the crossfilter reduce function:
[
{key: "5Y", value: [ {name: "pax", value: 60}, {name: "seats", value: 100}},
{key: "4Y", value: [ {name: "pax", value: 50}, {name: "seats", value: 90}},
{key: "3Y", value: [ {name: "pax", value: 40}, {name: "seats", value: 80}},
]
I feel it's just a question of changing the reduce function in a certain manner but unfortunately I have no clue. Could someone help me on this? Thanks in advance!

As far as I understand the question, you need to change your reduce function like so:
var seatsPaxAirline = alDim.group().reduce(
function(a, d) {
a[0].value += d.seats;
a[1].value += d.pax;
return a;
},
function(a, d) {
a[0].value -= d.seats;
a[1].value -= d.pax;
return a;
},
function() {
return [{name: 'seats', value: 0}, {name: 'pax', value: 0}]; }
).top(100);
The key is in how you set up the reduceInitial function, as that will determine the data structure of your 'a' object that gets passed through the reduce.

From what I understand, your input format is similar to:
var seatsPaxAirline =
[{key: "5Y", value: {pax: 60, seats: 100}},
{key: "5Y", value: {pax: 40, seats: 100}},
{key: "3Y", value: {pax: 30, seats: 100}},
{key: "2Y", value: {pax: 70, seats: 100}},
{key: "4Y", value: {pax: 50, seats: 100}},
{key: "1Y", value: {pax: 45, seats: 100}},
{key: "2Y", value: {pax: 50, seats: 100}},
{key: "5Y", value: {pax: 60, seats: 100}},
{key: "5Y", value: {pax: 65, seats: 100}},
{key: "5Y", value: {pax: 55, seats: 100}}
];
And, you wish to group elements based on their key values like:
{
"5Y": [{"key": "5Y", "value": {"pax": 60, "seats": 100}},
{"key": "5Y", "value": {"pax": 40, "seats": 100}},
{"key": "5Y", "value": {"pax": 60, "seats": 100}},
{"key": "5Y", "value": {"pax": 65, "seats": 100}},
{"key": "5Y", "value": {"pax": 55, "seats": 100}}],
"3Y": [{"key": "3Y", "value": {"pax": 30, "seats": 100}}],
"2Y": [{"key": "2Y", "value": {"pax": 70, "seats": 100}},
{"key": "2Y", "value": {"pax": 50, "seats": 100}}],
"4Y": [{"key": "4Y", "value": {"pax": 50, "seats": 100}}],
"1Y": [{"key": "1Y", "value": {"pax": 45, "seats": 100}}]
}
If this is what you are looking to achieve using D3 then this is the way to do it:
d3.nest().key(function(d){return d.key}).map(seatsPaxAirline);
And, here's the working demo.
EDIT:
I believe the sample object you've provided is malformed. But, if you modify the code I provided as shown below:
var output = d3.nest().key(function(d){return d.key}).map(seatsPaxAirline);
d3.entries(d3.entries(output).forEach(function(d,i){
d.value.forEach(function(o,j){
delete o["key"];
d.value[j] = d3.entries(o.value);
});
}));
output = d3.entries(output);
The output you'll get is:
[{
"key": "5Y",
"value": [
[{"key": "pax","value": 60}, {"key": "seats","value": 100}],
[{"key": "pax","value": 40}, {"key": "seats","value": 100}],
[{"key": "pax","value": 60}, {"key": "seats","value": 100}],
[{"key": "pax","value": 65}, {"key": "seats","value": 100}],
[{"key": "pax","value": 55}, {"key": "seats","value": 100}]
]
}, {
"key": "3Y",
"value": [
[{"key": "pax","value": 30}, {"key": "seats","value": 100}]
]
}, {
"key": "2Y",
"value": [
[{"key": "pax","value": 70}, {"key": "seats","value": 100}],
[{"key": "pax","value": 50}, {"key": "seats","value": 100}]
]
}, {
"key": "4Y",
"value": [
[{"key": "pax","value": 50}, {"key": "seats","value": 100}]
]
}, {
"key": "1Y",
"value": [
[{"key": "pax","value": 45}, {"key": "seats","value": 100}]
]
}]
This is as close as I can get it to the desired data structure. See if this is usable for you.
Updated Demo
PS: I realize that this isn't the most efficient of solutions but this is as good as it gets with me & D3. If you wish to fool around with the data structure, consider using underscoreJS.

Related

Multi series not working in Bar Chart in Recharts

I need to create a bar graph with one of the format below. data1 or data2
const data1 = [ { day: 'ABC', data :[ {job_name: "Mon", value: 4}, {job_name: "Tue", value: 5}, {job_name: "Thu", value: 18}, {job_name: "Fri", value: 5} ] }, { day: 'DEF', data :[ {job_name: "Mon", value: 12}, {job_name: "Tue", value: 15}, {job_name: "Wed", value: 18}, {job_name: "Fri", value: 8} ] }, { day: 'XYZ', data :[ {job_name: "Mon", value: 12}, {job_name: "Tue", value: 15}, {job_name: "Wed", value: 18}, {job_name: "Thu", value: 8} ] } ];
const data2 = [ { day: 'Mon', data :[ {job_name: "ABC", value: 4}, {job_name: "DEF", value: 5}, {job_name: "XYZ", value: 8} ] }, { day: 'Tue', data :[ {job_name: "ABC", value: 12}, {job_name: "DEF", value: 15}, {job_name: "XYZ", value: 18} ] }, { day: 'Wed', data :[ {job_name: "DEF", value: 15}, {job_name: "XYZ", value: 18} ] }, { day: 'Thu', data :[ {job_name: "ABC", value: 12}, {job_name: "XYZ", value: 18} ] }, { day: 'Fri', data :[ {job_name: "ABC", value: 12}, {job_name: "DEF", value: 15} ] } ];
Short answer, you can't. The shape of the data in your samples aren't suitable for Rechart.
Please look up the documentation of Rechart - BarChart -> data and reshape your data accordingly.
Here's an example of the required data format from the same page:
const data = [
{
"name": "Page A",
"uv": 4000,
"pv": 2400,
"amt": 2400
},
{
"name": "Page B",
"uv": 3000,
"pv": 1398,
"amt": 2210
}
Where name is the x-axis data label, and uv, pv and amt are the values of the data series.
So a more hands on example:
const data = [
{
"name": "2019-11-01",
"morning_temp": 8,
"midday_temp": 14,
"evening_temp": 10
},
{
"name": "2019-11-02",
"morning_temp": 5,
"midday_temp": 18,
"evening_temp": 7
},
...
Though from my own experience changing the shape you have can be challenging and possibly will end up a high complexity function. If you can't change the data shape you may have a look at react-vis -> sample as react-vis' data-shape looks more similar to your samples.
Either case the data has to be in the right shape.

C3 Extent not working as expected

I'm creating a pretty simple line graph and want the initial load to be a section of all available data, so am setting extent on the x axis:
This is how I am setting the extent:
axis: {
"x": {
"type": "timeseries",
"tick": {
"format": "%d/%m/%Y"
},
"label": {
"text": "X Label",
"position": "outer-center"
},
"padding": {
"left": 0
},
"extent": ["2017-10-01", "2017-10-05"]
},
"y": {
"label": {
"text": "Y Label",
"position": "outer-middle"
}
}
},
But it is ignored. The chart just shows the full extent of the data.
Is this the correct way to show a subset of the data when the chart is generated?
This is the full code and here's a fiddle (I tried a code snippet but didn't work)
const columnData = [
["x", "2017-10-01", "2017-10-02", "2017-10-03", "2017-10-04", "2017-10-05", "2017-10-06", "2017-10-07", "2017-10-08", "2017-10-09", "2017-10-10", "2017-10-11", "2017-10-12", "2017-10-13", "2017-10-14", "2017-10-15", "2017-10-16"],
["data0", -55, -50, 11, -18, 39, 65, -84, -15, 14, 81, -79, 67, -48, 38, 99, -60],
["data1", 28, 14, -99, -33, 55, 71, 58, 66, 7, -88, 99, -37, -7, 59, -13, -57],
["data2", 14, 6, -9, 25, 42, -93, -6, 67, -35, 88, 36, 45, 42, 78, 51, -88],
["data3", 31, -73, -69, 45, 55, 15, -48, 41, -64, -12, -6, 14, -69, 16, -65, -73],
["data4", 98, 60, 82, 80, -62, -47, 55, 87, -65, 37, 22, 30, 93, -69, -88, 33],
["data5", -98, 57, 71, -25, -40, 13, 72, -90, 71, -71, -21, -9, -90, 73, -94, 100]
];
const generateChart = function() {
const chart = c3.generate({
data: {
"x": "x",
"columns": columnData,
"type": "line"
},
axis: {
"x": {
"type": "timeseries",
"tick": {
"format": "%d/%m/%Y"
},
"label": {
"text": "X Label",
"position": "outer-center"
},
"padding": {
"left": 0
},
"extent": ["2017-10-01", "2017-10-05"]
},
"y": {
"label": {
"text": "Y Label",
"position": "outer-middle"
}
}
},
zoom: {
enabled: true
},
transition: {
duration: 100
},
legend: {
show: false
},
subchart: {
show: true
},
size: {
height: 500
},
grid: {
x: {
show: true
},
y: {
show: true
}
}
});
};
generateChart();
As of 11th May 2018 it seems this is a bug: https://github.com/c3js/c3/issues/2357
I switched to earlier versions (see below) of C3 and D3 and it works as it should.
I am now using C3 version 0.4.10 and D3 version 3.5.0

How to make such a schedule?

It is necessary that all lines had different colors
The lines can be a maximum of 10 pieces
help me please
http://demos.telerik.com/kendo-ui/bar-charts/stacked-bar (It was not possible to make different color lines in each category :( )
You can use the colorField property (API: http://docs.telerik.com/kendo-ui/api/javascript/dataviz/ui/chart#configuration-series.colorField)
var data = [
{x: "Name 1", y: 16, valueColor: "#556270"},
{x: "Name 2", y: 13, valueColor: "#4ECDC4"},
{x: "Name 3", y: 15, valueColor: "#C7F464"},
{x: "Name 4", y: 14, valueColor: "#FF6B6B"},
{x: "Name 5", y: 16, valueColor: "#C44D58"},
];
$("#chart").kendoChart({
dataSource: data,
series: [{
type: "bar",
field: "y",
categoryField: "x",
colorField: "valueColor"
}],
});
DEMO

Trying to create a grouped and stacked bar chart but having some trouble

I'm trying to create a grouped and stacked bar chart but having a little trouble understanding how to do this with c3js. This tutorial is great and shows me a nicely grouped chart.
http://blog.trifork.com/2014/07/29/creating-charts-with-c3-js/
I'm looking for something similar but to have those stacked 2 data sets per bar. My data looks like:
columns: [
['x', 'data1', 'data2'],
['Jan', {data1: 20, data2: 80}, {data1: 80, data2: 20}, {data1: 20, data2: 80}]
['Feb', {data1: 20, data2: 80}, {data1: 80, data2: 20}, {data1: 20, data2: 80}]
['Mar', {data1: 20, data2: 80}, {data1: 80, data2: 20}, {data1: 20, data2: 80}]
]
I would like the chart to have groups of Jan, Feb, Mar. Then have 3 stacked bars labeled within each group. Any ideas if this is possible and how might it be possible, if at all.
Thanks!
Are you wanting something like this? -->
http://jsfiddle.net/3r39gknt/1/
If so, the data1 and data2 fields will need renamed per the example. If not, draw/scan a picture/sketch of what you'd expect - groups in c3 are the data elements involved in each stack, but I've interpreted rightly or wrongly your use of month 'groups' as being different points on the x-axis.
var chart = c3.generate({
data: {
columns: [ // randomish data
['data1', 30, 200, 200],
['data2', 130, 100, 100],
['data3', 230, 220, 200],
['data4', 230, 220, 200],
['data5', 230, 200, 210],
['data6', 230, 190, 200]
],
type: 'bar',
groups: [
['data1', 'data2'],
['data3', 'data4'],
['data5', 'data6'],
],
order: null,
colors: {
data1: '#f00',
data2: '#00f',
data3: '#f33',
data4: '#33f',
data5: '#f66',
data6: '#66f'
}
},
bar: {
width: {
ratio: 0.5 // this makes bar width 50% of length between ticks
}
},
axis: {
x: {
type: 'category',
categories: ['january', 'february', 'march']
}
}
});

How to add CategoryAxis in amcharts?

I need to be able to draw a vertical line in the graph and seems easy enough using this information: http://www.amcharts.com/tutorials/vertical-or-horizontal-lines-or-fill-ranges/
However, I am not finding a way to add the CategoryAxis property to the existing chart.
If you are using an object-based configuration, when you create a chart instance it will already have categoryAxis property set:
var chart = new AmCharts.AmSerialChart();
// chart.categoryAxis is already set and populated with a reference to CategoryAxis object
// we can set its properties, including guides
chart.categoryAxis.guides = [ {
"category": "2001",
"toCategory": "2003",
"lineColor": "#CC0000",
"lineAlpha": 1,
"fillAlpha": 0.2,
"fillColor": "#CC0000",
"dashLength": 2,
"inside": true,
""label"Rotation": 90,
"label": "fines for speeding increased"
}, {
"category": "2007",
"lineColor": "#CC0000",
"lineAlpha": 1,
"dashLength": 2,
"inside": true,
""label"Rotation": 90,
"label": "motorcycle fee introduced"
} ];
If you are using JSON-based approach, the guides config needs to go into "categoryAxis" object:
AmCharts.makeChart( "chartdiv", {
"type": "serial",
"categoryAxis": {
"guides": [ {
"category": "2001",
"toCategory": "2003",
"lineColor": "#CC0000",
"lineAlpha": 1,
"fillAlpha": 0.2,
"fillColor": "#CC0000",
"dashLength": 2,
"inside": true,
""label"Rotation": 90,
"label": "fines for speeding increased"
}, {
"category": "2007",
"lineColor": "#CC0000",
"lineAlpha": 1,
"dashLength": 2,
"inside": true,
""label"Rotation": 90,
"label": "motorcycle fee introduced"
} ]
},
// the rest of the chart config
// ...
};

Resources