C3JS - Group dates correctly - javascript

I'm using the library c3js to show an area chart.
I've a problem with my dates data. I've a lot of dates (+/- 1000) and I want to group correctly my dates.
Here is my actuel render :
[
You can see that some dates appear two time on x axis.
Here is the code of my chart :
var usedList = JSON.parse(usedList);
var format = informations[0];
var counts = informations[1];
var amounts = new Array('Evolution');
var dates = new Array("x");
for (var i = 0; i < usedList.length; i++)
{
amounts.push(usedList[i].price);
dates.push(usedList[i].date);
}
var chart = c3.generate({
bindto : '#js-chart',
size : {
height : 220
},
area: {
zerobased: false
},
data : {
selection : {
draggable : false
},
x : 'x',
columns : [ dates, amounts ],
types : {
Evolution : 'area'
},
colors : {
Evolution : '#143FB4'
}
},
axis : {
x : {
type : 'timeseries',
tick : {
fit: true,
format : format
},
padding: {
left: 0,
right: 0,
}
}
},
point : {
r : 0
},
grid : {
y : {
show : true
}
}
});
How do you think I can do to regroup date in order to have just one repetition of each month ?
Thank you,
David

What if push only one date for a month?
for (var i = 0; i < usedList.length; i++)
{
amounts.push(usedList[i].price);
// replacing existing dates with empty string, keeping array size
var d = usedList[i].date;
dates.push(dates.indexOf(d) == -1 ? d : '');
}
This should work if dates are stored as 'MM/YYYY'.
If dates are stored in a different format, different check is needed.
But general idea stays the same - only one date for a month at x-axis.

Related

Add plotbonds to Gaps defined by GapSize in Highcharts

I am trying to add background color to gaps in data to be more visible on large intervals, I know that I can do that by adding plotbonds with the color I want, the problem is I don't have the start and end of the gap because it is created by defining the GapSize and GapUnit (no dates with null data, juste a gap in the dates).
I tried adding the plotbonds by calculating the difference between the dates and comparing it to the tickInterval but no luck so far,
here is an example of gaps set with gapsize
plotOptions: {
series: {
gapSize: 1
}
}
https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/stock/plotoptions/series-gapsize
Is there a simpler way of doing this ?
thanks
Highcharts internally adds null points to create gaps. You can get the calculated null points and based on their values, create plot-bands.
For example:
let plotBands = [];
let allowChartUpdate = true;
(function(H) {
H.wrap(H.seriesTypes.area.prototype, 'getGraphPath', function(proceed, points) {
const xAxis = this.xAxis;
plotBands = [];
points.forEach((p, index) => {
if (p.isNull) {
plotBands.push({
from: points[index - 1] ? points[index-1].x : xAxis.min,
to: points[index + 1] ? points[index+1].x : xAxis.max,
color: 'red'
});
}
});
return proceed.apply(this, Array.prototype.slice.call(arguments, 1));
});
}(Highcharts));
Highcharts.stockChart('container', {
chart: {
type: 'area',
events: {
render: function() {
// prevent infinity loop
if (allowChartUpdate) {
allowChartUpdate = false;
this.xAxis[0].update({plotBands});
allowChartUpdate = true;
}
}
}
},
...
});
Live demo: https://jsfiddle.net/BlackLabel/jz0n28om/
API Reference: https://api.highcharts.com/highcharts/xAxis.plotBands
Docs: https://www.highcharts.com/docs/extending-highcharts/extending-highcharts

dc.js bar chart - calculate bar size by sum of multiple data entries

Sorry for the title, it is hard to sumarize what I am trying to achieve in one sentence.
I have a bar chart that uses a crossfilter that is also used by 6 other charts. My data looks as follows (note: this is only a small sample, there are more keys in each object)
var data = [
{ clientime : '20210603000000', calendarweek : '22', x : 9, y : 4 },
{ clientime : '20210603000007', calendarweek : '22', x : 5, y : 5 },
{ clientime : '20210607000000', calendarweek : '23', x : 1, y : 2 },
{ clientime : '20210607000007', calendarweek : '23', x : 5, y : 5 },
{ clientime : '20210615000000', calendarweek : '24', x : 10, y : 20 },
{ clientime : '20210615000011', calendarweek : '24', x : 5, y : 5 },
];
The category for each bar is the calendarweek and I wan the the value to be the sum of all x devided by the sum of all y.
According to the above sample I would like to see 3 bars.
Bar '22' should have the value `sum(9,5)/sum(4,5)` = 1.556
Bar '23' should have the value `sum(1,5)/sum(2,5)` = 0.857
Bar '24' should have the value `sum(10,5)/sum(20,5)` = 0.6
My first intention wasto use the reduce function where I would add or remove the sums in a custom dictionary.
var x_key = "calendarweek";
var dim = crossfilter.dimension( d => d[x_key] );
var grp = dim.group().reduce(
( p, v ) => {
p.sum_x += v.x;
p.sum_y += v.y;
return p;
},
( p, v ) => {
p.sum_x -= v.x;
p.sum_y -= v.y;
return p;
},
() => ( { sum_x : 0, sum_y : 0 } )
);
var chart = dc.barChart( "#chart" );
chart
.width( 490 )
.height( 280 )
.dimension( dim )
.group( grp )
.x( d3.scaleBand() )
.xUnits( dc.units.ordinal )
.elasticX( true )
.elasticY( true )
.controlsUseVisibility( true )
.margins( {
top : 10, right : 50, bottom : 20, left : 40,
} );
grp.all() does seem to look fine but from here on out I am not sure how to set the data correctly to chart. Using the created group no bars are shown at all because I creted an object in the reduce function that dc.js does not understand.
Additionally I would like to still be able to limit the bars to N entries.
Thanks to Gordon in the comments pointing me into the right direction I was able to solve my problem using the valueAccesor.
chart_mttrhist.valueAccessor( function( d ) {
return ( d.value.sum_x / d.value.sum_y );
} );
Please note that the code in the question changed a little.

Start live Flot Chart at 0

I have a Flot Chart that is live and generates random numbers that displays on the chart. It is used only as a dummy so I don't need real live data. I want the chart to start at 0 on yaxis and 100 on xaxis to make it look as real as possible. Below is my code in the js file.
var data = [], totalPoints = 100
h = 0
function getRandomData() {
//h = h + 1
//return h
//data.push(h)
if (data.length > 0)
data = data.slice(1)
// Do a random walk
while (data.length < totalPoints) {
var prev = data.length > 0 ? data[data.length - 1] : 50,
y = prev + Math.random() * 10 - 5,
if (y < 0) {
y = 0
} else if (y > 100) {
y = 100
}
data.push(y)
}
// Zip the generated y values with the x values
var res = []
for (var i = 0; i < data.length; ++i) {
res.push([i, data[i]])
}
return res
}
var interactive_plot = $.plot('#interactive', [getRandomData()], {
grid : {
borderColor: '#f3f3f3',
borderWidth: 1,
tickColor : '#f3f3f3'
},
series: {
shadowSize: 0, // Drawing is faster without shadows
color : '#3c8dbc'
},
lines : {
fill : true, //Converts the line chart to area chart
color: '#3c8dbc'
},
yaxis : {
min : 0,
max : 100,
show: true
},
xaxis : {
show: true
}
})
var updateInterval = 500 //Fetch data ever x milliseconds
var realtime = 'on' //If == to on then fetch data every x seconds. else stop fetching
function update() {
interactive_plot.setData([getRandomData()])
// Since the axes don't change, we don't need to call plot.setupGrid()
interactive_plot.draw()
if (realtime === 'on')
setTimeout(update, updateInterval)
}
//INITIALIZE REALTIME DATA FETCHING
if (realtime === 'on') {
update()
}
//REALTIME TOGGLE
$('#realtime .btn').click(function () {
if ($(this).data('toggle') === 'on') {
realtime = 'on'
}
else {
realtime = 'off'
}
update()
})
/*
* END INTERACTIVE CHART
*/
There is a button that calls the Flot chart into action and it works great but it looks too much like a staged chart. I need it start at 0 on the y-axis and 100 on the x-axis. Any information on how to go about this would be great and very much appreciated.
Fill your data array with zeroes before you start, then your update function starts adding random points one at a time instead of starting with 100 random data points:
for (var i = 0; i < totalPoints; i++) {
data.push(0);
}
See this fiddle for a full example.
PS: Please end your code lines with a semicolon!

d3 chart + jQuery DataTables: trouble reading nested array

I'm trying to adapt this jQuery DataTables example to a d3 chart that I've been developing.
Here's a link to the semi-working Plunker: http://plnkr.co/edit/kXvBjNsCbblC3ykkuPsL?p=preview
The problem is that some of the values are showing up in the table, while others are not (in particular, the values that come from an array within an array).
Oddly enough, the error message I'm getting, Cannot read property '0' of undefined, refers to line 1074, on which recordCol is defined. This is strange because the values for recordCol and stateName both show up just fine in the DataTable. Also strange is that all of the column headers do appear, even for the nested array (though not their values).
Here's the problematic code:
function tables(dataset) {
var recordCol = Object.keys(dataset[0])[0];
var stateName = Object.keys(dataset[0])[3];
var dateCol = Object.keys(dataset[0].values[0])[0];
var valCol = Object.keys(dataset[0].values[0])[1];
var monthDateFormat = d3.time.format("%B");
var yearDateFormat = d3.time.format("%Y");
var properDateFormat = d3.time.format("%B %Y");
var tableData = dataset.map(function(d) {
d[recordCol] = d[recordCol].toString().slice(0, 15);
d[stateName] = d[stateName].toString().slice(0, 20);
d[dateCol] = d[dateCol];//.toString().slice(0, 20);
d[valCol] = d[valCol];///.toString().slice(0, 20);
return d;
})
$(".data-table").empty();
if (dataTable) {
dataTable.destroy();
}
dataTable = $(".data-table").DataTable({
data: tableData,
columns: [{
"data": recordCol
}, {
"data": stateName
}, {
"data": dateCol
}, {
"data": valCol
}]
});
d3.selectAll("thead th").each(function(d, i) {
if (i === 0) {
this.textContent = recordCol;
} else if (i === 1) {
this.textContent = stateName;
} else if (i === 2) {
this.textContent = dateCol;
} else if (i === 3) {
this.textContent = valCol;
}
});
}
As you'll see in my semi-working Plunker, I've been beating a dead horse in console.log, trying to troubleshoot the error messages I've been getting, the other of which is this.
In sum, what I'm trying to do is get all the values for x and y to appear in the DataTable alongside state and record -- as well as relabel the column headers that currently read x and y as as date and value, respectively.
In advance, thanks very much for any assistance you're able to offer.
Update:
The following changes, I've discovered, make the full subarray, containing all the x and y values, appear in every row of the DataTable:
dataTable = $(".data-table").DataTable({
data: tableData,
columns: [{
"data": recordCol
}, {
"data": stateName
}, {
"data": "values[, ].x"
}, {
"data": "values[, ].y"
}]
});
Here's an updated Plunker. The search for a solution continues.
Update 2:
The following changes make the x and y value appear as Sun May 01 2011 00:00:00 GMT-0400 (Eastern Daylight Time) and 2761, but the same in every row:
var tableData = dataset.map(function(d) {
d[recordCol] = d[recordCol].toString().slice(0, 15);
d[stateName] = d[stateName].toString().slice(0, 20);
for (var i = 0; i < dataset.length; i++) {
d[dateCol] = dataset[0].values[i].x;
d[valCol] = dataset[0].values[i].y;
}
dataTable = $(".data-table").DataTable({
data: tableData,
columns: [{
"data": recordCol
}, {
"data": stateName
}, {
"data": dateCol
}, {
"data": valCol
}]
});
Another updated Plunker. Obviously, this is wrong; still searching for a solution.
Update 3:
Still searching for a solution, but in this third updated Plunker, I discovered how to take all of the values contained in the subarray of the parent array's first row, and (incorrectly) map them onto all of the rows of the parent array. Here's an illustration of what's going wrong:
Is anyone able to demonstrate a solution?
You need to change your table map. Try it:
var table_map = [];
for (var i = 0; i < dataset.length; i++) {
for (var j = 0; j < dataset[i].values.length; j++) {
var table_row = {};
table_row[recordCol] = dataset[i][recordCol];
table_row[stateName] = dataset[i][stateName];
table_row[dateCol] = dataset[i].values[j][dateCol];
table_row[valCol] = dataset[i].values[j][valCol];
table_map.push(table_row);
}
}

Script function on running once in Chrome but works fine in Firefox

I am using NVD3 to make several graphs. I put a console.log in the addGraph function which adds a particular graph.
I am wondering whether there is something wrong in my code? Am I using the library correctly? I do not know where to begin to resolve this issue.
FIREFOX (All the graphs are displayed properly)
total 444
Add graph is called here
total 518
Add graph is called here
total 572
Add graph is called here
total 553
Add graph is called here
total 617
Add graph is called here
total 595
Add graph is called here
GOOGLE CHROME
total NaN
total NaN
total NaN
total NaN
total NaN
Add graph is called here
total 108
total 138
total 145
total 146
CODE
Calling the scripts
<script src="../../../data/novus/lib/d3.v2.js"></script>
<script src="../../../data/novus/nv.d3.js"></script>
<script src="../../../data/novus/src/tooltip.js"></script>
<script src="../../../data/novus/src/models/legend.js"></script>
<script src="../../../data/novus/src/models/axis.js"></script>
<script src="../../../data/novus/src/models/scatter.js"></script>
<script src="../../../data/novus/src/models/line.js"></script>
<script src="../../../data/novus/src/models/multiChart.js"></script>
In the loop
<script src="../../../data/novus/nv.d3.js"></script>
<script>
var impressions = [];
var clickrate = [];
var trial_impressions = [];
var trial_clickrate = [];
var testdata = [{
"key" : "Impressions",
"type" : "line",
"values" : impressions,
"yAxis" : 1
}, {
"key" : "Clicks",
"type" : "line",
"values" : clickrate,
"yAxis" : 2
}, {
"key" : "T Impressions",
"type" : "line",
"values" : trial_impressions,
"yAxis" : 1
}, {
"key" : "T Clicks",
"type" : "line",
"values" : trial_clickrate,
"yAxis" : 2
}].map(function(series) {
series.values = series.values.map(function(d) {
return {
x : d[0],
y : d[1]
}
});
return series;
});
var chart;
nv.addGraph(function() {
console.log("Add");
chart = nv.models.multiChart().margin({
top : 30,
right : 60,
bottom : 50,
left : 70
}).x(function(d, i) {
return i
}).color(d3.scale.category10().range());
chart.xAxis.tickFormat(function(d) {
var dx = testdata[0].values[d] && testdata[0].values[d].x || 0;
if ( typeof (dx) == undefined || d > 1000) {
dx = new Date(d);
} else {
dx = new Date(dx);
}
return dx ? d3.time.format('%x')(dx) : '';
});
chart.yAxis1.tickFormat(d3.format(',.1f'));
v
chart.yAxis2.tickFormat(d3.format(',.4f'));
d3.select('#chart1<?= $chartID?> svg').datum(testdata).transition().duration(500).call(chart);
return chart;
});
</script>
The div where the graph is called
<div id='chart1<?= $chartID?>' style="width:1110px;height:300px;font-size:11px;margin-top:5px">
<svg></svg>
</div>
My best guess is that there is mathematical calculations on those numbers. In witch case using the function parseFloat() can convert the string to a number so that mathematical calculations will be done correctly.

Categories

Resources