简体   繁体   中英

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.

 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'

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM