Skip to content

figure_resampler

Module withholding wrappers for the plotly go.Figure and go.FigureWidget class which allows bookkeeping and back-end based resampling of high-frequency sequential data.

Tip

The term high-frequency actually refers very large amounts of sequential data.

FigureResampler

Bases: AbstractFigureAggregator, go.Figure

Data aggregation functionality for go.Figures.

Source code in plotly_resampler/figure_resampler/figure_resampler.py
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
class FigureResampler(AbstractFigureAggregator, go.Figure):
    """Data aggregation functionality for ``go.Figures``."""

    def __init__(
        self,
        figure: BaseFigure | dict = None,
        convert_existing_traces: bool = True,
        default_n_shown_samples: int = 1000,
        default_downsampler: AbstractAggregator = MinMaxLTTB(),
        default_gap_handler: AbstractGapHandler = MedDiffGapHandler(),
        resampled_trace_prefix_suffix: Tuple[str, str] = (
            '<b style="color:sandybrown">[R]</b> ',
            "",
        ),
        show_mean_aggregation_size: bool = True,
        convert_traces_kwargs: dict | None = None,
        verbose: bool = False,
        show_dash_kwargs: dict | None = None,
    ):
        """Initialize a dynamic aggregation data mirror using a dash web app.

        Parameters
        ----------
        figure: BaseFigure
            The figure that will be decorated. Can be either an empty figure
            (e.g., ``go.Figure()``, ``make_subplots()``, ``go.FigureWidget``) or an
            existing figure.
        convert_existing_traces: bool
            A bool indicating whether the high-frequency traces of the passed ``figure``
            should be resampled, by default True. Hence, when set to False, the
            high-frequency traces of the passed ``figure`` will not be resampled.
        default_n_shown_samples: int, optional
            The default number of samples that will be shown for each trace,
            by default 1000.\n
            !!! note
                - This can be overridden within the [`add_trace`][figure_resampler.figure_resampler_interface.AbstractFigureAggregator.add_trace] method.
                - If a trace withholds fewer datapoints than this parameter,
                  the data will *not* be aggregated.
        default_downsampler: AbstractAggregator, optional
            An instance which implements the AbstractAggregator interface and
            will be used as default downsampler, by default ``MinMaxLTTB`` with
            ``MinMaxLTTB`` is a heuristic to the LTTB algorithm that uses pre-selection
            of min-max values (default 4 per bin) to speed up LTTB (as now only 4 values
            per bin are considered by LTTB). This min-max ratio of 4 can be changed by
            initializing ``MinMaxLTTB`` with a different value for the ``minmax_ratio``
            parameter. \n
            !!! note
                This can be overridden within the [`add_trace`][figure_resampler.figure_resampler_interface.AbstractFigureAggregator.add_trace] method.
        default_gap_handler: AbstractGapHandler, optional
            An instance which implements the AbstractGapHandler interface and
            will be used as default gap handler, by default ``MedDiffGapHandler``.
            ``MedDiffGapHandler`` will determine gaps by first calculating the median
            aggregated x difference and then thresholding the aggregated x delta on a
            multiple of this median difference.  \n
            !!! note
                This can be overridden within the [`add_trace`][figure_resampler.figure_resampler_interface.AbstractFigureAggregator.add_trace] method.
        resampled_trace_prefix_suffix: str, optional
            A tuple which contains the ``prefix`` and ``suffix``, respectively, which
            will be added to the trace its legend-name when a resampled version of the
            trace is shown. By default a bold, orange ``[R]`` is shown as prefix
            (no suffix is shown).
        show_mean_aggregation_size: bool, optional
            Whether the mean aggregation bin size will be added as a suffix to the trace
            its legend-name, by default True.
        convert_traces_kwargs: dict, optional
            A dict of kwargs that will be passed to the [`add_trace`][figure_resampler.figure_resampler_interface.AbstractFigureAggregator.add_trace] method and
            will be used to convert the existing traces. \n
            !!! note
                This argument is only used when the passed ``figure`` contains data and
                ``convert_existing_traces`` is set to True.
        verbose: bool, optional
            Whether some verbose messages will be printed or not, by default False.
        show_dash_kwargs: dict, optional
            A dict that will be used as default kwargs for the [`show_dash`][figure_resampler.figure_resampler.FigureResampler.show_dash] method.
            Note that the passed kwargs will be take precedence over these defaults.

        """
        # Parse the figure input before calling `super`
        if is_figure(figure) and not is_fr(figure):
            # A go.Figure
            # => base case: the figure does not need to be adjusted
            f = figure
        else:
            # Create a new figure object and make sure that the trace uid will not get
            # adjusted when they are added.
            f = self._get_figure_class(go.Figure)()
            f._data_validator.set_uid = False

            if isinstance(figure, BaseFigure):
                # A base figure object, can be;
                # - a go.FigureWidget
                # - a plotly-resampler figure: subclass of AbstractFigureAggregator
                # => we first copy the layout, grid_str and grid ref
                f.layout = figure.layout
                f._grid_str = figure._grid_str
                f._grid_ref = figure._grid_ref
                f.add_traces(figure.data)
            elif isinstance(figure, dict) and (
                "data" in figure or "layout" in figure  # or "frames" in figure  # TODO
            ):
                # A figure as a dict, can be;
                # - a plotly figure as a dict (after calling `fig.to_dict()`)
                # - a pickled (plotly-resampler) figure (after loading a pickled figure)
                # => we first copy the layout, grid_str and grid ref
                f.layout = figure.get("layout")
                f._grid_str = figure.get("_grid_str")
                f._grid_ref = figure.get("_grid_ref")
                f.add_traces(figure.get("data"))
                # `pr_props` is not None when loading a pickled plotly-resampler figure
                f._pr_props = figure.get("pr_props")
                # `f._pr_props`` is an attribute to store properties of a
                # plotly-resampler figure. This attribute is only used to pass
                # information to the super() constructor. Once the super constructor is
                # called, the attribute is removed.

                # f.add_frames(figure.get("frames")) TODO
            elif isinstance(figure, (dict, list)):
                # A single trace dict or a list of traces
                f.add_traces(figure)

        self._show_dash_kwargs = (
            show_dash_kwargs if show_dash_kwargs is not None else {}
        )

        super().__init__(
            f,
            convert_existing_traces,
            default_n_shown_samples,
            default_downsampler,
            default_gap_handler,
            resampled_trace_prefix_suffix,
            show_mean_aggregation_size,
            convert_traces_kwargs,
            verbose,
        )

        if isinstance(figure, AbstractFigureAggregator):
            # Copy the `_hf_data` if the previous figure was an AbstractFigureAggregator
            # and adjust the default `max_n_samples` and `downsampler`
            self._hf_data.update(
                self._copy_hf_data(figure._hf_data, adjust_default_values=True)
            )

            # Note: This hack ensures that the this figure object initially uses
            # data of the whole view. More concretely; we create a dict
            # serialization figure and adjust the hf-traces to the whole view
            # with the check-update method (by passing no range / filter args)
            with self.batch_update():
                graph_dict: dict = self._get_current_graph()
                update_indices = self._check_update_figure_dict(graph_dict)
                for idx in update_indices:
                    self.data[idx].update(graph_dict["data"][idx])

        # The FigureResampler needs a dash app
        self._app: dash.Dash | None = None
        self._port: int | None = None
        self._host: str | None = None
        # Certain functions will be different when using persistent inline
        # (namely `show_dash` and `stop_callback`)
        self._is_persistent_inline = False

    def show_dash(
        self,
        mode=None,
        config: dict | None = None,
        graph_properties: dict | None = None,
        **kwargs,
    ):
        """Registers the `update_graph` callback & show the figure in a dash app.

        Parameters
        ----------
        mode: str, optional
            Display mode. One of:\n
              * ``"external"``: The URL of the app will be displayed in the notebook
                output cell. Clicking this URL will open the app in the default
                web browser.
              * ``"inline"``: The app will be displayed inline in the notebook output
                cell in an iframe.
              * ``"inline_persistent"``: The app will be displayed inline in the
                notebook output cell in an iframe, if the app is not reachable a static
                image of the figure is shown. Hence this is a persistent version of the
                ``"inline"`` mode, allowing users to see a static figure in other
                environments, browsers, etc.

                !!! note

                    This mode requires the ``kaleido`` and ``flask_cors`` package.
                    Install them : ``pip install plotly_resampler[inline_persistent]``
                    or ``pip install kaleido flask_cors``.

              * ``"jupyterlab"``: The app will be displayed in a dedicated tab in the
                JupyterLab interface. Requires JupyterLab and the ``jupyterlab-dash``
                extension.
            By default None, which will result in the same behavior as ``"external"``.
        config: dict, optional
            The configuration options for displaying this figure, by default None.
            This ``config`` parameter is the same as the dict that you would pass as
            ``config`` argument to the `show` method.
            See more [https://plotly.com/python/configuration-options/](https://plotly.com/python/configuration-options/)
        graph_properties: dict, optional
            Dictionary of (keyword, value) for the properties that should be passed to
            the dcc.Graph, by default None.
            e.g.: `{"style": {"width": "50%"}}`
            Note: "config" is not allowed as key in this dict, as there is a distinct
            ``config`` parameter for this property in this method.
            See more [https://dash.plotly.com/dash-core-components/graph](https://dash.plotly.com/dash-core-components/graph)
        **kwargs: dict
            Additional app.run_server() kwargs. e.g.: port, ...
            Also note that these kwargs take precedence over the ones passed to the
            constructor via the ``show_dash_kwargs`` argument.

        """
        available_modes = ["external", "inline", "inline_persistent", "jupyterlab"]
        assert (
            mode is None or mode in available_modes
        ), f"mode must be one of {available_modes}"
        graph_properties = {} if graph_properties is None else graph_properties
        assert "config" not in graph_properties  # There is a param for config

        # 0. Check if the traces need to be updated when there is a xrange set
        # This will be the case when the users has set a xrange (via the `update_layout`
        # or `update_xaxes` methods`)
        relayout_dict = {}
        for xaxis_str in self._xaxis_list:
            x_range = self.layout[xaxis_str].range
            if x_range:  # when not None
                relayout_dict[f"{xaxis_str}.range[0]"] = x_range[0]
                relayout_dict[f"{xaxis_str}.range[1]"] = x_range[1]
        if relayout_dict:  # when not empty
            update_data = self.construct_update_data(relayout_dict)

            if not self._is_no_update(update_data):  # when there is an update
                with self.batch_update():
                    # First update the layout (first item of update_data)
                    self.layout.update(self._parse_relayout(update_data[0]))

                    # Then update the data
                    for updated_trace in update_data[1:]:
                        trace_idx = updated_trace.pop("index")
                        self.data[trace_idx].update(updated_trace)

        # 1. Construct the Dash app layout
        if mode == "inline_persistent":
            mode = "inline"
            if _jupyter_dash_installed:
                # Inline persistent mode: we display a static image of the figure when the
                # app is not reachable
                # Note: this is the "inline" behavior of JupyterDashInlinePersistentOutput
                app = JupyterDashPersistentInlineOutput("local_app")
                self._is_persistent_inline = True
            else:
                # If Jupyter Dash is not installed, inline persistent won't work and hence
                # we default to normal inline mode with a normal Dash app
                app = dash.Dash("local_app")
                warnings.warn(
                    "'jupyter_dash' is not installed. The persistent inline mode will not work. Defaulting to standard inline mode."
                )
        else:
            # jupyter dash uses a normal Dash app as figure
            app = dash.Dash("local_app")
        app.layout = dash.html.Div(
            [
                dash.dcc.Graph(
                    id="resample-figure", figure=self, config=config, **graph_properties
                ),
                TraceUpdater(
                    id="trace-updater", gdID="resample-figure", sequentialUpdate=False
                ),
            ]
        )
        self.register_update_graph_callback(app, "resample-figure", "trace-updater")

        height_param = "height" if self._is_persistent_inline else "jupyter_height"

        # 2. Run the app
        if mode == "inline" and height_param not in kwargs:
            # If app height is not specified -> re-use figure height for inline dash app
            #  Note: default layout height is 450 (whereas default app height is 650)
            #  See: https://plotly.com/python/reference/layout/#layout-height
            fig_height = self.layout.height if self.layout.height is not None else 450
            kwargs[height_param] = fig_height + 18

        # kwargs take precedence over the show_dash_kwargs
        kwargs = {**self._show_dash_kwargs, **kwargs}

        # Store the app information, so it can be killed
        self._app = app
        self._host = kwargs.get("host", "127.0.0.1")
        self._port = kwargs.get("port", "8050")

        # function signature is slightly different for the Dash and JupyterDash implementations
        if self._is_persistent_inline:
            app.run(mode=mode, **kwargs)
        else:
            app.run(jupyter_mode=mode, **kwargs)

    def stop_server(self, warn: bool = True):
        """Stop the running dash-app.

        Parameters
        ----------
        warn: bool
            Whether a warning message will be shown or  not, by default True.

        !!! warning

            This only works if the dash-app was started with [`show_dash`][figure_resampler.figure_resampler.FigureResampler.show_dash].
        """
        if self._app is not None:
            servers_dict = (
                self._app._server_threads
                if self._is_persistent_inline
                else dash.jupyter_dash._servers
            )
            old_server = servers_dict.get((self._host, self._port))
            if old_server:
                if self._is_persistent_inline:
                    old_server.kill()
                    old_server.join()
                else:
                    old_server.shutdown()
            del servers_dict[(self._host, self._port)]
        elif warn:
            warnings.warn(
                "Could not stop the server, either the \n"
                + "\t- 'show-dash' method was not called, or \n"
                + "\t- the dash-server wasn't started with 'show_dash'"
            )

    def register_update_graph_callback(
        self, app: dash.Dash, graph_id: str, trace_updater_id: str
    ):
        """Register the [`construct_update_data`][figure_resampler.figure_resampler_interface.AbstractFigureAggregator.construct_update_data] method as callback function to
        the passed dash-app.

        Parameters
        ----------
        app: Union[dash.Dash, JupyterDash]
            The app in which the callback will be registered.
        graph_id:
            The id of the ``dcc.Graph``-component which withholds the to-be resampled
            Figure.
        trace_updater_id
            The id of the ``TraceUpdater`` component. This component is leveraged by
            ``FigureResampler`` to efficiently POST the to-be-updated data to the
            front-end.

        """
        app.callback(
            dash.dependencies.Output(trace_updater_id, "updateData"),
            dash.dependencies.Input(graph_id, "relayoutData"),
            prevent_initial_call=True,
        )(self.construct_update_data)

    def _get_pr_props_keys(self) -> List[str]:
        # Add the additional plotly-resampler properties of this class
        return super()._get_pr_props_keys() + ["_show_dash_kwargs"]

    def _ipython_display_(self):
        # To display the figure inline as a dash app
        self.show_dash(mode="inline")

