Skip to content

Commit df125b3

Browse files
authored
Merge pull request #70 from predict-idlab/register_pr
✨ register plotly-resampler
2 parents 0fda64c + 4804862 commit df125b3

23 files changed

+3055
-459
lines changed

README.md

+114-52
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,12 @@
1818

1919
> `plotly_resampler`: visualize large sequential data by **adding resampling functionality to Plotly figures**
2020
21-
[Plotly](https://github.com/plotly/plotly.py) is an awesome interactive visualization library, however it can get pretty slow when a lot of data points are visualized (100 000+ datapoints). This library solves this by downsampling (aggregating) the data respective to the view and then plotting the aggregated points. When you interact with the plot (panning, zooming, ...), callbacks are used to aggregate data and update the figure.
21+
[Plotly](https://github.com/plotly/plotly.py) is an awesome interactive visualization library, however it can get pretty slow when a lot of data points are visualized (100 000+ datapoints). This library solves this by downsampling (aggregating) the data respective to the view and then plotting the aggregated points. When you interact with the plot (panning, zooming, ...), callbacks are used to aggregate data and update the figure.
2222

23-
<p align="center">
24-
<a href="#readme">
25-
<img alt="example demo" src="https://github.com/predict-idlab/plotly-resampler/blob/main/docs/sphinx/_static/basic_example.gif" width=95%>
26-
</a>
27-
</p>
23+
![basic example gif](https://raw.githubusercontent.com/predict-idlab/plotly-resampler/main/docs/sphinx/_static/basic_example.gif)
2824

29-
In [this Plotly-Resampler demo](https://github.com/predict-idlab/plotly-resampler/blob/main/examples/basic_example.ipynb) over `110,000,000` data points are visualized!
25+
26+
In [this Plotly-Resampler demo](https://github.com/predict-idlab/plotly-resampler/blob/main/examples/basic_example.ipynb) over `110,000,000` data points are visualized!
3027

3128
<!-- These dynamic aggregation callbacks are realized with: -->
3229
<!-- * [Dash](https://github.com/plotly/dash) when a `go.Figure` object is wrapped with dynamic aggregation functionality, see example ⬆️. -->
@@ -39,79 +36,144 @@ In [this Plotly-Resampler demo](https://github.com/predict-idlab/plotly-resample
3936

4037
### Installation
4138

42-
| [**pip**](https://pypi.org/project/plotly_resampler/) | `pip install plotly-resampler` |
39+
| [**pip**](https://pypi.org/project/plotly_resampler/) | `pip install plotly-resampler` |
4340
| ---| ----|
4441
<!-- | [**conda**](https://anaconda.org/conda-forge/plotly_resampler/) | `conda install -c conda-forge plotly_resampler` | -->
4542

43+
<br>
4644

4745
## Usage
4846

49-
To **add dynamic resampling** to your plotly Figure
50-
* using a web application with *Dash* callbacks, you should;
51-
1. wrap the plotly Figure with `FigureResampler`
52-
2. call `.show_dash()` on the Figure
53-
* within a *jupyter* environment and *without creating a web application*, you should:
54-
1. wrap the plotly Figure with `FigureWidgetResampler`
55-
2. output the `FigureWidgetResampler` instance in a cell
47+
**Add dynamic aggregation** to your plotly Figure _(unfold your fitting use case)_
48+
* 🤖 <b>Automatically</b> _(minimal code overhead)_:
49+
<details><summary>Use the <code>register_plotly_resampler</code> function</summary>
50+
<br>
5651

57-
> **Note**:
58-
> Any plotly Figure can be wrapped with `FigureResampler` and `FigureWidgetResampler`! 🎉
59-
> But, (obviously) only the scatter traces will be resampled.
52+
1. Import and call the `register_plotly_resampler` method
53+
2. Just use your regular graph construction code
54+
55+
* **code example**:
56+
```python
57+
import plotly.graph_objects as go; import numpy as np
58+
from plotly_resampler import register_plotly_resampler
59+
60+
# Call the register function once and all Figures/FigureWidgets will be wrapped
61+
# according to the register_plotly_resampler its `mode` argument
62+
register_plotly_resampler(mode='auto')
63+
64+
x = np.arange(1_000_000)
65+
noisy_sin = (3 + np.sin(x / 200) + np.random.randn(len(x)) / 10) * x / 1_000
66+
67+
68+
# auto mode: when working in an IPython environment, this will automatically be a
69+
# FigureWidgetResampler else, this will be an FigureResampler
70+
f = go.Figure()
71+
f.add_trace({"y": noisy_sin + 2, "name": "yp2"})
72+
f
73+
```
74+
75+
> **Note**: This wraps **all** plotly graph object figures with a
76+
> `FigureResampler` | `FigureWidgetResampler`. This can thus also be
77+
> used for the `plotly.express` interface. 🎉
6078

61-
> **Tip** 💡:
62-
> For significant faster initial loading of the Figure, we advise to wrap the constructor of the plotly Figure and add the trace data as `hf_x` and `hf_y`
79+
</details>
6380

64-
### Minimal example
81+
* 👷 <b>Manually</b> _(higher data aggregation configurability, more speedup possibilities)_:
82+
<details>
83+
<summary>Within a <b><i>jupyter</i></b> environment without creating a <i>web application</i></summary>
84+
<br>
6585

66-
```python
67-
import plotly.graph_objects as go; import numpy as np
68-
from plotly_resampler import FigureResampler, FigureWidgetResampler
86+
1. wrap the plotly Figure with `FigureWidgetResampler`
87+
2. output the `FigureWidgetResampler` instance in a cell
6988

70-
x = np.arange(1_000_000)
71-
noisy_sin = (3 + np.sin(x / 200) + np.random.randn(len(x)) / 10) * x / 1_000
89+
* **code example**:
90+
```python
91+
import plotly.graph_objects as go; import numpy as np
92+
from plotly_resampler import FigureResampler, FigureWidgetResampler
7293

73-
# OPTION 1 - FigureResampler: dynamic aggregation via a Dash web-app
74-
fig = FigureResampler(go.Figure())
75-
fig.add_trace(go.Scattergl(name='noisy sine', showlegend=True), hf_x=x, hf_y=noisy_sin)
94+
x = np.arange(1_000_000)
95+
noisy_sin = (3 + np.sin(x / 200) + np.random.randn(len(x)) / 10) * x / 1_000
7696

77-
fig.show_dash(mode='inline')
78-
```
97+
# OPTION 1 - FigureWidgetResampler: dynamic aggregation via `FigureWidget.layout.on_change`
98+
fig = FigureWidgetResampler(go.Figure())
99+
fig.add_trace(go.Scattergl(name='noisy sine', showlegend=True), hf_x=x, hf_y=noisy_sin)
79100

80-
#### FigureWidgetResampler: dynamic aggregation via `FigureWidget.layout.on_change`
81-
```python
82-
...
83-
# OPTION 2 - FigureWidgetResampler: dynamic aggregation via `FigureWidget.layout.on_change`
84-
fig = FigureWidgetResampler(go.Figure())
85-
fig.add_trace(go.Scattergl(name='noisy sine', showlegend=True), hf_x=x, hf_y=noisy_sin)
101+
fig
102+
```
103+
</details>
104+
<details>
105+
<summary>Using a <b><i>web-application</i></b> with <b><a href="https://github.com/plotly/dash">dash</a></b> callbacks</summary>
106+
<br>
86107

87-
fig
88-
```
108+
1. wrap the plotly Figure with `FigureResampler`
109+
2. call `.show_dash()` on the `Figure`
89110

90-
### Features
111+
* **code example**:
112+
```python
113+
import plotly.graph_objects as go; import numpy as np
114+
from plotly_resampler import FigureResampler, FigureWidgetResampler
91115

92-
* **Convenient** to use:
93-
* just add either
94-
* `FigureResampler` decorator around a plotly Figure and call `.show_dash()`
95-
* `FigureWidgetResampler` decorator around a plotly Figure and output the instance in a cell
96-
* allows all other plotly figure construction flexibility to be used!
97-
* **Environment-independent**
98-
* can be used in Jupyter, vscode-notebooks, Pycharm-notebooks, Google Colab, and even as application (on a server)
99-
* Interface for **various aggregation algorithms**:
100-
* ability to develop or select your preferred sequence aggregation method
116+
x = np.arange(1_000_000)
117+
noisy_sin = (3 + np.sin(x / 200) + np.random.randn(len(x)) / 10) * x / 1_000
101118

119+
# OPTION 2 - FigureResampler: dynamic aggregation via a Dash web-app
120+
fig = FigureResampler(go.Figure())
121+
fig.add_trace(go.Scattergl(name='noisy sine', showlegend=True), hf_x=x, hf_y=noisy_sin)
122+
123+
fig.show_dash(mode='inline')
124+
```
125+
126+
</details>
127+
<br>
128+
129+
> **Tip** 💡:
130+
> For significant faster initial loading of the Figure, we advise to wrap the
131+
> constructor of the plotly Figure and add the trace data as `hf_x` and `hf_y`
132+
133+
<br>
134+
135+
> **Note**:
136+
> Any plotly Figure can be wrapped with `FigureResampler` and `FigureWidgetResampler`! 🎉
137+
> But, (obviously) only the scatter traces will be resampled.
138+
139+
140+
141+
142+
<br>
143+
<details><summary>Features</summary>
144+
145+
* **Convenient** to use:
146+
* just add either
147+
* `register_plotly_resampler` function to your notebook with the best suited `mode` argument.
148+
* `FigureResampler` decorator around a plotly Figure and call `.show_dash()`
149+
* `FigureWidgetResampler` decorator around a plotly Figure and output the instance in a cell
150+
* allows all other plotly figure construction flexibility to be used!
151+
* **Environment-independent**
152+
* can be used in Jupyter, vscode-notebooks, Pycharm-notebooks, Google Colab, and even as application (on a server)
153+
* Interface for **various aggregation algorithms**:
154+
* ability to develop or select your preferred sequence aggregation method
155+
</details>
102156

103157
### Important considerations & tips
104158

105159
* When running the code on a server, you should forward the port of the `FigureResampler.show_dash()` method to your local machine.<br>
106160
**Note** that you can add dynamic aggregation to plotly figures with the `FigureWidgetResampler` wrapper without needing to forward a port!
107-
* In general, when using downsampling one should be aware of (possible) [aliasing](https://en.wikipedia.org/wiki/Aliasing) effects.
108-
The <b><a style="color:orange">[R]</a></b> 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.
161+
* In general, when using downsampling one should be aware of (possible) [aliasing](https://en.wikipedia.org/wiki/Aliasing) effects.
162+
The <b style="color:orange">[R]</b> 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.
109163
* The plotly **autoscale** event (triggered by the autoscale button or a double-click within the graph), **does not reset the axes but autoscales the current graph-view** of 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 the `reset_axis` button. If you want to give feedback and discuss this further with the developers, see issue [#49](https://github.com/predict-idlab/plotly-resampler/issues/49).
110164

165+
<!-- ## Cite
166+
167+
```latex
168+
{
169+
}
170+
``` -->
111171

112172
## Future work 🔨
113173

114-
* Support `.add_traces()` (currently only `.add_trace` is supported)
174+
- [x] Support `.add_traces()` (currently only `.add_trace` is supported)
175+
- [ ] Support `hf_color` and `hf_markersize`, see [#50](https://github.com/predict-idlab/plotly-resampler/pull/50)
176+
- [ ] Create C bindings for our EfficientLTTB algorithm.
115177

116178
<br>
117179

docs/sphinx/api_reference.rst

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
API reference 📖
2-
================
1+
API 📖
2+
======
33

44
.. autosummary::
55
:toctree: _autosummary
@@ -9,4 +9,7 @@ API reference 📖
99
plotly_resampler.figure_resampler
1010

1111
.. _aggregation
12-
plotly_resampler.aggregation
12+
plotly_resampler.aggregation
13+
14+
.. _registering
15+
plotly_resampler.registering

docs/sphinx/conf.py

+16-4
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
import os
1414
import sys
1515

16-
sys.path.insert(0, os.path.abspath("../plotly_resampler"))
16+
sys.path.append(os.path.abspath("../../"))
17+
sys.path.append(os.path.abspath("../../plotly_resampler"))
1718

1819

1920
# -- Project information -----------------------------------------------------
@@ -44,7 +45,7 @@
4445
"sphinx.ext.autosummary",
4546
"sphinx_autodoc_typehints",
4647
"sphinx.ext.todo",
47-
'sphinx.ext.autosectionlabel',
48+
"sphinx.ext.autosectionlabel",
4849
"sphinx.ext.viewcode",
4950
# 'sphinx.ext.githubpages',
5051
]
@@ -88,6 +89,7 @@
8889
html_theme = "pydata_sphinx_theme"
8990
html_logo = "_static/logo.png"
9091
html_favicon = "_static/icon.png"
92+
language = "en"
9193

9294
html_theme_options = {
9395
# "show_nav_level": 2,
@@ -104,12 +106,22 @@
104106
"type": "fontawesome", # Default is fontawesome
105107
}
106108
],
109+
"pygment_light_style": "tango", # tango
110+
"pygment_dark_style": "native",
111+
"navbar_end": [
112+
"theme-switcher.html",
113+
"navbar-icon-links.html",
114+
"search-field.html",
115+
],
107116
}
108117

109118
html_sidebars = {
110-
'figure_resampler*': [],
111-
'aggregation*': []
119+
"figure_resampler*": [],
120+
"aggregation*": [],
121+
"_autosummary*": [],
122+
"*": [],
112123
}
124+
# html_sidebars = {"figure_resampler*": [], "aggregation*": []}
113125

114126

115127
# Add any paths that contain custom static files (such as style sheets) here,

docs/sphinx/dash_app_integration.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77

88

99

10-
Integration with a dash app 🤝
11-
==============================
10+
Dash integration 🤝
11+
===================
1212

1313
This documentation page describes how you can integrate ``plotly-resampler`` in a `dash <https://dash.plotly.com/>`_ application.
1414

docs/sphinx/figure_resampler.rst

+8
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,11 @@ FigureWidgetResampler
2727
:undoc-members:
2828
:show-inheritance:
2929

30+
^^^^^^^^^^^^^^^^^
31+
utility functions
32+
^^^^^^^^^^^^^^^^^
33+
34+
.. automodule:: plotly_resampler.figure_resampler.utils
35+
:members:
36+
:undoc-members:
37+
:show-inheritance:

0 commit comments

Comments
 (0)