predict-idlab / plotly-resampler

Visualize large time series data with plotly.py
https://predict-idlab.github.io/plotly-resampler/latest
MIT License
1.03k stars 68 forks source link

FigureWidget() update #45

Closed zxweed closed 2 years ago

zxweed commented 2 years ago

Hi, very useful project, all my career I dream about such thing. It seems that it can make plotly usable in real life, not only in the iris dataset.

Is there a way to dynamic update the resampled FigureWidget instance? For example, in the Jupyter lab:

image

The last cell causes an update of the data in the chart if fig is an FigureWidget instance, but does not update if the instance is a FigureResampler(go.FigureWidget())

Test case:

import numpy as np
from plotly_resampler import FigureResampler

x = np.arange(1_000_000)
noisy_sin = (3 + np.sin(x / 15000) + np.random.randn(len(x)) / 10) * x / 1_000

fig = FigureResampler(go.FigureWidget())
fig.add_scattergl(name='noisy sine', showlegend=True, x=x, y=noisy_sin)

fig.update_layout(autosize=True, height=300, template=None, legend=dict(x=0.1, y=1, orientation="h"),
                  margin=dict(l=45, r=15, b=20, t=30, pad=3))
fig.show()

# does not update chart if fig is FigureResampler instance
with fig.batch_update():
    fig.data[0].y = -fig.data[0].y

PS: It seems that resampling only works in dash, but not in jupyterlab?

jvdd commented 2 years ago

Hi @zxweed

Thank your for the nice words!

I see you are asking two things here; a) How to have dynamic resampling with plotly-resampler b) How to update the graph data

a) How to have dynamic resampling with plotly-resampler

To use plotly-resampler, one should perform 2 steps; https://predict-idlab.github.io/plotly-resampler/getting_started.html#how-to-use

  1. wrap the plotly Figure with FigureResampler
  2. call .show_dash() on the Figure

You did step 1 correctly, but called .show instead of .show_dash. If you do not perform step 2, no dynamic resampling will happen (as no underlying dahs app will get started)

PS: plotly-resampler works in jupyterlab as well. You should then call .show_dash(mode="inline") :blush:

import numpy as np; import plotly.graph_objects as go
from plotly_resampler import FigureResampler

x = np.arange(1_000_000)
noisy_sin = (3 + np.sin(x / 15000) + np.random.randn(len(x)) / 10) * x / 1_000

# Step 1
fig = FigureResampler(go.FigureWidget())
fig.add_scattergl(name='noisy sine', showlegend=True, x=x, y=noisy_sin)

fig.update_layout(autosize=True, height=300, template=None, legend=dict(x=0.1, y=1, orientation="h"),
                  margin=dict(l=45, r=15, b=20, t=30, pad=3))
# Step 2
fig.show_dash(mode='inline')  # visualizes the chart inline in your notebook

b) How to update the graph data

As for now, the large high-frequency sequence data is stored in a private variable hf_data. Just had a discussion with @jonasvdd about this, and we could support such an update by exposing the hf_data via a property. A future PR will tackle this issue.

Hope this answer already helps you!

Cheers, Jeroen

jvdd commented 2 years ago

Question b) is tackled in PR #46

zxweed commented 2 years ago

Is it really necessary to use dash server in Jupyter-only environment? For example, I can't open additional port 8050 on the server, only 8888 is available. After all, the FigureWidget has its own event handlers, on which you can hook your fast resamplers:

image

internal.resample.ipynb.zip

How hard is it to implement?

jvdd commented 2 years ago

Oh, I think I get what you mean! That is a very interesting suggestion :smiley:

I'll look into it and will keep you posted in this thread, @jonasvdd also sees the potential of supporting FigureWidget as such no port forwarding is required for the end-users (in Jupyter)

Thanks for your input!

jonasvdd commented 2 years ago

@zxweed, very good issue!!

I think it is feasible to implement and has a high-relevance!

However, I will need to break my head about how to implement this without losing backward compatibility & still having clean code. Will further look into it next week!

(fyi: at the end of this week, we will release a new version #46 which makes our aggregators faster and memory efficient 2x speedup) 🔥

jonasvdd commented 2 years ago

see #47 🙏🏼

jonasvdd commented 2 years ago

@zxweed PR is merged, you can try it out with v=0.6.0! Always glad to hear feedback :)

zxweed commented 2 years ago

Thanks, I checked in jupyter lab and notebook - works as it should, you make plotly great again :)

The parameters hf_x and hf_y are not yet available through fig.data? They cannot be changed dynamically?