__init__(figure=None, convert_existing_traces=True, default_n_shown_samples=1000, default_downsampler=MinMaxLTTB(), default_gap_handler=MedDiffGapHandler(), resampled_trace_prefix_suffix=('<b style="color:sandybrown">[R]</b> ', ''), show_mean_aggregation_size=True, convert_traces_kwargs=None, verbose=False, show_dash_kwargs=None)

Initialize a dynamic aggregation data mirror using a dash web app.

Parameters:

Name Type Description Default
figure BaseFigure | dict

The figure that will be decorated. Can be either an empty figure (e.g., go.Figure(), make_subplots(), go.FigureWidget) or an existing figure.

None
convert_existing_traces bool

A bool indicating whether the high-frequency traces of the passed figure should be resampled, by default True. Hence, when set to False, the high-frequency traces of the passed figure will not be resampled.

True
default_n_shown_samples int

The default number of samples that will be shown for each trace, by default 1000.

Note

  • This can be overridden within the add_trace method.
  • If a trace withholds fewer datapoints than this parameter, the data will not be aggregated.
1000
default_downsampler AbstractAggregator

An instance which implements the AbstractAggregator interface and will be used as default downsampler, by default MinMaxLTTB with MinMaxLTTB is a heuristic to the LTTB algorithm that uses pre-selection of min-max values (default 4 per bin) to speed up LTTB (as now only 4 values per bin are considered by LTTB). This min-max ratio of 4 can be changed by initializing MinMaxLTTB with a different value for the minmax_ratio parameter.

