0

I have some issue and I hope you can help me to resolve it. I need to create interactive plot with dropdown widget where I could select and plot the interested data. I doing it by following way:

import plotly.graph_objects as go
import ipywidgets as widgets

import pandas as pd

df = pd.DataFrame(
    {'ticker' : ['a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c'], 'timestamp' : [1,1,1,2,2,2,3,3,3], 'val' : [10,11,12,21,22,23, 100, 200, 300]})

ddwn = widgets.Dropdown(options = df.ticker.unique())
display(ddwn)

def on_change(change):
    if change['type'] == 'change' and change['name'] == 'value':
            d = df[df['ticker'] == change['new']].sort_values('timestamp')
            fig = go.Figure(data=go.Scatter(x=list(d.timestamp), y=list(d.val), name=change['new']))
            fig.show()

ddwn.observe(on_change)

The problem is that a new figure is added below the previous one, instead of the current figure cleared. But really I want to update figure. I tried to use answer from Clear MatPlotLib figure in Jupyter Python notebook but it didn't help me.

P.S. I have a lot of tickers - therefore I don't want to create dict on every ticker and use it.

Roman Kazmin
  • 931
  • 6
  • 18
  • You are using plotly. Plotly and Matplotlib don't overlap in anyway. Plotly has it's own related solutions for this. I say this because I'm not getting the reference to matplotlib as a topic tag? You do reference trying another answer but maybe we are meant to take that as meaning you don't care about what package is forming the basis for the implementation? – Wayne Feb 06 '23 at 21:13
  • Yes I don't care about what package use - how can I fix it using Plotly? – Roman Kazmin Feb 06 '23 at 21:24

2 Answers2

0

Option 1a:

This option is mainly Plotly-based with ipywidgets to interact:

# based on https://stackoverflow.com/a/73918907/8508004 and https://stackoverflow.com/q/75365695/8508004
import plotly.graph_objects as go
import pandas as pd
from ipywidgets import widgets
from ipywidgets import interact

df = pd.DataFrame(
    {'ticker' : ['a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c'], 'timestamp' : [1,1,1,2,2,2,3,3,3], 'val' : [10,11,12,21,22,23, 100, 200, 300]})

tickers = df.ticker.tolist()

@interact
def read_values(
    selected_ticker=widgets.Dropdown(
        description="Select :", value="a", options=tickers[:3]
    )
):

    df_sub = df[df.ticker == selected_ticker]
    fig = go.Figure(data=go.Scatter(x = df_sub.timestamp, y = df_sub.val))
    go.FigureWidget(fig.to_dict()).show()

(Note that I find usually with Plotly Graph Objects, I have to choose something from the drop-down and then it works to display the plot. Just shows blank space until I pick that first letter. With JupyterLab it works right away. No blank space; it starts off showing the a plot immediately.)


Option 1b:

Pure Plotly-based option:

# based primarily on https://stackoverflow.com/q/69510612/8508004, 
# combining with https://stackoverflow.com/q/75365695/8508004
# Influenced also by 
# based on https://plotly.com/python/dropdowns/#relayout-dropdown and https://stackoverflow.com/q/71296687/8508004 and
# and https://stackoverflow.com/q/69510612/8508004 and
# https://community.plotly.com/t/how-to-delete-a-particular-trace-from-multiple-traces-in-a-graph-objects-figure/70203/2
import pandas as pd
import plotly.graph_objects as go
df = pd.DataFrame(
    {'ticker' : ['a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c'], 'timestamp' : [1,1,1,2,2,2,3,3,3], 'val' : [10,11,12,21,22,23, 100, 200, 300]})

tickers = df.ticker.tolist()

# Create figure and add one scatter trace
fig = go.Figure()
dfa = df[df.ticker == 'a']

fig.add_trace(go.Scatter(
    x=dfa.timestamp, 
    y=dfa.val,
    visible=True,
    #mode='markers',
    marker=dict(size=12, line=dict(width=1, color='rgba(0.4, 0.4, 0.4, 0.8)')), # based on https://plotly.com/python/marker-style/
    )
              )

# Create selection buttons
select_buttons = []

for selection in tickers[:3]:
    select_buttons.append(dict(method='update',
                        label=selection,
                        args=[{'x': [df[df.ticker == selection].timestamp], 'y': [df[df.ticker == selection].val]},
                              ]
                        )
                )
    
# Pass buttons to the updatemenus argument
fig.update_layout(updatemenus=[dict(buttons=select_buttons,x=0.18, y=1.21),])
# Update remaining layout properties
fig.update_layout(
    title_text="Select Ticker:",
    showlegend=False,
)
fig.show()

Option 2:

Matplotlib-based option:

#based on https://stackoverflow.com/a/74551129/8508004 and references therein, combined with https://stackoverflow.com/q/75365695/8508004
import matplotlib.pyplot as plt
import ipywidgets as widgets
import pandas as pd

df = pd.DataFrame(
    {'ticker' : ['a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c'], 'timestamp' : [1,1,1,2,2,2,3,3,3], 'val' : [10,11,12,21,22,23, 100, 200, 300]})

tickers = df.ticker.tolist()

def display_selected_plot(x):
    df_sub = df[df.ticker == x]
    fig=plt.figure()
    plt.plot(df_sub.timestamp,  df_sub.val, '-o')
    plt.grid(True) #optional grid
    plt.show()
    
selected_ticker=widgets.Dropdown(
        description="Select :", value="a", options=tickers[:3]
    )