jonasvdd commented 2 years ago

Hi @zxweed, to adjust these data properties use the hf_data property! see: https://predict-idlab.github.io/plotly-resampler/getting_started.html#dynamically-adjusting-the-scatter-data

zxweed commented 2 years ago

Yes, thank you, but my chart doesn't update after I change the data - I need to explicitly press some button to do that:

test plotly.resampler.test.zip

zxweed commented 2 years ago

@jonasvdd, should I call some method to update the graph explicitly after changing the hf_data property?

jonasvdd commented 2 years ago

Hi @zxweed, we have a solution for your issue! will create a new release for this!

jonasvdd commented 2 years ago

You will indeed have to call an additional method, i.e. reset_axes on your FigureWidgetResampler

jonasvdd commented 2 years ago

@zxweed version 0.6.1 has the desired functionality i.e., FigureWidgetResampler.reset_axes and/or FigureWidgetResampler.reload_data

zxweed commented 2 years ago

Thank you, it works as it should in 0.6.1 - is this a special build for me? In the current version 0.6.2 these methods are removed...

thanosam commented 2 years ago

Hello,

Congratulations for such a useful library. Makes plotly so powerful. I have one question. I have an implementation in Django and using plotly (in my views), I send the plot to the frond-end(templates). I am currently using your library and I manage to plot more than 1mil datapoints in less than 1sec. (more than 15sec before !). But the dynamic aggregation doesn't work. Any ideas of how can I implement this feature in Django?

Thank you so much in advance.

jvdd commented 2 years ago

Hi @thanosam

To help you with your issue, I will need some more details;

Perhaps you could send some minimal code example so I can reproduce your issue on my machine?

What I can think of right now;

Let me know if one of the above possible issues already helped you! :)

Cheers, Jeroen

jvdd commented 2 years ago

Hi @zxweed

@jonasvdd created the v0.6.2 release, I suspect that he created this release from the #52 branch (which is most likely not branched from the latest version of the main branch).

We will look into it, to avoid such behavior in the next release!

Jeroen

thanosam commented 2 years ago

Hi @jvdd,

Thank you for your fast answer. I am working in pycharm and I use FigureWidgetResampler since I wanna avoid dash involvement.

In my current implementation I produce plots dynamically from a list. Everytime I click on an option in the list, the frond-end makes an ajax call to the back-end. Then, until today with plotly I was giving the plot back as an ajax response, adding it in a div. Now, I use your method that way. I receive the resampled plot. But when I zoom it doesn't resamples.

Example:

Frond-end -> ajax call to the back-end. In a view:

... fig = FigureWidgetResampler(go.Figure()) fig.add_trace(go.Scattergl(name='noisy sine', showlegend=True), hf_x=x, hf_y=noisy_sin) plot_div = plot(fig, output_type='div') ...

Backe-end -> sends the plot_div as a response to the ajax call and I add it in a div.

jonasvdd commented 2 years ago

@zxweed, again, good catch, thanks for telling us! Just deployed 0.6.3 should be fixed now! 😃

zxweed commented 2 years ago

Yes, it works now in the 0.6.3 version, thank you for fast fix.

jvdd commented 2 years ago

Hi @thanosam

I believe I discovered why no resampling occurs.

I think you use the plotly.offline.plot() function, which creates a standalone HTML. Therefore, (I think) there is no connection with the IPython environment that is required for your FigureWidgetResampler or your back-end dash application when using FigureResampler.

If possible, I would advise to check whether you can ingest the figure without using plotly.offline.plot so that the figure is not a standalone html but still connected to either your IPython environment (when using FigureWidgetResampler) or a dash backend (when using FigureResampler).

Hope this helps!

thanosam commented 2 years ago

Hi jvdd,

Thank you for your response. Indeed I need to use the offline version. I need to have it as an html in my response in order to change it dynamically. With this implementation there isn't something I could do?

Thanks

jvdd commented 2 years ago

Hi @thanosam,

I do not think there is a way to add callbacks to plotly.offline plots.

If you want adaptive resampling in your application I would suggest to use dash (as such you can have callbacks, and define your figure using plotly dash-html-componets).

More info on how you can integrate plotly-resampler in a dash application;

Cheers

thanosam commented 2 years ago

Hi @jvdd ,

I am developing my app in Django and I use django-plotly-dash library for the plots. I was wondering is the plotly-resampler compatible with the django-plotly-dash library? If yes, how?

jvdd commented 2 years ago

Hi @thanosam,

I've created a separate issue for this (see #82) - we can further discuss it over there