Note

This can be overridden within the add_trace method.

MinMaxLTTB()
default_gap_handler AbstractGapHandler

An instance which implements the AbstractGapHandler interface and will be used as default gap handler, by default MedDiffGapHandler. MedDiffGapHandler will determine gaps by first calculating the median aggregated x difference and then thresholding the aggregated x delta on a multiple of this median difference.

Note

This can be overridden within the add_trace method.

MedDiffGapHandler()
resampled_trace_prefix_suffix Tuple[str, str]

A tuple which contains the prefix and suffix, respectively, which will be added to the trace its legend-name when a resampled version of the trace is shown. By default a bold, orange [R] is shown as prefix (no suffix is shown).

('<b style="color:sandybrown">[R]</b> ', '')
show_mean_aggregation_size bool

Whether the mean aggregation bin size will be added as a suffix to the trace its legend-name, by default True.

True
convert_traces_kwargs dict | None

A dict of kwargs that will be passed to the add_trace method and will be used to convert the existing traces.

Note

This argument is only used when the passed figure contains data and convert_existing_traces is set to True.

None
verbose bool

Whether some verbose messages will be printed or not, by default False.

False
show_dash_kwargs dict | None

A dict that will be used as default kwargs for the show_dash method. Note that the passed kwargs will be take precedence over these defaults.

