I already know how to draw a vertical line in the chart but it's a static value and I want to be dynamic. So what I want is to get the Index of the current month and year is there a function?.
Example:
Imagine that the current date is now = '27-03-2021'
I need a function that retrieves the current month and year
Note: this.value, this.category, this.point always show undefined why can't I use it in the potLines area?
xAxis: {
categories: ['01-08-2018', '01-10-2019', '31-02-2020', '27-03-2021', '01-01-1900'],
plotLines: [{
color: '#FF0000',
width: 1,
value: index = (function() {
var value;
if (this.category == '03-2021') { //Compare only the month and year
value = //I want the Index of this position date
} else {
value = //the length of the list
}
return value;
})()
}]
}
Here is a dynamic way to set the plotLine
let data = () => {
let data = []
let start = 1609455600000
for(let i = 0; i < 12; i++){
data.push([start + i * 2592000000, Math.floor(Math.random() * Math.floor(30))])
}
return data
}
Highcharts.chart('container', {
xAxis: {
type: 'datetime',
plotLines: [{
color: '#FF0000',
width: 2,
value: Date.now()
}]
},
series: [{
name: 'Installation',
data: data(),
}],
});
<script src="https://code.highcharts.com/highcharts.js"></script>
<div id="container"></div>
I believe as per the API documentation that value only takes a value not a function. If you wish to use a function to you can do so in chart.events.load for example. Then you could try to get the value, category, point from the chart object using this keyword.
Related
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'
I need help to improve the number of points displayed on the chart line.
With the current code, for 100000 points, only 20 drawn in the graph line.
var elements = new Array(100000);
for (i = 0; i < elements.length; i++) {
elements[i] = i;
}
var myChart = echarts.init(document.getElementById('main'));
var option = {
title: {
text: 'ECharts entry example'
},
tooltip: {},
legend: {
data:['Sales']
},
xAxis: {
data: elements
},
yAxis: {},
series: [{
name: 'Sales',
type: 'line',
data: elements
}]
};
myChart.setOption(option);
You have to modify the xAxis for this. You should take a look at the axisTicks, and play around with the interval option. It either supports auto, a number or a function.
Alternatively, you can also manually show/hide the datapoints, by telling the data elements to display them, but maybe this only works when there's an axis tick available.
For displaying every datapoint, set showAllSymbol to true in the series data.
series: [{
name: 'Sales',
type: 'line',
showAllSymbol: true,
data: elements
}]
However, 20.000 datapoints may be a lot, so you can also create an interval by setting showSymbol within the data elements
for(i = 0; i < elements.length; i++){
elements[i] = {
value: i,
symbol: (i % 100 === 0) ? 'circle' : 'none'
}
}
This will set showSymbol to true for every 100th iteration. You may have to combine this with showAllSymbol: true in the series data to work properly.
Note: % is the modulus operator
Currently using Angular JS and ChartJS to try and put a chart on my page. The data is requested through a route in NodeJS and then the functions loop through the leads that are in the response and try to count how many were created on each day of the month.
When I console log the leadsPerDay it returns an array with everything how I would expect it, but the chart doesn't seem to render the dots appropriately. They all fall on the bottom which tells me it's finding my array because if I take it out, no dots. If I manually put in the array, it renders properly.
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: getDaysInMonth(currentMonth, currentYear),
datasets: [{
label: '# new leads created',
data: getLeadsForMonth(currentMonth, currentYear),
backgroundColor: [
'rgba(255, 99, 132, 0.2)'
],
borderColor: [
'rgba(255,99,132,1)'
],
borderWidth: 1
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
},
maintainAspectRatio: false
});
function getDaysInMonth(month, year) {
var date = new Date(year, month, 1);
var dates = [];
while (date.getMonth() === month) {
var currentDate = new Date(date).toISOString().replace(/T.*/, '').split('-').reverse().join('-');
var catDate = currentDate.replace(/-2017/g, '').replace(/-/g, '/').split('/').reverse().join('/');;
dates.push(catDate);
date.setDate(date.getDate() + 1);
}
return dates;
}
function getLeadsForMonth(month, year) {
// Create empty array to put leadCount in
var leadsPerDay = new Array();
/* Use $http.get to fetch contents*/
$http.get('/pipedrive/getLeadsForMonth', function() {}).then(function successCallback(response) {
// Loop through each lead and index them based on date
var leads = response.data.data[0].deals;
// Set date to first of the month
var date = new Date(year, month, 1);
// Define the month for the loop
var currentMonth = date.getMonth();
// Loop through the days in the month
while (date.getMonth() === currentMonth) {
// Save the date
var currentDate = new Date(date).toISOString().replace(/T.*/, '');
date.setDate(date.getDate() + 1);
leadCount = 0;
// Loop through each lead and search data for date
for (i = 0; i < leads.length; i++) {
if (leads[i].add_time.includes(currentDate)) {
leadCount++
}
}
leadsPerDay.push(leadCount);
}
}, function errorCallback(response) {
console.log('There was a problem with your GET request.')
});
console.log(leadsPerDay);
return leadsPerDay;
}
The problem here is that the chart is being rendered before the data is actually returned from your function, because you are making an asynchronous call to your endpoint.
Here is the current flow.
1) You instantiate a new chart which then synchronously calls 2 functions to get the labels (getDaysInMonth) and data (getLeadsForMonth).
2) The label function returns no problem with your label data. The data function however then does an async call to an endpoint to acquire data. $http.get returns a Promise and when it is ready you are processing the result and building your data array. Unfortunately at this point, the initial call to getLeadsForMonth has already returned with an empty array.
The reason you are able to see your data using console.log is probably just a timing issue. By the time the browser executes that statement chances are the $http.get already returned (especially true if you are running this on locahost) and the data is now present in the array (however the chart is already rendered so it does not show it).
Since you are asynchronously acquiring your data you have a couple of options.
1) Wait and create your chart after the data is acquired. You could do this by moving the chart creation code inside your $http.get callback. Something like this...
function getDaysInMonth(month, year) {
var date = new Date(year, month, 1);
var dates = [];
while (date.getMonth() === month) {
var currentDate = new Date(date).toISOString().replace(/T.*/, '').split('-').reverse().join('-');
var catDate = currentDate.replace(/-2017/g, '').replace(/-/g, '/').split('/').reverse().join('/');;
dates.push(catDate);
date.setDate(date.getDate() + 1);
}
return dates;
};
function createChart(month, year) {
// generate the labels
var labels = getDaysInMonth(month, year),
// Create empty array to put leadCount in
var leadsPerDay = new Array();
/* Use $http.get to fetch contents*/
$http.get('/pipedrive/getLeadsForMonth', function() {}).then(function successCallback(response) {
// Loop through each lead and index them based on date
var leads = response.data.data[0].deals;
// Set date to first of the month
var date = new Date(year, month, 1);
// Define the month for the loop
var currentMonth = date.getMonth();
// Loop through the days in the month
while (date.getMonth() === currentMonth) {
// Save the date
var currentDate = new Date(date).toISOString().replace(/T.*/, '');
date.setDate(date.getDate() + 1);
leadCount = 0;
// Loop through each lead and search data for date
for (i = 0; i < leads.length; i++) {
if (leads[i].add_time.includes(currentDate)) {
leadCount++
}
}
leadsPerDay.push(leadCount);
}
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
label: '# new leads created',
data: leadsPerDay,
backgroundColor: [
'rgba(255, 99, 132, 0.2)'
],
borderColor: [
'rgba(255,99,132,1)'
],
borderWidth: 1
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
},
maintainAspectRatio: false
});
}, function errorCallback(response) {
console.log('There was a problem with your GET request.')
});
};
createChart(currentMonth, currentYear);
2) Create an empty chart and then use the .update() prototype method to re-render the chart when the data is acquired. Something like this...
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: getDaysInMonth(currentMonth, currentYear),
datasets: [{
label: '# new leads created',
data: [],
backgroundColor: [
'rgba(255, 99, 132, 0.2)'
],
borderColor: [
'rgba(255,99,132,1)'
],
borderWidth: 1
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
},
maintainAspectRatio: false
});
getLeadsForMonthAndUpdateChart(myChart, currentMonth, currentYear);
function getLeadsForMonthAndUpdateChart(chart, month, year) {
// Create empty array to put leadCount in
var leadsPerDay = new Array();
/* Use $http.get to fetch contents*/
$http.get('/pipedrive/getLeadsForMonth', function() {}).then(function successCallback(response) {
// Loop through each lead and index them based on date
var leads = response.data.data[0].deals;
// Set date to first of the month
var date = new Date(year, month, 1);
// Define the month for the loop
var currentMonth = date.getMonth();
// Loop through the days in the month
while (date.getMonth() === currentMonth) {
// Save the date
var currentDate = new Date(date).toISOString().replace(/T.*/, '');
date.setDate(date.getDate() + 1);
leadCount = 0;
// Loop through each lead and search data for date
for (i = 0; i < leads.length; i++) {
if (leads[i].add_time.includes(currentDate)) {
leadCount++
}
}
leadsPerDay.push(leadCount);
}
// add the data to the chart and re-render
chart.data.datasets[0].data = leadsPerDay;
chart.update();
}, function errorCallback(response) {
console.log('There was a problem with your GET request.')
});
};
Here is a codepen example that demonstrates the second option (updating the chart with new data) for you to look at as well.
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.
I am working in representing data in Highcharts. I have got a task like representing sales of every company for every year, where x axis has categories from jan to dec and y axis has data in which each year's data should be shown in different series, for example, if year is 2010, then jan to dec of 2010's data should be displayed as one series and remaining years.
For that I have googled all over the StackOverflow but I have not succeeded yet.
Here is my code:
$.post('./reportdata/salesbyyear.php', { reportid: value }, function(data) {
//incoming data from php echo statement
var year_data = data;
//replacing all special characters with space, except :
year_data = year_data.replace(/[()""/>{}<]/g, '');
//replacing : with ,
year_data = year_data.replace(/:/g, ',');
//splitting the input data into array
var total_data = new Array();
total_data = year_data.split(',');
//creted two new arrays and stroing alternate elements of previous array into two different arrays
var month_data = new Array();
var year_name = new Array();
var y = 0;
for (i = 0; i < total_data.length; i++) {
month_data[y] = total_data[i];
year_name[y] = total_data[i + 1];
i++;
y++;
}
series = [];
series = generatedata(month_data);
function generatedata(data_array) {
// finding total number of arrays by divding the elements by 12
//so that if I get 44 elements, the total arrays will be 4 , i.e each array
// has sequential 12 elements that represents 1 year (12 months)data
var total = Math.ceil(data_array.length / 12);
var values = [];
var l = 0;
for (i = 0; i < total; i++) {
values[i] = [];
for (j = 0; j < 12; j++) {
values[i][j] = parseFloat(data_array[l]);
l++;
//adding data to series and using this series in highcharts code
series.push({
data: values[i]
});
}
}
}
$('#container').highcharts({
chart: {
type: 'line',
backgroundColor: 'rgba(255, 255, 255, 0.1)',
lineColor: 'orange'
},
title: {
text: 'Customer Details',
style: {
fontFamily: 'monospace',
color: 'orange'
}
},
xAxis: {
categories:['January','February','March','April','May','June','July','August','September','October','November','December'],
lineColor: 'orange',
labels: {
style: {
fontFamily: 'monospace',
color: 'orange'
},
},
},
series: [{
data: series
}]
});
});