Package: plotly-resampler
plotly_resampler: visualize large sequential data byadding resampling functionality to Plotly figuresplotly-resamplerimproves the scalability ofPlotlyfor visualizing large time series datasets. Specifically, our librarydynamicallyaggregates time-series data respective to the current graph view, ensuring efficient and responsive updates during user interactions like panning or zooming via callbacks.This core aggregation functionality is achieved by utilizing bytime-series data point selection algorithms, for whichplotly-resamplerleverages the highly optimized implementations available intsdownsample. Our default data aggregation method isMinMaxLTTB(and selects 1000 data points for plotting). For a deeper understanding of this method, you can consult to the algorithm's dedicatedMinMaxLTTB repositoryand the associatedresearch paper.Inthis Plotly-Resampler demoover110,000,000data points are visualized!🛠️ Installationpippip install plotly-resampler👀 What is the difference between plotly-resampler figures and plain plotly figures?plotly-resamplercan be thought of as wrapper around plain plotly figures which adds visualization scalability to line-charts by dynamically aggregating the data w.r.t. the front-end view.plotly-resamplerthus adds dynamic aggregation functionality to plain plotly figures.❗ Important to know:showalwaysgenerates a static HTML view of the figure, prohibiting dynamic aggregation.To have dynamic aggregation:Useshow_dashwithFigureResamplerto initiate aDashapp to realize the dynamic aggregation withcallbacks.(or output the object in a cell viaIPython.display), which will also spawn a dash-web appwithFigureWidgetResampler, you need to useIPython.displayon the object, which uses widget-events to realize dynamic aggregation (via the runningIPython kernel).Other changes of plotly-resampler figures w.r.t. vanilla plotly:double-clickingwithin a line-chart areadoes not Reset Axes, as it results in an “Autoscale” event. We decided to implement an Autoscale event as updating your y-range such that it shows all the data that is in your x-range.Note: vanilla Plotly figures their Autoscale result in Reset Axes behavior, in our opinion this did not make a lot of sense. It is therefore that we have overriden this behavior in plotly-resampler.📋 FeaturesConvenientto use:just add eitherregister_plotly_resamplerfunction to your notebook with the best suitedmodeargument.FigureResamplerdecorator around a plotly Figure and call.show_dash()FigureWidgetResamplerdecorator around a plotly Figure and output the instance in a cellallows all other plotly figure construction flexibility to be used!Environment-independentcan be used in Jupyter, vscode-notebooks, Pycharm-notebooks, Google Colab, DataSpell, and even as application (on a server)Interface forvarious aggregation algorithms:ability to develop or select your preferred sequence aggregation method🚀 UsageAdd dynamic aggregationto your plotly Figure(unfold your fitting use case)🤖Automatically(minimal code overhead):Use theregister_plotly_resamplerfunctionImport and call theregister_plotly_resamplermethodJust use your regular graph construction codecode example:importplotly.graph_objectsasgo;importnumpyasnpfromplotly_resamplerimportregister_plotly_resampler# Call the register function once and all Figures/FigureWidgets will be wrapped# according to the register_plotly_resampler its `mode` argumentregister_plotly_resampler(mode='auto')x=np.arange(1_000_000)noisy_sin=(3+np.sin(x/200)+np.random.randn(len(x))/10)*x/1_000# auto mode: when working in an IPython environment, this will automatically be a# FigureWidgetResampler else, this will be an FigureResamplerf=go.Figure()f.add_trace({"y":noisy_sin+2,"name":"yp2"})fNote: This wrapsallplotly graph object figures with aFigureResampler|FigureWidgetResampler. This can thus also be
used for theplotly.expressinterface. 🎉👷Manually(higher data aggregation configurability, more speedup possibilities):Within ajupyterenvironment without creating aweb applicationwrap the plotly Figure withFigureWidgetResampleroutput theFigureWidgetResamplerinstance in a cellimportplotly.graph_objectsasgo;importnumpyasnpfromplotly_resamplerimportFigureResampler,FigureWidgetResamplerx=np.arange(1_000_000)noisy_sin=(3+np.sin(x/200)+np.random.randn(len(x))/10)*x/1_000# OPTION 1 - FigureWidgetResampler: dynamic aggregation via `FigureWidget.layout.on_change`fig=FigureWidgetResampler(go.Figure())fig.add_trace(go.Scattergl(name='noisy sine',showlegend=True),hf_x=x,hf_y=noisy_sin)figUsing aweb-applicationwithdashcallbackswrap the plotly Figure withFigureResamplercall.show_dash()on theFigureimportplotly.graph_objectsasgo;importnumpyasnpfromplotly_resamplerimportFigureResampler,FigureWidgetResamplerx=np.arange(1_000_000)noisy_sin=(3+np.sin(x/200)+np.random.randn(len(x))/10)*x/1_000# OPTION 2 - FigureResampler: dynamic aggregation via a Dash web-appfig=FigureResampler(go.Figure())fig.add_trace(go.Scattergl(name='noisy sine',showlegend=True),hf_x=x,hf_y=noisy_sin)fig.show_dash(mode='inline')Tip💡:
For significant faster initial loading of the Figure, we advise to wrap the
constructor of the plotly Figure and add the trace data ashf_xandhf_yNote:
Any plotly Figure can be wrapped withFigureResamplerandFigureWidgetResampler! 🎉
Butonlythego.Scatter/go.Scattergltraces are resampled.💭 Important considerations & tipsWhen running the code on a server, you should forward the port of theFigureResampler.show_dash()method to your local machine.Notethat you can add dynamic aggregation to plotly figures with theFigureWidgetResamplerwrapper without needing to forward a port!TheFigureWidgetResampleruses the IPython main threadfor its data aggregation functionality, so when this main thread is occupied, no resampling logic can be executed. For example; if you perform long computations within your notebook, the kernel will be occupied during these computations, and will only execute the resampling operations that take place during these computations after finishing that computation.In general, when using downsampling one should be aware of (possible)aliasingeffects.
The[R]in the legend indicates when the corresponding trace is being resampled (and thus possibly distorted) or not. Additionally, the~<range>suffix represent the mean aggregation bin size in terms of the sequence index.The plotlyautoscaleevent (triggered by the autoscale button or a double-click within the graph),does not reset the axes but autoscales the current graph-viewof plotly-resampler figures. This design choice was made as it seemed more intuitive for the developers to support this behavior with double-click than the default axes-reset behavior. The graph axes can ofcourse be resetted by using thereset_axisbutton.  If you want to give feedback and discuss this further with the developers, see issue#49.📜 Citation and papersThe paper about the plotly-resampler toolkit itself (preprint):https://arxiv.org/abs/2206.08703@inproceedings{van2022plotly,title={Plotly-resampler: Effective visual analytics for large time series},author={Van Der Donckt, Jonas and Van Der Donckt, Jeroen and Deprost, Emiel and Van Hoecke, Sofie},booktitle={2022 IEEE Visualization and Visual Analytics (VIS)},pages={21--25},year={2022},organization={IEEE}}Related papers:Visual representativenessof time series data point selection algorithms (preprint):https://arxiv.org/abs/2304.00900code:https://github.com/predict-idlab/ts-datapoint-selection-visMinMaxLTTB- an efficient data point selection algorithm (preprint):https://arxiv.org/abs/2305.00332code:https://github.com/predict-idlab/MinMaxLTTB👤Jonas Van Der Donckt, Jeroen Van Der Donckt, Emiel Deprost