None
Source code in plotly_resampler/figure_resampler/figure_resampler.py
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
def __init__(
    self,
    figure: BaseFigure | dict = None,
    convert_existing_traces: bool = True,
    default_n_shown_samples: int = 1000,
    default_downsampler: AbstractAggregator = MinMaxLTTB(),
    default_gap_handler: AbstractGapHandler = MedDiffGapHandler(),
    resampled_trace_prefix_suffix: Tuple[str, str] = (
        '<b style="color:sandybrown">[R]</b> ',
        "",
    ),
    show_mean_aggregation_size: bool = True,
    convert_traces_kwargs: dict | None = None,
    verbose: bool = False,
    show_dash_kwargs: dict | None = None,
):
    """Initialize a dynamic aggregation data mirror using a dash web app.

    Parameters
    ----------
    figure: BaseFigure
        The figure that will be decorated. Can be either an empty figure
        (e.g., ``go.Figure()``, ``make_subplots()``, ``go.FigureWidget``) or an
        existing figure.
    convert_existing_traces: bool
        A bool indicating whether the high-frequency traces of the passed ``figure``
        should be resampled, by default True. Hence, when set to False, the
        high-frequency traces of the passed ``figure`` will not be resampled.
    default_n_shown_samples: int, optional
        The default number of samples that will be shown for each trace,
        by default 1000.\n
        !!! note
            - This can be overridden within the [`add_trace`][figure_resampler.figure_resampler_interface.AbstractFigureAggregator.add_trace] method.
            - If a trace withholds fewer datapoints than this parameter,
              the data will *not* be aggregated.
    default_downsampler: AbstractAggregator, optional
        An instance which implements the AbstractAggregator interface and
        will be used as default downsampler, by default ``MinMaxLTTB`` with
        ``MinMaxLTTB`` is a heuristic to the LTTB algorithm that uses pre-selection
        of min-max values (default 4 per bin) to speed up LTTB (as now only 4 values
        per bin are considered by LTTB). This min-max ratio of 4 can be changed by
        initializing ``MinMaxLTTB`` with a different value for the ``minmax_ratio``
        parameter. \n
        !!! note
            This can be overridden within the [`add_trace`][figure_resampler.figure_resampler_interface.AbstractFigureAggregator.add_trace] method.
    default_gap_handler: AbstractGapHandler, optional
        An instance which implements the AbstractGapHandler interface and
        will be used as default gap handler, by default ``MedDiffGapHandler``.
        ``MedDiffGapHandler`` will determine gaps by first calculating the median
        aggregated x difference and then thresholding the aggregated x delta on a
        multiple of this median difference.  \n
        !!! note
            This can be overridden within the [`add_trace`][figure_resampler.figure_resampler_interface.AbstractFigureAggregator.add_trace] method.
    resampled_trace_prefix_suffix: str, optional
        A tuple which contains the ``prefix`` and ``suffix``, respectively, which
        will be added to the trace its legend-name when a resampled version of the
        trace is shown. By default a bold, orange ``[R]`` is shown as prefix
        (no suffix is shown).
    show_mean_aggregation_size: bool, optional
        Whether the mean aggregation bin size will be added as a suffix to the trace
        its legend-name, by default True.
    convert_traces_kwargs: dict, optional
        A dict of kwargs that will be passed to the [`add_trace`][figure_resampler.figure_resampler_interface.AbstractFigureAggregator.add_trace] method and
        will be used to convert the existing traces. \n
        !!! note
            This argument is only used when the passed ``figure`` contains data and
            ``convert_existing_traces`` is set to True.
    verbose: bool, optional
        Whether some verbose messages will be printed or not, by default False.
    show_dash_kwargs: dict, optional
        A dict that will be used as default kwargs for the [`show_dash`][figure_resampler.figure_resampler.FigureResampler.show_dash] method.
        Note that the passed kwargs will be take precedence over these defaults.

    """
    # Parse the figure input before calling `super`
    if is_figure(figure) and not is_fr(figure):
        # A go.Figure
        # => base case: the figure does not need to be adjusted
        f = figure
    else:
        # Create a new figure object and make sure that the trace uid will not get
        # adjusted when they are added.
        f = self._get_figure_class(go.Figure)()
        f._data_validator.set_uid = False

        if isinstance(figure, BaseFigure):
            # A base figure object, can be;
            # - a go.FigureWidget
            # - a plotly-resampler figure: subclass of AbstractFigureAggregator
            # => we first copy the layout, grid_str and grid ref
            f.layout = figure.layout
            f._grid_str = figure._grid_str
            f._grid_ref = figure._grid_ref
            f.add_traces(figure.data)
        elif isinstance(figure, dict) and (
            "data" in figure or "layout" in figure  # or "frames" in figure  # TODO
        ):
            # A figure as a dict, can be;
            # - a plotly figure as a dict (after calling `fig.to_dict()`)
            # - a pickled (plotly-resampler) figure (after loading a pickled figure)
            # => we first copy the layout, grid_str and grid ref
            f.layout = figure.get("layout")
            f._grid_str = figure.get("_grid_str")
            f._grid_ref = figure.get("_grid_ref")
            f.add_traces(figure.get("data"))
            # `pr_props` is not None when loading a pickled plotly-resampler figure
            f._pr_props = figure.get("pr_props")
            # `f._pr_props`` is an attribute to store properties of a
            # plotly-resampler figure. This attribute is only used to pass
            # information to the super() constructor. Once the super constructor is
            # called, the attribute is removed.

            # f.add_frames(figure.get("frames")) TODO
        elif isinstance(figure, (dict, list)):
            # A single trace dict or a list of traces
            f.add_traces(figure)

    self._show_dash_kwargs = (
        show_dash_kwargs if show_dash_kwargs is not None else {}
    )

    super().__init__(
        f,
        convert_existing_traces,
        default_n_shown_samples,
        default_downsampler,
        default_gap_handler,
        resampled_trace_prefix_suffix,
        show_mean_aggregation_size,
        convert_traces_kwargs,
        verbose,
    )

    if isinstance(figure, AbstractFigureAggregator):
        # Copy the `_hf_data` if the previous figure was an AbstractFigureAggregator
        # and adjust the default `max_n_samples` and `downsampler`
        self._hf_data.update(
            self._copy_hf_data(figure._hf_data, adjust_default_values=True)
        )

        # Note: This hack ensures that the this figure object initially uses
        # data of the whole view. More concretely; we create a dict
        # serialization figure and adjust the hf-traces to the whole view
        # with the check-update method (by passing no range / filter args)
        with self.batch_update():
            graph_dict: dict = self._get_current_graph()
            update_indices = self._check_update_figure_dict(graph_dict)
            for idx in update_indices:
                self.data[idx].update(graph_dict["data"][idx])

    # The FigureResampler needs a dash app
    self._app: dash.Dash | None = None
    self._port: int | None = None
    self._host: str | None = None
    # Certain functions will be different when using persistent inline
    # (namely `show_dash` and `stop_callback`)
    self._is_persistent_inline = False

