Skip to content

Commit 0aad786

Browse files
authored
Merge pull request #104 from predict-idlab/improve_docs
📦 improve docs
2 parents 8ce4a49 + 8bed8a7 commit 0aad786

33 files changed

+2901
-951
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
venv*
22
.cache_datasets/
33
*.DS_Store
4+
file_system_store/
45

56
# Sphinx documentation
67
*_build/

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ In [this Plotly-Resampler demo](https://github.com/predict-idlab/plotly-resample
158158

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

build.py

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,13 @@
1818
def get_script_path():
1919
return os.path.dirname(os.path.realpath(sys.argv[0]))
2020

21+
2122
extensions = []
2223
if with_extensions:
2324
extensions = [
2425
Extension(
25-
name="plotly_resampler.aggregation.algorithms.lttbcv2",
26-
sources=["plotly_resampler/aggregation/algorithms/lttbcv2.c"],
26+
name="plotly_resampler.aggregation.algorithms.lttbc",
27+
sources=["plotly_resampler/aggregation/algorithms/lttbc.c"],
2728
define_macros=[("NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION")],
2829
include_dirs=[np.get_include(), get_script_path()],
2930
),
@@ -44,15 +45,26 @@ def run(self):
4445
try:
4546
build_ext.run(self)
4647
except (DistutilsPlatformError, FileNotFoundError) as e:
47-
print(" Unable to build the C extensions.")
48-
raise e
48+
print(
49+
" Unable to build the C extensions, will use the slower python "
50+
"fallback for LTTB"
51+
)
52+
print(e)
4953

5054
def build_extension(self, ext):
5155
try:
5256
build_ext.build_extension(self, ext)
53-
except (CCompilerError, DistutilsExecError, DistutilsPlatformError, ValueError) as e:
54-
print(' Unable to build the "{}" C extension, '.format(ext.name))
55-
raise e
57+
except (
58+
DistutilsPlatformError,
59+
CCompilerError,
60+
DistutilsExecError,
61+
ValueError,
62+
) as e:
63+
print(
64+
' Unable to build the "{}" C extension; '.format(ext.name)
65+
+ "will use the slower python fallback."
66+
)
67+
print(e)
5668

5769

5870
def build(setup_kwargs):

docs/sphinx/FAQ.rst

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
.. role:: raw-html(raw)
2+
:format: html
3+
4+
.. |br| raw:: html
5+
6+
<br>
7+
8+
9+
FAQ ❓
10+
======
11+
12+
.. raw:: html
13+
14+
<details>
15+
<summary>
16+
<a><b>What does the orange <b style="color:orange">~ time|number </b> suffix in legend name indicate?</b></a>
17+
</summary>
18+
<div style="margin-left:1em">
19+
20+
21+
This tilde suffix is only shown when the data is aggregated and represents the *mean aggregation bin size* which is the mean index-range difference between two consecutive aggregated samples.
22+
23+
* for *time-indexed data*: the mean time-range between 2 consecutive (sampled) samples.
24+
* for *numeric-indexed data*: the mean numeric range between 2 consecutive (sampled) samples.
25+
26+
When the index is a range-index; the *mean aggregation bin size* represents the *mean* downsample ratio; i.e., the mean number of samples that are aggregated into one sample.
27+
28+
.. raw:: html
29+
30+
</div>
31+
</details>
32+
<br>
33+
<details>
34+
<summary>
35+
<a><b>What is the difference between plotly-resampler figures and plain plotly figures?</b></a>
36+
</summary>
37+
<div style="margin-left:1em">
38+
39+
plotly-resampler can be thought of as wrapper around plain plotly figures which adds line-chart visualization scalability by dynamically aggregating the data of the figures w.r.t. the front-end view. plotly-resampler thus adds dynamic aggregation functionality to plain plotly figures.
40+
41+
**important to know**:
42+
43+
* ``show`` *always* returns a static html view of the figure, i.e., no dynamic aggregation can be performed on that view.
44+
* To have dynamic aggregation:
45+
46+
* with ``FigureResampler``, you need to call ``show_dash`` (or output the object in a cell via ``IPython.display``) -> which spawns a dash-web app, and the dynamic aggregation is realized with dash callback
47+
* with ``FigureWidgetResampler``, you need to use ``IPython.display`` on the object, which uses widget-events to realize dynamic aggregation (via the running IPython kernel).
48+
49+
.. raw:: html
50+
51+
</div>
52+
</details>
53+
<br>
54+
<details>
55+
<summary>
56+
<a><b>What does <code><a href="https://github.com/predict-idlab/trace-updater" target="_blank">TraceUpdater</a></code> do?</b></a>
57+
</summary>
58+
<div style="margin-left:1em">
59+
60+
The ``TraceUpdater`` class is a custom dash component that aids ``dcc.Graph`` components to efficiently send and update (in our case aggregated) data to the front-end.
61+
62+
For more information on how to use the trace-updater component together with the ``FigureResampler``, see our dash app `examples <https://github.com/predict-idlab/plotly-resampler/tree/main/examples>`_` and look at the `trace-updater <https://github.com/predict-idlab/trace-updater/blob/master/trace_updater/TraceUpdater.py>`_ its documentation.
63+
64+
.. raw:: html
65+
66+
</div>
67+
</details>
68+
<br>
69+
<details>
70+
<summary>
71+
<a><b>What is the difference in approach between plotly-resampler and datashader?</b></a>
72+
</summary>
73+
<div style="margin-left:1em">
74+
75+
76+
`Datashader <https://datashader.org/getting_started/Introduction.html>`_ is a highly scalable `open-source <https://github.com/holoviz/datashader>`_ library for analyzing and visualizing large datasets. More specifically, datashader *“rasterizes”* or *“aggregates”* datasets into regular grids that can be analyzed further or viewed as **images**.
77+
78+
79+
**The main differences are**:
80+
81+
Datashader can deal with various kinds of data (e.g., location related data, point clouds), whereas plotly-resampler is more tailored towards time-series data visualizations.
82+
Furthermore, datashader outputs a **rasterized image/array** encompassing all traces their data, whereas plotly-resampler outputs an **aggregated series** per trace. Thus, datashader is more suited for analyzing data where you do not want to pin-out a certain series/trace.
83+
84+
In our opinion, datashader truly shines (for the time series use case) when:
85+
86+
* you want a global, overlaying view of all your traces
87+
* you want to visualize a large number of time series in a single plot (many traces)
88+
* there is a lot of noise on your high-frequency data and you want to uncover the underlying pattern
89+
* you want to render all data points in your visualization
90+
91+
In our opinion, plotly-resampler shines when:
92+
93+
* you need the capabilities to interact with the traces (e.g., hovering, toggling traces, hovertext per trace)
94+
* you want to use a less complex (but more restricted) visualization interface (as opposed to holoviews), i.e., plotly
95+
* you want to make existing plotly time-series figures more scalable and efficient
96+
* to build scalable Dash apps for time-series data visualization
97+
98+
Furthermore combined with holoviews, datashader can also be employed in an interactive manner, see the example below.
99+
100+
.. code:: python
101+
102+
from holoviews.operation.datashader import datashade
103+
import datashader as ds
104+
import holoviews as hv
105+
import numpy as np
106+
import pandas as pd
107+
import panel as pn
108+
109+
hv.extension("bokeh")
110+
pn.extension(comms='ipywidgets')
111+
112+
# Create the dummy dataframe
113+
n = 1_000_000
114+
x = np.arange(n)
115+
noisy_sine = (np.sin(x / 3_000) + (np.random.randn(n) / 10)) * x / 5_000
116+
df = pd.DataFrame(
117+
{"ns": noisy_sine, "ns_abs": np.abs(noisy_sine),}
118+
)
119+
120+
# Visualize interactively with datashader
121+
opts = hv.opts.RGB(width=800, height=400)
122+
ndoverlay = hv.NdOverlay({c:hv.Curve((df.index, df[c])) for c in df.columns})
123+
datashade(ndoverlay, cnorm='linear', aggregator=ds.count(), line_width=3).opts(opts)
124+
125+
.. image:: _static/datashader.png
126+
127+
128+
.. raw:: html
129+
130+
</div>
131+
</details>
132+
<br>
133+
<details>
134+
<summary>
135+
<a><b>I get errors such as:</b><br><ul><li>
136+
<code>RuntimeError: module compiled against API version 0x10 but this version of numpy is 0xe</code></li>
137+
<li><code>ImportError: numpy.core.multiarray failed to import</code></li>
138+
</ul>
139+
</a>
140+
</summary>
141+
<div style="margin-left:1em">
142+
143+
Plotly-resampler uses compiled C code (which uses the NumPy C API) to speed up the LTTB data-aggregation algorithm. This C code gets compiled during the building stage of the package (which might be before you install the package).<br><br>
144+
If this C extension was build against a more recent NumPy version than your local version, you obtain a
145+
<a href="https://numpy.org/devdocs/user/troubleshooting-importerror.html#c-api-incompatibility"><i>NumPy C-API incompatibility</i></a>
146+
and the above error will be raised.<br><br>
147+
148+
These above mentioned errors can thus be resolved by running<br>
149+
&nbsp;&nbsp;&nbsp;&nbsp;<code>pip install --upgrade numpy</code><br>
150+
and reinstalling plotly-resampler afterwards.<br><br>
151+
152+
For more information about compatibility and building upon NumPy, you can consult
153+
<a href="https://numpy.org/doc/stable/user/depending_on_numpy.html?highlight=compiled#for-downstream-package-authors">NumPy's docs for downstream package authors</a>.
154+
155+
We aim to limit this issue as much as possible (by for example using <a href="https://github.com/scipy/oldest-supported-numpy">oldest-supported-numpy</a> in our build.py),
156+
but if you still experience issues, please open an issue on <a href="https://github.com/predict-idlab/plotly-resampler/issues">GitHub</a>.
157+
158+
.. raw:: html
159+
160+
</div>
161+
</details>
162+
<br>
3.69 MB
Loading

docs/sphinx/_static/datashader.png

69.2 KB
Loading

docs/sphinx/dash_app_integration.rst

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77

88

99

10-
Dash integration 🤝
11-
===================
10+
Dash apps 🤝
11+
============
1212

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

@@ -48,16 +48,19 @@ When you add a :class:`FigureResampler <plotly_resampler.figure_resampler.Figure
4848
fig.register_update_graph_callback(app, "graph-id", "trace-updater")
4949
5050
51+
.. warning::
52+
53+
The above example serves as an illustration, but uses a *global variable* to store the ``FigureResampler`` instance; this is not a good practice.
54+
Ideally you should cache the ``FigureResampler`` per session on the server side.
55+
In our `examples folder <https://github.com/predict-idlab/plotly-resampler/tree/main/examples>`_, we provide several dash app examples where we perform server side caching of such figures.
56+
57+
5158
.. tip::
5259

5360
You can make the resampling faster by ensuring the
5461
`TraceUpdater <https://github.com/predict-idlab/trace-updater>`_ its
5562
``sequentialUpdate`` argument is set to ``False``.
5663

57-
58-
* `This TraceUpdater-example <https://github.com/predict-idlab/trace-updater/blob/master/usage.py>`_ serves as a minimal working example.
59-
60-
6164
Limitations
6265
-----------
6366
``plotly_resampler`` relies on `TraceUpdater <https://github.com/predict-idlab/trace-updater>`_ to ensure that the *updateData* is sent

docs/sphinx/getting_started.rst

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
.. role:: raw-html(raw)
22
:format: html
33

4-
Getting started 🚀
5-
==================
4+
Get started 🚀
5+
==============
66

77
``plotly-resampler`` serves two main **modules**:
88

@@ -56,7 +56,7 @@ Dynamic resampling callbacks are realized:
5656

5757
**To add dynamic resampling using a FigureWidget, you should**:
5858
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
59+
2. output the ``FigureWidgetResampler`` instance in a cell
6060

6161
.. tip::
6262

@@ -114,6 +114,17 @@ The gif below demonstrates the example usage of of :class:`FigureWidgetResampler
114114

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

117+
118+
.. raw:: html
119+
120+
<br><br>
121+
122+
123+
Furthermore, plotly's ``FigureWidget`` allows to conveniently add callbacks to for example click events. This allows creating a high-frequency time series annotation app in a couple of lines; as shown in the gif below and in `this notebook <https://github.com/predict-idlab/plotly-resampler/blob/main/examples/figurewidget_example.ipynb>`_.
124+
125+
126+
.. image:: _static/annotate_twitter.gif
127+
117128
Important considerations & tips 🚨
118129
----------------------------------
119130

@@ -162,7 +173,7 @@ Working example ⬇️:
162173
.. tip::
163174

164175
The ``FigureWidgetResampler`` graph will not be automatically redrawn after
165-
adjusting the fig its `hf_data` property,. The redrawning can be triggered by
176+
adjusting the fig its `hf_data` property,. The redrawing can be triggered by
166177
manually calling either:
167178

168179
* :func:`FigureWidgetResampler.reload_data <plotly_resampler.figure_resampler.FigureWidgetResampler.reload_data>`, which keeps the current-graph range.

docs/sphinx/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ As shown in the demo above, ``plotly-resampler`` maintains its interactiveness o
4040

4141
getting_started
4242
dash_app_integration
43-
4443
api_reference
44+
FAQ
4545

4646

4747

0 commit comments

Comments
 (0)