Dynamic width for each column if value is 0 in highcharts - javascript

I'm new to highcharts plugin and I need to set different width for each column if the value is empty.
series: [{
name: 'Jane',
data: [1, 0, 4]
}, {
name: 'John',
data: [5, 7, 3]
}]
And i need to display it this way
I need this
I have been searching the documentation but i don't find anything that does it, without a lot of code.
I achieved this, but it doesn't upload/resize dynamically.
http://jsfiddle.net/5vgjztk0/
Thanks

You can use something like merimekko chart.
(function(H) {
var each = H.each;
var xStart = 0;
var rememberX = 0;
H.wrap(H.seriesTypes.column.prototype, 'drawPoints', function(proceed) {
var series = this;
if(series.data.length > 0 ){
each(this.data, function(point) {
point.shapeArgs.width = series.xAxis.translate(point.x) - series.xAxis.translate(xStart);
point.shapeArgs.x = series.xAxis.translate(point.x) - point.shapeArgs.width;
xStart = point.x;
});
xStart = 0;
}
proceed.call(this);
})
})(Highcharts)
Demo:
http://jsfiddle.net/js55fuo1/24

Related

How to group values by week in plotly.js

Seen below is a time series bar graph with a range selector in plotly.js.
In it, I am trying to figure out how to group the values by week, but cannot seem to accomplish this. Is there a setting in plotly.js to group these by week when changing the time range selection? I cannot seem to figure out if it is possible.
Here are the main documentation pages they offer, of which I tried as many settings as I thought pertained to accomplishing this, but could not figure it out.
https://plot.ly/javascript/time-series/
https://plot.ly/javascript/bar-charts/
var days = (function(start,count){
var days = [];
var MSday = 1000 * 60 * 60 * 24;
for(var i = 0; i < count; i++){
days.push(new Date(+start + i*MSday));
}
return days;
})(new Date(2018,0,1),100);
function vals(){
var vals = [];
for(var i = 0; i < 100; i++){
vals.push((Math.random() * 2 * i) | 0);
}
return vals;
}
var selectorOptions = {
buttons: [{
step: 'month',
stepmode: 'backward',
count: 1,
label: '1m'
}, {
step: 'month',
stepmode: 'backward',
count: 6,
label: '6m'
}, {
step: 'year',
stepmode: 'todate',
count: 1,
label: 'YTD'
}, {
step: 'year',
stepmode: 'backward',
count: 1,
label: '1y'
}, {
step: 'all',
}],
};
var trace1 = {
x: days,
y: vals(),
type: 'bar',
name: 'Trace 1'
};
var trace2 = {
x: days,
y: vals(),
type: 'bar',
name: 'Trace 2'
};
var data = [trace1, trace2];
var layout = {
title: 'Bar Demo',
barmode: 'group',
xaxis: {
rangeselector: selectorOptions
}
};
Plotly.newPlot('myDiv', data, layout);
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<div id="myDiv"><!-- Plotly chart will be drawn inside this DIV --></div>
How can I make the 6 month selection group by week instead of by day on the graph?
Apparently this isn't built in. If it is, or becomes built in at some point, please indicate that here in a comment or another answer.
The only option I was able to determine as viable was to hook into the relayout event using .on('plotly_relayout', function () {, taking the arguments from the range selector buttons (which seem limited, only a from and to date, if there is a better way to determine the origination please also let me know and I will update here), and then roughly based on that to bin the dates by week and adjust the x and y values in the plot.
This is just a basic implementation as proof of concept. Using it in production would require refactoring this code to work with the existing data structures with regards to design and page implementation.
There is a lot going on here. Basically, it will iterate through the set of dates to create sunday bins which will hold the weekly data (note that it still lacks a display update to show it is a week from the start date). Once it has the bins it sums the dates in each bin range. Then it replaces the data set using restyle. If the range selected is not 6m then it will use the a slice of the backup data because plotly modifies arrays in place, and as a result it will overwrite the data if there is no backup copy in addition with a single copy every time the backup is used.
See below for a working demo.
function sum(array){
return array.reduce(function(sum,curr){
return sum + curr;
},0);
};
Date.MSday = 1000 * 60 * 60 * 24;
Date.prototype.floor = function(){
return new Date(this.getFullYear(),this.getMonth(),this.getDate());
}
Date.prototype.addDays = function(days){
var time = +this - +this.floor();
var addedDays = new Date(+this.floor() + Date.MSday*days);
return new Date(+addedDays + time);
}
function weeksFromDates(datesArray, valsArray){
var lastDay = datesArray[datesArray.length -1];
var firstDay = datesArray[0];
var dayOfWeek = firstDay.getDay();
var firstSunday = firstDay.addDays(-dayOfWeek);
var sundays = [];
var currentSunday = firstSunday;
while(currentSunday < lastDay){
sundays.push(currentSunday);
currentSunday = currentSunday.addDays(7);
}
currentSunday = currentSunday.addDays(7);
sundays.push(currentSunday);
var valSets = [];
var n = 0;
for(var i = 1; i < sundays.length; i++){
var last = sundays[i-1];
var next = sundays[i];
var theseVals = [];
for(; n < datesArray.length && last <= datesArray[n] && next > datesArray[n]; n++){
theseVals.push(valsArray[n]);
}
valSets.push(sum(theseVals));
}
sundays.pop();
return {x: sundays, y: valSets};
}
var MSday = 1000 * 60 * 60 * 24;
var days = (function(start,count){
var days = [];
for(var i = 0; i < count; i++){
days.push(new Date(+start + i*MSday));
}
return days;
})(new Date(2018,0,1),100);
function vals(){
var vals = [];
for(var i = 0; i < 100; i++){
vals.push((Math.random() * 2 * i) | 0);
}
return vals;
}
var selectorOptions = {
buttons: [{
step: 'month',
stepmode: 'backward',
count: 1,
label: '1m'
}, {
step: 'month',
stepmode: 'backward',
count: 6,
label: '6m'
}, {
step: 'year',
stepmode: 'todate',
count: 1,
label: 'YTD'
}, {
step: 'year',
stepmode: 'backward',
count: 1,
label: '1y'
}, {
step: 'all',
}],
};
var trace1 = {
x: days,
y: vals(),
type: 'bar',
name: 'Trace 1',
orientation: 'v'
};
var trace2 = {
x: days,
y: vals(),
type: 'bar',
name: 'Trace 2',
orientation: 'v'
};
var data = [trace1, trace2];
var dataBackup = $.extend(true,{},data);
var layout = {
title: 'Bar Demo',
barmode: 'group',
xaxis: {
rangeselector: selectorOptions
}
};
Plotly.newPlot('myDiv', data, layout);
$('#myDiv').on('plotly_relayout', function () {
var lower = new Date(arguments[1]['xaxis.range[0]']);
var upper = new Date(arguments[1]['xaxis.range[1]']);
var dayRange = (+upper - +lower) / MSday;
if( dayRange < 190 && dayRange > 170 ){
//6m
for(var n = 0; n < data.length; n++){
var weekly = weeksFromDates(dataBackup[n].x,dataBackup[n].y);
Plotly.restyle('myDiv',{x:[weekly.x],y: [weekly.y]},n);
}
}else{
for(var n = 0; n < data.length; n++){
Plotly.restyle('myDiv',{x:[dataBackup[n].x.slice()],y: [dataBackup[n].y.slice()]},n);
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<div id="myDiv"><!-- Plotly chart will be drawn inside this DIV --></div>
Blimey! There is a much simpler option...
use 7 days:
step: 'day',
stepmode: 'backward',
count: 7,
label: '1w'

Set custom X-Axis values in Flot

My custom X-axis values are not displayed in flot js.
Code for drawing the line chart as below :
var length = 0;
var length1 = 0;
var dataXY = [];
var data = [];
var dataX = [];
length = allVenues.length;
for (var i = 0; i < length; i++) {
length1 = allVenues[i].length;
for (var j = 0; j < length1; j++) {
dataX.push([j, returnDate(allVenues[i][j].date)]);
dataXY.push([returnTimestamp(allVenues[i][j].date), allVenues[i][j].price, "<b>X</b> : " + returnDate(allVenues[i][j].date) + " | " + " <b>Y</b>: " + allVenues[i][j].price]);
}
}
var result = {'label': 'Demo Graph', 'data': dataXY};
data = [result];
var options = {
lines: {
show: true
},
points: {
show: true
},
xaxis: {
ticks: dataX
},
grid: {
hoverable: true,
clickable: true
},
tooltip: {
show: true,
content: "%s | X: %x | Y: %y"
}
};
function returnTimestamp(val) {
var dateTime = new Date(val);
return moment(dateTime).unix();
}
function returnDate(val) {
var dateTime = new Date(val);
return moment(dateTime).format("YYYY-MM-DD hh:mm:ss A");
}
$.plot("#placeholder", data, options);
dataXY array values are:
{"label":"Demo Graph","data":[[1455776629,12],[1455801889,30],[1455962948,45]]}
dataX array values are:
[[0, "2016-02-18 11:53:49 AM"], [1, "2016-02-18 06:54:49 PM"], [2, "2016-02-20 03:39:08 PM"]]
Now i want to set this "dataX" array as X axis values (ticks) on the chart.
This values display below in each point of line graph with X-Axis.
Here dataX and dataXY from allVenues Json Array.
My graph is woking fine except the X-Axis data. You can see in the image below.
Your dataX array for the ticks must have the same x values (timestamps) as your real data array:
[[1455776629, "2016-02-18 11:53:49 AM"], [1455801889, "2016-02-18 06:54:49 PM"], [1455962948, "2016-02-20 03:39:08 PM"]]
PS: I would also suggest to put a linebreak (<br>) between date and time in the labels.

Handsontable Sequence word creation

Thanks for this wonderful plugin.
I am using this plugin for grid functionality. For this I am trying to use sequence word creation on drag down.
I have implemented upto my knowledge. But I am facing one problem while select and drag multiple column.
I have created jsfiddle for this sample
var myData = [
["WIRE-001", 10, 11, 12, 13],
["WIRE-002", 20, 11, 14, 13],
["WIRE-003", 30, 15, 12, 13]
];
$("#exampleGrid").handsontable({
data: myData,
startRows: 5,
startCols: 5,
//minSpareCols: 1, //always keep at least 1 spare row at the right
minSpareRows: 10, //always keep at least 1 spare row at the bottom,
rowHeaders: true,
colHeaders: true,
contextMenu: true,
currentRowClassName: 'currentRow',
currentColClassName: 'currentCol',
outsideClickDeselects: false,
fillHandle: true,
beforeAutofill: function(start, end, data) {
console.log(arguments);
console.log(start);
console.log(end);
console.log(data);
var selectedVal = this.getSelected();
var selectedData = this.getData(selectedVal[0], selectedVal[1], selectedVal[2], selectedVal[3]);
var sequenceNum = [];
var sequenceWord = [];
var numberFormat = 1;
if (start.col == 0) {
for (var j = 0; j < selectedData.length; j++) {
var numbers = selectedData[j][0].match(/[0-9]+$/g);
if (numbers && !isNaN(numbers[0])) {
numberFormat = numbers[0].length;
sequenceNum.push(Number(numbers[0]));
}
var words = selectedData[j][0].match(/[A-Za-z\-]+/g);
if (words && isNaN(words[0])) {
sequenceWord.push(words[0]);
}
}
var prefix = sequenceWord.length > 0 ? sequenceWord[0] : "";
var lastValue = sequenceNum[sequenceNum.length - 1]
var diff = sequenceNum.length > 1 ? (sequenceNum[sequenceNum.length - 1] - sequenceNum[sequenceNum.length - 2]) : 1;
for (var i = 0; i < end.row; i++) {
if (!data[i]) { data[i] = []; }
data[i][0] = prefix + pad((lastValue + diff), numberFormat);
diff++;
}
}
},
afterChange: function(changes, source) {
}
});
Appreciate for helping to solve this.
I recommend opening the console and checking the errors since it becomes very clear what your problem is if you do so. The example that leads to the bug that you mentioned on your comment uses an empty row. In your code you are doing
var words = selectedData[j][0].match(/[A-Za-z\-]+/g);
However, selectedData[j][0] will be null. I would simply add a check for empty rows and handle it appropriately (toss it or default to some value)

How can I style highchart select to look like hover?

Highcharts is an amazing library, however I need to programatically scroll through the chart as if the cursor itself was hovering across the x-axis. The only way I can think of doing this is by selecting points, unfortunately I could not find options in highcharts api documentation that allows me to put crosshairs/ playheader on selected points.
How can I do this?
This is not part of the API, but you can use point.onMouseOver() to fire mouseover event: http://jsfiddle.net/15dzo23n/1/
Example:
var index = 0;
$('#container').highcharts({
chart: {
events: {
load: hoverAnim
}
},
tooltip: {
crosshairs: true
},
series: [{
name: 'AAPL',
data: [5, 10, 15, 12, 13, 14, 12, 1, 12, 1, 12, 12, 12, 1]
}]
});
function hoverAnim(e) {
var chart = this,
points = chart.series[0].points,
len = points.length;
if (index < len) {
chart.hoverSeries = chart.series[0];
chart.hoverPoint = points[index];
chart.pointer.runPointActions(e); //display crosshair
points[index].onMouseOver(e); // display tooltip and set hover state
index++;
setTimeout(function () {
hoverAnim.call(chart, e);
}, 100);
}
}
Pawel Fus had a good answer, however I found an even better solution which doesn't have the buggy lag behavior when switching to different time points. Also events are not needed in this solution.
var chart = myElement.highcharts();
function select(value) {
// Scroll to some time.
var axes = chart.axes;
for (var i = 0; i < axes.length; i ++) {
var axis = axes[i];
axis.drawCrosshair(null, chart.series[0].points[value])
}
for (i = 0; i < chart.series.length; i++) {
var series = chart.series[i];
// Unset last value
series.points[oldValue].setState('');
// Set new value
series.points[value].setState('hover');
}
oldValue = value;
});

Hichcharts- hidden series does not show again after updating plots twice

I have done something like:
Have a check box to change the plot's data
when the check box is checked/unchecked, update the series data array by calling series[i].update() accordingly.
But I have a problem that when a series is hidden by the user and try to check the checkbox and then uncheck, followed by reshow the series, the series does not show again.
Please note that If I only check the box once, no problem occurs, there will be problem only when we check the box more than once after we hide the series.
I have reproduce the problem HERE (JSFiddle)
The code to update the plot is :
if (chart) {
for (var i = 0; i < chart.series.length; i++) {
//chart.series[i].setVisible(true, true); // Note that if I add this code, then no problem
chart.series[i].update({
data: dataArray[i]
});
}
}
The problem is that you base on Highcharts generated points. When series is hidden, then points don't exist and you set empty data for a series. In other words it looks like this: chart.series[i].setData( [] );.
I suggest to use separate arrays for data and use those for your calculations. General idea: http://jsfiddle.net/pGuEv/37/ (I know, it works a bit different, but shows how should be written)
var origData = [
[11, 22, 33, 44, 55, 66, 77, 88],
[10, 20, 30, 40, 50, 60, 70, 80]
];
$('#container').highcharts({
series: [{
data: origData[0].slice()
}, {
data: origData[1].slice()
}]
});
And later:
for (var i = 0; i < origData.length; i++) { // original data
var onePlotData = [];
var sum = 0; //前面点的合计值s
for (var j = 0; j < origData[i].length; j++) { // original data
var value = origData[i][j]; // original data
var point = {
//x: value.x,
y: state ? value + sum : value - sum,
//total: value.total
}
sum = point.y;
onePlotData.push(point);
}
dataArray.push(onePlotData);
}

Categories