register_update_graph_callback(app, graph_id, trace_updater_id)

Register the construct_update_data method as callback function to the passed dash-app.

Parameters:

Name Type Description Default
app dash.Dash

The app in which the callback will be registered.

required
graph_id str

The id of the dcc.Graph-component which withholds the to-be resampled Figure.

required
trace_updater_id str

The id of the TraceUpdater component. This component is leveraged by FigureResampler to efficiently POST the to-be-updated data to the front-end.

required
Source code in plotly_resampler/figure_resampler/figure_resampler.py
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
def register_update_graph_callback(
    self, app: dash.Dash, graph_id: str, trace_updater_id: str
):
    """Register the [`construct_update_data`][figure_resampler.figure_resampler_interface.AbstractFigureAggregator.construct_update_data] method as callback function to
    the passed dash-app.

    Parameters
    ----------
    app: Union[dash.Dash, JupyterDash]
        The app in which the callback will be registered.
    graph_id:
        The id of the ``dcc.Graph``-component which withholds the to-be resampled
        Figure.
    trace_updater_id
        The id of the ``TraceUpdater`` component. This component is leveraged by
        ``FigureResampler`` to efficiently POST the to-be-updated data to the
        front-end.

    """
    app.callback(
        dash.dependencies.Output(trace_updater_id, "updateData"),
        dash.dependencies.Input(graph_id, "relayoutData"),
        prevent_initial_call=True,
    )(self.construct_update_data)

show_dash(mode=None, config=None, graph_properties=None, **kwargs)

Registers the update_graph callback & show the figure in a dash app.

Parameters:

Name Type Description Default
mode

Display mode. One of:

  • "external": The URL of the app will be displayed in the notebook output cell. Clicking this URL will open the app in the default web browser.
  • "inline": The app will be displayed inline in the notebook output cell in an iframe.
  • "inline_persistent": The app will be displayed inline in the notebook output cell in an iframe, if the app is not reachable a static image of the figure is shown. Hence this is a persistent version of the "inline" mode, allowing users to see a static figure in other environments, browsers, etc.

Note

This mode requires the kaleido and flask_cors package. Install them : pip install plotly_resampler[inline_persistent] or pip install kaleido flask_cors.

  • "jupyterlab": The app will be displayed in a dedicated tab in the JupyterLab interface. Requires JupyterLab and the jupyterlab-dash extension. By default None, which will result in the same behavior as "external".
None
config dict | None

The configuration options for displaying this figure, by default None. This config parameter is the same as the dict that you would pass as config argument to the show method. See more https://plotly.com/python/configuration-options/

None
graph_properties dict | None

Dictionary of (keyword, value) for the properties that should be passed to the dcc.Graph, by default None. e.g.: {"style": {"width": "50%"}} Note: “config” is not allowed as key in this dict, as there is a distinct config parameter for this property in this method. See more https://dash.plotly.com/dash-core-components/graph

None
**kwargs

Additional app.run_server() kwargs. e.g.: port, … Also note that these kwargs take precedence over the ones passed to the constructor via the show_dash_kwargs argument.

