Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ trace adjustment example #159

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

✨ trace adjustment example #159

wants to merge 1 commit into from

Conversation

jonasvdd
Copy link
Member

Related: #18

@jonasvdd jonasvdd mentioned this pull request Jan 12, 2023
@juan11iguel
Copy link

Hey! Thank you so much Jonas for putting the work. I have studied the code extensively 🧐 to try and apply it in my use case. I wanted to extend the example to be able to dynamically generate plots/subplots that update its data based on user interaction, that is, using pattern-matching callbacks.

I attach a minimal (non) working example built taking clues from 03_minimal_cache_dynamic and your PR. So far I have managed to have a plot generated dynamically that after some user input updates its data. The problem is that contrary to yours, the callback responsible for refreshing the graph in the front end is not triggering the trace_updater, though if I moved or zoomed the plot the data did get refreshed. I then modified the callback to include as an output the relayout, this makes the trace_updater callback get triggered (not like in your code where is not necessary) , and even though the inputs given to the callback seem correct, the browser throws a weird r[m] is undefined.

A couple of observations:

  • If there are two traces in the same subplot, even though only one might need to change it is necessary to update both otherwise construct_update_data throws an error.
  • ServersideOutput needs to be the last output
  • If one of the inputs or outputs uses a string for one of the elements of its id while others use MATCH that input will be broken (just a random string) instead of the actual component property. For example: {'type':'graph', 'id':'step1', "index": MATCH} and {'type':'input', 'id':'MATCH', "index": MATCH} cannot be used as an input/output pair. It should either be all MATCH or all a manually specified key.

I am not sure if this is the right way of sharing the code but pasting it here felt like would be too much:
app_minimal.zip

@jonasvdd
Copy link
Member Author

Hi!

I see that you are a researcher (as well! 😄), on what time series data are you working, if I may ask?

I glanced at your minimal_app.py and got it working without the r[m] bug. I propose you take a look at the changes I made via a difftool. (ps. I formatted the code with black (line-width=88) b4 altering the code).

main diff: removing the relayoutData output from the process_and_update_figure method.

kind regards,
Jonas

app_minimal.zip

@juan11iguel
Copy link

Yeah a PhD student trying to do too many things at the same time, I am quite sure my supervisors would not like to know I am spending my time doing this (so please don't tell them 😅)

I mainly work with signals from a thermal desalination process (temperatures, pressures, levels, flows, etc). I am trying to implement a variation of a steady state identification algorithm from the literature but I found the parameter calibration process to be quite un-intuitive. So I am trying to develop an online interactive tool to see in real time the effect of the parameter values and export the results. Later I want to implement the algorithm to work online in the real plant using directly the file generated from the tool.

I plan on publishing both things open-source, once I do I will let you know (and of course reference the project in any publication it might yield.

I am not sure if I am doing something wrong out of excitement but, is the modified code in the zip you attached? It states that it was modified yesterday and when I diff it, it shows no differences 😥

@jonasvdd
Copy link
Member Author

Whoops, my bad, this is the zip i intended to send you! 🙈

minimal_app_review.zip

@jonasvdd
Copy link
Member Author

jonasvdd commented Jan 18, 2023

I also remarked that in my updated version the app_minimal.py kinda works (after either resizing the dash app browser window, or performing a relayout on the graph (e.g., zooming/panning)).

However, when you update the parameters rather quickly, for some reason the traces become invisible, see 📷 ⬇️ (remark how you can see a hovertext, but no traces). I honestly have no idea what part of the code causes this (maybe this is even not related to plotly-resampler; and is a plotly.js bug? 🤷🏼).

image

Regarding your research, really cool stuff!

  • What data-sizes (nbr. of samples) and how many signals do you usually deal with?
  • Which kind of visualizations do you tend to mostly use for your time-series data wrangling next to time-plots?
    BTW: When you are in the process of releasing your tool its code, I certainly want to take a glance at it, so keep me posted! 😄

Fyi: My work, next to developing code, encompasses toying with large wearable datasets; e.g., data from a wrist worn wearable from over 90 days (monitored on patients who are diagnosed with chronic headache disorders). But due to ethical constraints I cannot make any of that data open access. 😒

Kind regards,
Jonas

@juan11iguel
Copy link

juan11iguel commented Jan 19, 2023

How do you like ugly hacks? I got it working (except for the part of disappearing traces when updating too fast. I modified the relayout dict to make sure we trigger the trace updater in the following way:

        # 2. Alter the relayout dict to make sure that `construct_update_data` will
        #    trigger (and thus not return `dash.no_update`)
        for ax in fig.layout.to_plotly_json().keys():
            if not ax.startswith("xaxis"):
                continue

            # Current axis relayout
            if f"{ax}.range[0]" in relayout:
                print('current relayout')
                relayout.update({f"{ax}.range[0]":relayout[f"{ax}.range[0]"]-1, 
                                 f"{ax}.range[1]":relayout[f"{ax}.range[1]"]+1})
            
            # Past axis relayout
            elif f"{ax}.range" in relayout:
                print('sustained relayout')
                relayout.update({f"{ax}.range[0]":relayout[f"{ax}.range"][0]-1, 
                                 f"{ax}.range[1]": relayout[f"{ax}.range"][1]+1})
                relayout.pop(f"{ax}.range")

            # No axis relayouts
            else:
                print('no relayouts')
                axes = [trace['xaxis'] for trace in fig.data]
                if ax[-1].isnumeric():
                    trace_idx=axes.index(f'x{ax[-1]}')
                else:
                    trace_idx=axes.index('x')
                    relayout = {}
                
                relayout.update({f"{ax}.range[0]":fig.hf_data[ trace_idx ]["x"][0]-1,
                                 f"{ax}.range[1]":fig.hf_data[ trace_idx ]["x"][-1]+1})

Maybe the problem is the relayout is being performed before the server-sided cache figure had time to update its data? I added a half a second delay in the trace-updater callback and that seems to go away with most of the disappearing traces. When I have time I will create a post in the Plotly forum, usually they are very helpful and maybe can point us to a better way of handling this ..

Let me get back to you tomorrow to answer your questions (it's late here and still did not have dinner 😥 (or took a shower if we are being completely honest 🙉)).

app_minimal_review_of_the_review.zip

EDIT: Maybe the ±1 offsets are not really necessary, need to also check that

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants