Skip to content

Commit b94dc4a

Browse files
committed
➰ updating & improving docs
1 parent 59c70ab commit b94dc4a

File tree

3 files changed

+188
-90
lines changed

3 files changed

+188
-90
lines changed

README.md

Lines changed: 113 additions & 68 deletions
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.
22+
23+
![basic example gif](https://raw.githubusercontent.com/predict-idlab/plotly-resampler/main/docs/sphinx/_static/basic_example.gif)
2224

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>
2825

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!
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,96 +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
47+
**Add dynamic aggregation** to your plotly Figure _(unfold your fitting use case)_
48+
* Automatically _(minimal code overhead)_:
49+
<details><summary>Use the <code>register_plotly_resampler</code> function</summary>
50+
<br>
51+
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. 🎉
78+
79+
</details>
80+
81+
* Manually _(higher data aggregation configurability, more speedup possibilities)_:
5082
<details>
51-
<summary>within a <b><i>jupyter</i> environment without creating a <i>web application</i></b></summary>
52-
<br>
53-
54-
1. wrap the plotly Figure with `FigureWidgetResampler`
55-
2. output the `FigureWidgetResampler` instance in a cell
56-
57-
**example code**:
58-
```python
59-
import plotly.graph_objects as go; import numpy as np
60-
from plotly_resampler import FigureResampler, FigureWidgetResampler
61-
62-
x = np.arange(1_000_000)
63-
noisy_sin = (3 + np.sin(x / 200) + np.random.randn(len(x)) / 10) * x / 1_000
64-
65-
# OPTION 1 - FigureWidgetResampler: dynamic aggregation via `FigureWidget.layout.on_change`
66-
fig = FigureWidgetResampler(go.Figure())
67-
fig.add_trace(go.Scattergl(name='noisy sine', showlegend=True), hf_x=x, hf_y=noisy_sin)
68-
69-
fig
70-
```
71-
83+
<summary>Within a <b><i>jupyter</i></b> environment without creating a <i>web application</i></summary>
84+
<br>
85+
86+
1. wrap the plotly Figure with `FigureWidgetResampler`
87+
2. output the `FigureWidgetResampler` instance in a cell
88+
89+
* **code example**:
90+
```python
91+
import plotly.graph_objects as go; import numpy as np
92+
from plotly_resampler import FigureResampler, FigureWidgetResampler
93+
94+
x = np.arange(1_000_000)
95+
noisy_sin = (3 + np.sin(x / 200) + np.random.randn(len(x)) / 10) * x / 1_000
96+
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)
100+
101+
fig
102+
```
72103
</details>
73104
<details>
74-
<summary>using a <b><i>web-application</i> with <a href="https://github.com/plotly/dash">dash</a></b> callbacks</summary>
75-
<br>
76-
77-
1. wrap the plotly Figure with `FigureResampler`
78-
2. call `.show_dash()` on the `Figure`
79-
80-
**example code**:
81-
```python
82-
import plotly.graph_objects as go; import numpy as np
83-
from plotly_resampler import FigureResampler, FigureWidgetResampler
84-
85-
x = np.arange(1_000_000)
86-
noisy_sin = (3 + np.sin(x / 200) + np.random.randn(len(x)) / 10) * x / 1_000
87-
88-
# OPTION 2 - FigureResampler: dynamic aggregation via a Dash web-app
89-
fig = FigureResampler(go.Figure())
90-
fig.add_trace(go.Scattergl(name='noisy sine', showlegend=True), hf_x=x, hf_y=noisy_sin)
91-
92-
fig.show_dash(mode='inline')
93-
```
94-
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>
107+
108+
1. wrap the plotly Figure with `FigureResampler`
109+
2. call `.show_dash()` on the `Figure`
110+
111+
* **code example**:
112+
```python
113+
import plotly.graph_objects as go; import numpy as np
114+
from plotly_resampler import FigureResampler, FigureWidgetResampler
115+
116+
x = np.arange(1_000_000)
117+
noisy_sin = (3 + np.sin(x / 200) + np.random.randn(len(x)) / 10) * x / 1_000
118+
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+
95126
</details>
127+
<br>
96128

97-
---
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`
98132

99-
> **Note**:
100-
> Any plotly Figure can be wrapped with `FigureResampler` and `FigureWidgetResampler`! 🎉
101-
> But, (obviously) only the scatter traces will be resampled.
133+
<br>
102134

103-
> **Tip** 💡:
104-
> 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`
135+
> **Note**:
136+
> Any plotly Figure can be wrapped with `FigureResampler` and `FigureWidgetResampler`! 🎉
137+
> But, (obviously) only the scatter traces will be resampled.
105138

106139

107-
### Features
108140

109-
* **Convenient** to use:
110-
* just add either
111-
* `FigureResampler` decorator around a plotly Figure and call `.show_dash()`
112-
* `FigureWidgetResampler` decorator around a plotly Figure and output the instance in a cell
113-
* allows all other plotly figure construction flexibility to be used!
114-
* **Environment-independent**
115-
* can be used in Jupyter, vscode-notebooks, Pycharm-notebooks, Google Colab, and even as application (on a server)
116-
* Interface for **various aggregation algorithms**:
117-
* ability to develop or select your preferred sequence aggregation method
118141

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>
119156

120157
### Important considerations & tips
121158

122159
* When running the code on a server, you should forward the port of the `FigureResampler.show_dash()` method to your local machine.<br>
123160
**Note** that you can add dynamic aggregation to plotly figures with the `FigureWidgetResampler` wrapper without needing to forward a port!
124-
* In general, when using downsampling one should be aware of (possible) [aliasing](https://en.wikipedia.org/wiki/Aliasing) effects.
161+
* In general, when using downsampling one should be aware of (possible) [aliasing](https://en.wikipedia.org/wiki/Aliasing) effects.
125162
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.
126163
* 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).
127164

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

129172
## Future work 🔨
130173

131174
- [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.
132177

133178
<br>
134179

docs/sphinx/conf.py

Lines changed: 16 additions & 4 deletions
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/getting_started.rst

Lines changed: 59 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,40 +21,79 @@ Install via `pip <https://pypi.org/project/plotly-resampler>`_:
2121
How to use 📈
2222
-------------
2323

24-
Dynamic resampling callbacks are realized with either:
24+
Dynamic resampling callbacks are realized:
2525

26-
* `Dash <https://github.com/plotly/dash>`_ callbacks, when a ``go.Figure`` object is wrapped with dynamic aggregation functionality.
26+
* **Automatically** (low code overhead):
2727

28-
.. note::
28+
* using the :func:`register_plotly_resampler <plotly_resampler.registering.register_plotly_resampler>` function
2929

30-
This is especially useful when working with **dash functionality** or when you do **not want to solely operate in jupyter environments**.
30+
**To add dynamic resampling using a FigureWidget, you should**:
31+
1. Import and call the :func:`register_plotly_resampler <plotly_resampler.registering.register_plotly_resampler>` method
32+
2. Just use your regular graph construction code
3133

32-
To **add dynamic resampling**, you should:
33-
1. wrap the plotly Figure with :class:`FigureResampler <plotly_resampler.figure_resampler.FigureResampler>`
34-
2. call :func:`.show_dash() <plotly_resampler.figure_resampler.FigureResampler.show_dash>` on the Figure
34+
Once this method is called, it will automatically convert all new defined plotly
35+
graph objects into a :class:`FigureResampler <plotly_resampler.figure_resampler.FigureResampler>` or :class:`FigureWidgetResampler <plotly_resampler.figure_resampler.FigureWidgetResampler>` object.
36+
The ``mode`` parameter of this method allows to define which type of the aforementioned resampling objects is used.
3537

36-
* `FigureWidget.layout.on_change <https://plotly.com/python-api-reference/generated/plotly.html?highlight=on_change#plotly.basedatatypes.BasePlotlyType.on_change>`_ , when a ``go.FigureWidget`` is used within a ``.ipynb`` environment.
38+
* **Manually** (data aggregation configurability, graph construction speedups):
3739

38-
.. note::
40+
* `Dash <https://github.com/plotly/dash>`_ callbacks, when a ``go.Figure`` object is wrapped with dynamic aggregation functionality.
3941

40-
This is especially useful when developing in ``jupyter`` environments and when **you cannot open/forward a network-port**.
42+
.. note::
4143

44+
This is especially useful when working with **dash functionality** or when you do **not want to solely operate in jupyter environments**.
4245

43-
To **add dynamic resampling** using a **FigureWidget**, you should:
44-
1. wrap your plotly Figure (can be a ``go.Figure``) with :class:`FigureWidgetResampler <plotly_resampler.figure_resampler.FigureWidgetResampler>`
45-
2. output the ```FigureWidgetResampler`` instance in a cell
46+
**To add dynamic resampling, you should**:
47+
1. wrap the plotly Figure with :class:`FigureResampler <plotly_resampler.figure_resampler.FigureResampler>`
48+
2. call :func:`.show_dash() <plotly_resampler.figure_resampler.FigureResampler.show_dash>` on the Figure
4649

47-
.. tip::
50+
* `FigureWidget.layout.on_change <https://plotly.com/python-api-reference/generated/plotly.html?highlight=on_change#plotly.basedatatypes.BasePlotlyType.on_change>`_ , when a ``go.FigureWidget`` is used within a ``.ipynb`` environment.
51+
52+
.. note::
53+
54+
This is especially useful when developing in ``jupyter`` environments and when **you cannot open/forward a network-port**.
4855

49-
For **significant faster initial loading** of the Figure, we advise to wrap the constructor of the plotly Figure with either :class:`FigureResampler <plotly_resampler.figure_resampler.FigureResampler>` or :class:`FigureWidgetResampler <plotly_resampler.figure_resampler.FigureWidgetResampler>` and add the trace data as ``hf_x`` and ``hf_y``
5056

51-
.. note::
57+
**To add dynamic resampling using a FigureWidget, you should**:
58+
1. wrap your plotly Figure (can be a ``go.Figure``) with :class:`FigureWidgetResampler <plotly_resampler.figure_resampler.FigureWidgetResampler>`
59+
2. output the ```FigureWidgetResampler`` instance in a cell
5260

53-
Any plotly Figure can be wrapped with dynamic aggregation functionality! 🎉 :raw-html:`<br>`
54-
But, (obviously) only the scatter traces will be resampled.
61+
.. tip::
62+
63+
For **significant faster initial loading** of the Figure, we advise to wrap the constructor of the plotly Figure with either :class:`FigureResampler <plotly_resampler.figure_resampler.FigureResampler>` or :class:`FigureWidgetResampler <plotly_resampler.figure_resampler.FigureWidgetResampler>` and add the trace data as ``hf_x`` and ``hf_y``
64+
65+
.. note::
66+
67+
Any plotly Figure can be wrapped with dynamic aggregation functionality! 🎉 :raw-html:`<br>`
68+
But, (obviously) only the scatter traces will be resampled.
5569

5670
Working examples ✅
5771
-------------------
72+
register_plotly_resampler
73+
^^^^^^^^^^^^^^^^^^^^^^^^^
74+
75+
.. code:: py
76+
77+
import plotly.graph_objects as go; import numpy as np
78+
from plotly_resampler import register_plotly_resampler
79+
80+
# Call the register function once and all Figures/FigureWidgets will be wrapped
81+
# according to the register_plotly_resampler its `mode` argument
82+
register_plotly_resampler(mode='auto')
83+
84+
x = np.arange(1_000_000)
85+
noisy_sin = (3 + np.sin(x / 200) + np.random.randn(len(x)) / 10) * x / 1_000
86+
87+
88+
# when working in an IPython environment, this will automatically be a
89+
# FigureWidgetResampler else, this will be an FigureResampler
90+
f = go.Figure()
91+
f.add_trace({"y": noisy_sin + 2, "name": "yp2"})
92+
f
93+
94+
95+
FigureResampler
96+
^^^^^^^^^^^^^^^
5897

5998
.. code:: py
6099
@@ -69,6 +108,8 @@ Working examples ✅
69108
70109
fig.show_dash(mode='inline')
71110
111+
FigureWidget
112+
^^^^^^^^^^^^
72113
The gif below demonstrates the example usage of of :class:`FigureWidgetResampler <plotly_resampler.figure_resampler.FigureWidgetResampler>`, where ``JupyterLab`` is used as environment and the ``FigureWidgetResampler`` instance it's output is redirected into a new view. Also note how you are able to dynamically add traces!
73114

74115
.. image:: https://raw.githubusercontent.com/predict-idlab/plotly-resampler/main/docs/sphinx/_static/figurewidget.gif

0 commit comments

Comments
 (0)