{}
Source code in plotly_resampler/figure_resampler/figure_resampler.py
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
def show_dash(
    self,
    mode=None,
    config: dict | None = None,
    graph_properties: dict | None = None,
    **kwargs,
):
    """Registers the `update_graph` callback & show the figure in a dash app.

    Parameters
    ----------
    mode: str, optional
        Display mode. One of:\n
          * ``"external"``: The URL of the app will be displayed in the notebook
            output cell. Clicking this URL will open the app in the default
            web browser.
          * ``"inline"``: The app will be displayed inline in the notebook output
            cell in an iframe.
          * ``"inline_persistent"``: The app will be displayed inline in the
            notebook output cell in an iframe, if the app is not reachable a static
            image of the figure is shown. Hence this is a persistent version of the
            ``"inline"`` mode, allowing users to see a static figure in other
            environments, browsers, etc.

            !!! note

                This mode requires the ``kaleido`` and ``flask_cors`` package.
                Install them : ``pip install plotly_resampler[inline_persistent]``
                or ``pip install kaleido flask_cors``.

          * ``"jupyterlab"``: The app will be displayed in a dedicated tab in the
            JupyterLab interface. Requires JupyterLab and the ``jupyterlab-dash``
            extension.
        By default None, which will result in the same behavior as ``"external"``.
    config: dict, optional
        The configuration options for displaying this figure, by default None.
        This ``config`` parameter is the same as the dict that you would pass as
        ``config`` argument to the `show` method.
        See more [https://plotly.com/python/configuration-options/](https://plotly.com/python/configuration-options/)
    graph_properties: dict, optional
        Dictionary of (keyword, value) for the properties that should be passed to
        the dcc.Graph, by default None.
        e.g.: `{"style": {"width": "50%"}}`
        Note: "config" is not allowed as key in this dict, as there is a distinct
        ``config`` parameter for this property in this method.
        See more [https://dash.plotly.com/dash-core-components/graph](https://dash.plotly.com/dash-core-components/graph)
    **kwargs: dict
        Additional app.run_server() kwargs. e.g.: port, ...
        Also note that these kwargs take precedence over the ones passed to the
        constructor via the ``show_dash_kwargs`` argument.

    """
    available_modes = ["external", "inline", "inline_persistent", "jupyterlab"]
    assert (
        mode is None or mode in available_modes
    ), f"mode must be one of {available_modes}"
    graph_properties = {} if graph_properties is None else graph_properties
    assert "config" not in graph_properties  # There is a param for config

    # 0. Check if the traces need to be updated when there is a xrange set
    # This will be the case when the users has set a xrange (via the `update_layout`
    # or `update_xaxes` methods`)
    relayout_dict = {}
    for xaxis_str in self._xaxis_list:
        x_range = self.layout[xaxis_str].range
        if x_range:  # when not None
            relayout_dict[f"{xaxis_str}.range[0]"] = x_range[0]
            relayout_dict[f"{xaxis_str}.range[1]"] = x_range[1]
    if relayout_dict:  # when not empty
        update_data = self.construct_update_data(relayout_dict)

        if not self._is_no_update(update_data):  # when there is an update
            with self.batch_update():
                # First update the layout (first item of update_data)
                self.layout.update(self._parse_relayout(update_data[0]))

                # Then update the data
                for updated_trace in update_data[1:]:
                    trace_idx = updated_trace.pop("index")
                    self.data[trace_idx].update(updated_trace)

    # 1. Construct the Dash app layout
    if mode == "inline_persistent":
        mode = "inline"
        if _jupyter_dash_installed:
            # Inline persistent mode: we display a static image of the figure when the
            # app is not reachable
            # Note: this is the "inline" behavior of JupyterDashInlinePersistentOutput
            app = JupyterDashPersistentInlineOutput("local_app")
            self._is_persistent_inline = True
        else:
            # If Jupyter Dash is not installed, inline persistent won't work and hence
            # we default to normal inline mode with a normal Dash app
            app = dash.Dash("local_app")
            warnings.warn(
                "'jupyter_dash' is not installed. The persistent inline mode will not work. Defaulting to standard inline mode."
            )
    else:
        # jupyter dash uses a normal Dash app as figure
        app = dash.Dash("local_app")
    app.layout = dash.html.Div(
        [
            dash.dcc.Graph(
                id="resample-figure", figure=self, config=config, **graph_properties
            ),
            TraceUpdater(
                id="trace-updater", gdID="resample-figure", sequentialUpdate=False
            ),
        ]
    )
    self.register_update_graph_callback(app, "resample-figure", "trace-updater")

    height_param = "height" if self._is_persistent_inline else "jupyter_height"

    # 2. Run the app
    if mode == "inline" and height_param not in kwargs:
        # If app height is not specified -> re-use figure height for inline dash app
        #  Note: default layout height is 450 (whereas default app height is 650)
        #  See: https://plotly.com/python/reference/layout/#layout-height
        fig_height = self.layout.height if self.layout.height is not None else 450
        kwargs[height_param] = fig_height + 18

    # kwargs take precedence over the show_dash_kwargs
    kwargs = {**self._show_dash_kwargs, **kwargs}

    # Store the app information, so it can be killed
    self._app = app
    self._host = kwargs.get("host", "127.0.0.1")
    self._port = kwargs.get("port", "8050")

    # function signature is slightly different for the Dash and JupyterDash implementations
    if self._is_persistent_inline:
        app.run(mode=mode, **kwargs)
    else:
        app.run(jupyter_mode=mode, **kwargs)

stop_server(warn=True)

Stop the running dash-app.

Parameters:

Name Type Description Default
warn bool

Whether a warning message will be shown or not, by default True.

True

Warning

This only works if the dash-app was started with show_dash.

Source code in plotly_resampler/figure_resampler/figure_resampler.py
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
def stop_server(self, warn: bool = True):
    """Stop the running dash-app.

    Parameters
    ----------
    warn: bool
        Whether a warning message will be shown or  not, by default True.

    !!! warning

        This only works if the dash-app was started with [`show_dash`][figure_resampler.figure_resampler.FigureResampler.show_dash].
    """
    if self._app is not None:
        servers_dict = (
            self._app._server_threads
            if self._is_persistent_inline
            else dash.jupyter_dash._servers
        )
        old_server = servers_dict.get((self._host, self._port))
        if old_server:
            if self._is_persistent_inline:
                old_server.kill()
                old_server.join()
            else:
                old_server.shutdown()
        del servers_dict[(self._host, self._port)]
    elif warn:
        warnings.warn(
            "Could not stop the server, either the \n"
            + "\t- 'show-dash' method was not called, or \n"
            + "\t- the dash-server wasn't started with 'show_dash'"
        )

FigureWidgetResampler

Bases: AbstractFigureAggregator, go.FigureWidget

Data aggregation functionality wrapper for go.FigureWidgets.

Warning

  • This wrapper only works within jupyter-based environments.
  • The .show() method returns a static figure on which the dynamic resampling cannot be performed. To allow dynamic resampling, you should just output the FigureWidgetResampler object in a cell.