gui = widgets.interactive(display_selected_plot, x=selected_ticker) #Create our interactive graphic with the dropdown as the argument
gui

In my attempts, your referenced example code for Matplotlib doesn't work the way it says for me. I used ipywidget's interactive that I've seen works well lately without needing %matplotlib notebook which doesn't work in JupyterLab.



Try these out without installing anything on your system:

Go here and press 'launch binder'. A temporary, remote session will spin up served via MyBinder. For trying the Plotly one, select '3D scatter plots via Plotly' from the list available notebooks. When that opens, run all the examples using 'Run All'. Then at the bottom make a new cell & paste in the Plotly 1a example. (I don't get why the Plotly 1a example is being quirky in the classic Jupyter notebook interface and/or what isn't being imported without running that other code first in the classic Jupyter Notebook interface?) Or try Plotly 1b example.

The Matplotlib example will work if you paste it in the first notebook that comes up and run the code.

To switch to JupyterLab, click on the Jupyter icon above and on the left side. Then open a new notebook in the session that comes up. Paste in any of code blocks and run it.

As the session is temporary, if you make anything useful, be sure to download it to your local machine.

Trying on a remote temporary machine and opting-in to maintaining interactivity:

Either Plotly examples will be interactive where it works at what I suggest above to run. The options are in the upper left of the Plotly view pane.

The Matplotlib-based example has different steps to keep the 'interactive' features working depending if you are running it the classic Jupyter Notebook interface or JupyterLab.
For the classic notebook interface, at this time go to where I suggest above and simply add %matplotlib notebook at the top of the cell where you put the code I suggest. Then when you run it there, it will be interactive. You can click the blue button in the upper right to turn off the interactivity. For keeping the interactive features available JupyterLab, at this time it is easier to use a different place to launch a temporary session. Go to here. Sadly, the link currently goes to a dead end and doesn't seem to build a new image right now. Fortunately, right now this offering works for launching. (<== Dang. That has stopped launching correctly now, too. I need somewhere where ipympl gets installed in build.) When that session opens, run in the notebook %pip install pandas. Let that run and then restart the kernel and make a new cell. At the top of the new cell, add %matplotlib ipympl as the first line above the Matplotlib suggested example. It should be interactive. The interactive tool strip will come up if you hover over the side edge just to the left of the plot area. (If not, try changing it to %matplotlib widget.)

Wayne
  • 6,607
  • 8
  • 36
  • 93
  • It seems this solution doesn't work in my jupiter notebook – Roman Kazmin Feb 06 '23 at 21:25
  • Hmmm.. Plotly one is a bit quirky. Try this. Go to where I suggest and then when the session comes up, select '3D scatter plots via Plotly' from the list available notebooks. When that opens, run all the examples using 'Run All'. Then at the bottom make a new cell & paste in the Plotly example. It will work. I don't get what isn't being imported without running that other code first in the classic Jupyter Notebook interface. Maybe you'll see it. It works great in JupyterLab there without that weirdness of needing to run that other code first. (I've see graph objects be weird yesterday, too.) – Wayne Feb 06 '23 at 21:55
  • I added a way to demo each to the main text. Both work in JupyterLab just fine. Currently the classic notebook seems a little glitch for the Plotly one. I'm not sure why and why using it conjunction with other examples first allows it to work. The new version (version 7) of the document-centric notebook interface (what is called the classic Jupyter Notbeook interface at this time) will be made on JupyterLab components and so it's probably good that if it will only work well in one, it works in JupyterLab because probably will work better in the new version of the document-centric interface. – Wayne Feb 06 '23 at 22:18
  • The second solution works - but the interactivity of the plot is lost – Roman Kazmin Feb 06 '23 at 22:28
  • I think I know what you mean by interactivity. I'll put in the answer about where to run it, how to have interactivity with each. Let me know if that isn't what you meant. **Short answer**: Plotly will have interactivity where Plotly works. For the Matplotlib example in the classic notebook interface, you need to add `%matplotlb notebook` as the first line of the cell where you run the code block provided. For in JupyterLab at this time, you need to use `%matplotlib ipympl`. (ipympl has to be installed and working correctly for it to work.) – Wayne Feb 07 '23 at 04:13
  • For JupyterLab, try `%matplotlib widget` as the first line of the cell, if `%matplotlib ipympl` doesn't work. (It seems there's a debate about what is going to be the way to invoke it.) – Wayne Feb 07 '23 at 04:31
  • I added a pure Plotly option as well now. It may work a little better in the classic Jupyter notebook (or seems to right now.) – Wayne Feb 07 '23 at 21:10
0

This works for me (fixed the version prvided by @Wayne):

# based on https://stackoverflow.com/a/73918907/8508004 and https://stackoverflow.com/q/75365695/8508004
import plotly.graph_objects as go
import pandas as pd
from ipywidgets import widgets
from ipywidgets import interact

df = pd.DataFrame(
    {'ticker' : ['a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c'], 'timestamp' : [1,1,1,2,2,2,3,3,3], 'val' : [10,11,12,21,22,23, 100, 200, 300]})

tickers = df.ticker.tolist()
fig = go.FigureWidget()

@interact
def read_values(
    selected_ticker=widgets.Dropdown(
        description="Select :", value="a", options=tickers[:3]
    )
):

    df_sub = df[df.ticker == selected_ticker]
    fig.data = []
    fig.add_trace(
        go.Scatter(x = df_sub.timestamp, y = df_sub.val)
    )

fig
Serhiy
  • 4,357
  • 5
  • 37
  • 53