Source code in plotly_resampler/figure_resampler/figurewidget_resampler.py
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
class FigureWidgetResampler(
    AbstractFigureAggregator, go.FigureWidget, metaclass=_FigureWidgetResamplerM
):
    """Data aggregation functionality wrapper for ``go.FigureWidgets``.

    !!! warning

        * This wrapper only works within ``jupyter``-based environments.
        * The ``.show()`` method returns a **static figure** on which the
          **dynamic resampling cannot be performed**. To allow dynamic resampling,
          you should just output the ``FigureWidgetResampler`` object in a cell.

    """

    def __init__(
        self,
        figure: BaseFigure | dict = None,
        convert_existing_traces: bool = True,
        default_n_shown_samples: int = 1000,
        default_downsampler: AbstractAggregator = MinMaxLTTB(),
        default_gap_handler: AbstractGapHandler = MedDiffGapHandler(),
        resampled_trace_prefix_suffix: Tuple[str, str] = (
            '<b style="color:sandybrown">[R]</b> ',
            "",
        ),
        show_mean_aggregation_size: bool = True,
        convert_traces_kwargs: dict | None = None,
        verbose: bool = False,
    ):
        # Parse the figure input before calling `super`
        f = self._get_figure_class(go.FigureWidget)()
        f._data_validator.set_uid = False

        if isinstance(figure, BaseFigure):
            # A base figure object, can be;
            # - a base plotly figure: go.Figure or go.FigureWidget
            # - a plotly-resampler figure: subclass of AbstractFigureAggregator
            # => we first copy the layout, grid_str and grid ref
            f.layout = figure.layout
            f._grid_str = figure._grid_str
            f._grid_ref = figure._grid_ref
            f.add_traces(figure.data)
        elif isinstance(figure, dict) and (
            "data" in figure or "layout" in figure  # or "frames" in figure  # TODO
        ):
            # A figure as a dict, can be;
            # - a plotly figure as a dict (after calling `fig.to_dict()`)
            # - a pickled (plotly-resampler) figure (after loading a pickled figure)
            f.layout = figure.get("layout")
            f._grid_str = figure.get("_grid_str")
            f._grid_ref = figure.get("_grid_ref")
            f.add_traces(figure.get("data"))
            # `pr_props` is not None when loading a pickled plotly-resampler figure
            f._pr_props = figure.get("pr_props")
            # `f._pr_props`` is an attribute to store properties of a plotly-resampler
            # figure. This attribute is only used to pass information to the super()
            # constructor. Once the super constructor is called, the attribute is
            # removed.

            # f.add_frames(figure.get("frames")) TODO
        elif isinstance(figure, (dict, list)):
            # A single trace dict or a list of traces
            f.add_traces(figure)

        super().__init__(
            f,
            convert_existing_traces,
            default_n_shown_samples,
            default_downsampler,
            default_gap_handler,
            resampled_trace_prefix_suffix,
            show_mean_aggregation_size,
            convert_traces_kwargs,
            verbose,
        )

        if isinstance(figure, AbstractFigureAggregator):
            # Copy the `_hf_data` if the previous figure was an AbstractFigureAggregator
            # And adjust the default max_n_samples and
            self._hf_data.update(
                self._copy_hf_data(figure._hf_data, adjust_default_values=True)
            )

            # Note: This hack ensures that the this figure object initially uses
            # data of the whole view. More concretely; we create a dict
            # serialization figure and adjust the hf-traces to the whole view
            # with the check-update method (by passing no range / filter args)
            with self.batch_update():
                graph_dict: dict = self._get_current_graph()
                update_indices = self._check_update_figure_dict(graph_dict)
                for idx in update_indices:
                    self.data[idx].update(graph_dict["data"][idx])

        self._prev_layout = None  # Contains the previous xaxis layout configuration

        # used for logging purposes to save a history of layout changes
        self._relayout_hist = []

        # Assign the the update-methods to the corresponding classes
        showspike_keys = [f"{xaxis}.showspikes" for xaxis in self._xaxis_list]
        self.layout.on_change(self._update_spike_ranges, *showspike_keys)

        x_relayout_keys = [f"{xaxis}.range" for xaxis in self._xaxis_list]
        self.layout.on_change(self._update_x_ranges, *x_relayout_keys)

    def _update_x_ranges(self, layout, *x_ranges, force_update: bool = False):
        """Update the the go.Figure data based on changed x-ranges.

        Parameters
        ----------
        layout : go.Layout
            The figure's (i.e, self) layout object. Remark that this is a reference,
            so if we change self.layout (same object reference), this object will
            change.
        *x_ranges: iterable
            A iterable list of current x-ranges, where each x-range is a tuple of two
            items, indicating the current/new (if changed) left-right x-range,
            respectively.
        fore_update: bool
            Whether an update of all traces will be forced, by default False.
        """
        relayout_dict = {}  # variable in which we aim to reconstruct the relayout
        # serialize the layout in a new dict object
        layout = {
            xaxis_str: layout[xaxis_str].to_plotly_json()
            for xaxis_str in self._xaxis_list
        }
        if self._prev_layout is None:
            self._prev_layout = layout

        for xaxis_str, x_range in zip(self._xaxis_list, x_ranges):
            # We also check whether "range" is within the xaxis its layout otherwise
            # It is most-likely an autorange check
            if (
                "range" in layout[xaxis_str]
                and self._prev_layout[xaxis_str].get("range", []) != x_range
                or (force_update and x_range is not None)
            ):
                # a change took place -> add to the relayout dict
                relayout_dict[f"{xaxis_str}.range[0]"] = x_range[0]
                relayout_dict[f"{xaxis_str}.range[1]"] = x_range[1]

                # An update will take place for that trace
                # -> save current xaxis range to _prev_layout
                self._prev_layout[xaxis_str]["range"] = x_range

        if relayout_dict:  # when not empty
            # Construct the update data
            update_data = self.construct_update_data(relayout_dict)

            if self._is_no_update(update_data):
                # Return when no data update
                return

            if self._print_verbose:
                self._relayout_hist.append(dict(zip(self._xaxis_list, x_ranges)))
                self._relayout_hist.append(layout)
                self._relayout_hist.append(["xaxis-range-update", len(update_data) - 1])
                self._relayout_hist.append("-" * 30)

            with self.batch_update():
                # First update the layout (first item of update_data)
                self.layout.update(self._parse_relayout(update_data[0]))

                for xaxis_str in self._xaxis_list:
                    if "showspikes" in layout[xaxis_str]:
                        self.layout[xaxis_str].pop("showspikes")

                # Then update the data
                for updated_trace in update_data[1:]:
                    trace_idx = updated_trace.pop("index")
                    self.data[trace_idx].update(updated_trace)

    def _update_spike_ranges(self, layout, *showspikes, force_update=False):
        """Update the go.Figure based on the changed spike-ranges.

        Parameters
        ----------
        layout : go.Layout
            The figure's (i.e, self) layout object. Remark that this is a reference,
            so if we change self.layout (same object reference), this object will
            change.
        *showspikes: iterable
            A iterable where each item is a bool, indicating  whether showspikes is set
            to true/false for the corresponding xaxis in ``self._xaxis_list``.
        force_update: bool
            Bool indicating whether the range updates need to take place. This is
            especially useful when you have recently updated the figure its data (with
            the hf_data property) and want to perform an autoscale, independent from
            the current figure-layout.
        """
        relayout_dict = {}  # variable in which we aim to reconstruct the relayout
        # serialize the layout in a new dict object
        layout = {
            xaxis_str: layout[xaxis_str].to_plotly_json()
            for xaxis_str in self._xaxis_list
        }

        if self._prev_layout is None:
            self._prev_layout = layout

        for xaxis_str, showspike in zip(self._xaxis_list, showspikes):
            if (
                force_update
                or
                # autorange key must be set to True
                (
                    layout[xaxis_str].get("autorange", False)
                    # we only perform updates for traces which have 'range' property,
                    # as we do need to reconstruct the update-data for these traces
                    and self._prev_layout[xaxis_str].get("range", None) is not None
                )
            ):
                relayout_dict[f"{xaxis_str}.autorange"] = True
                relayout_dict[f"{xaxis_str}.showspikes"] = showspike
                # autorange -> we pop the xaxis range
                if "range" in layout[xaxis_str]:
                    del layout[xaxis_str]["range"]

        if len(relayout_dict):
            # An update will take place, save current layout to _prev_layout
            self._prev_layout = layout

            # Construct the update data
            update_data = self.construct_update_data(relayout_dict)
            if self._print_verbose:
                self._relayout_hist.append(layout)
                self._relayout_hist.append(["showspikes-update", len(update_data) - 1])
                self._relayout_hist.append("-" * 30)

            with self.batch_update():
                # First update the layout (first item of update_data)
                if not force_update:
                    self.layout.update(self._parse_relayout(update_data[0]))

                # Also:  Remove the showspikes from the layout, otherwise the autorange
                # will not work as intended (it will not be triggered again)
                # Note: this removal causes a second trigger of this method
                # which will go in the "else" part below.
                for xaxis_str in self._xaxis_list:
                    self.layout[xaxis_str].pop("showspikes")

                # Then, update the data
                for updated_trace in update_data[1:]:
                    trace_idx = updated_trace.pop("index")
                    self.data[trace_idx].update(updated_trace)
        elif self._print_verbose:
            self._relayout_hist.append(["showspikes", "initial call or showspikes"])
            self._relayout_hist.append("-" * 40)

    def reset_axes(self):
        """Reset the axes of the FigureWidgetResampler.

        This is useful when adjusting the `hf_data` properties of the
        ``FigureWidgetResampler``.
        """
        self._update_spike_ranges(
            self.layout, *[False] * len(self._xaxis_list), force_update=True
        )
        # Reset the layout
        self.update_layout(
            {
                axis: {"autorange": None, "range": None}
                for axis in self._xaxis_list + self._yaxis_list
            }
        )

    def reload_data(self):
        """Reload all the data of FigureWidgetResampler for the current range-view.

        This is useful when adjusting the `hf_data` properties of the
        ``FigureWidgetResampler``.
        """
        if all(
            self.layout[xaxis].autorange
            or (
                self.layout[xaxis].autorange is None
                and self.layout[xaxis].range is None
            )
            for xaxis in self._xaxis_list
        ):
            self._update_spike_ranges(
                self.layout, *[False] * len(self._xaxis_list), force_update=True
            )
        else:
            # Resample the data for the current range-view
            self._update_x_ranges(
                self.layout,
                # Pass the current view to trigger a resample operation
                *[self.layout[xaxis_str]["range"] for xaxis_str in self._xaxis_list],
                force_update=True,
            )

reload_data()

Reload all the data of FigureWidgetResampler for the current range-view.

This is useful when adjusting the hf_data properties of the FigureWidgetResampler.

Source code in plotly_resampler/figure_resampler/figurewidget_resampler.py
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
def reload_data(self):
    """Reload all the data of FigureWidgetResampler for the current range-view.

    This is useful when adjusting the `hf_data` properties of the
    ``FigureWidgetResampler``.
    """
    if all(
        self.layout[xaxis].autorange
        or (
            self.layout[xaxis].autorange is None
            and self.layout[xaxis].range is None
        )
        for xaxis in self._xaxis_list
    ):
        self._update_spike_ranges(
            self.layout, *[False] * len(self._xaxis_list), force_update=True
        )
    else:
        # Resample the data for the current range-view
        self._update_x_ranges(
            self.layout,
            # Pass the current view to trigger a resample operation
            *[self.layout[xaxis_str]["range"] for xaxis_str in self._xaxis_list],
            force_update=True,
        )

reset_axes()

Reset the axes of the FigureWidgetResampler.

This is useful when adjusting the hf_data properties of the FigureWidgetResampler.

Source code in plotly_resampler/figure_resampler/figurewidget_resampler.py
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
def reset_axes(self):
    """Reset the axes of the FigureWidgetResampler.

    This is useful when adjusting the `hf_data` properties of the
    ``FigureWidgetResampler``.
    """
    self._update_spike_ranges(
        self.layout, *[False] * len(self._xaxis_list), force_update=True
    )
    # Reset the layout
    self.update_layout(
        {
            axis: {"autorange": None, "range": None}
            for axis in self._xaxis_list + self._yaxis_list
        }
    )