Add tbutton that edits two scatter at once

Hi,

I’m trying trying to add a single widget that allows me to:

  1. choose data displayed in scatter 1
  2. choose data displayed in scatter 2

For the now I only found a solution for a single scatter:

# 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()

How to make it work with two different scatters and a single button widget?

Hi @kerim ,

If I understand what you need to achieve, maybe the output something like this :

two_traces_update_menus2-output

Here is the options that you can do :
Suppose the button will update two traces in one Figure and assuming that the ticker number is even
so it can be divided by 2, so I customize your data frame by adding new ticker value ‘d’


df = pd.DataFrame(
    {'ticker' : ['a', 'b', 'c', 'd', 'a', 'b', 'c', 'd', 'a', 'b', 'c', 'd'], 
     'timestamp' : [1,1,1,1,2,2,2,2,3,3,3,3], 
     'val' : [10,110,12,130, 21,220,23,240, 100, 200, 300,400]
     }
    )

For initial lice only first two tickers, ex. ‘a’ and ‘b’ and using for loop to create two traces (trace ‘a’ an ‘b’ )

dfa = df.query("ticker in ['a','b']")

for idx,label in enumerate(tickers[:2]):

    fig.add_trace(go.Scatter(
        x=dfa[dfa['ticker'] == label].timestamp,
        y=dfa[dfa['ticker'] == label].val,
        visible=True,
        # mode='markers',
        marker=dict(size=12, line=dict(width=1, color=colors[idx])),
        ))

Updating the second trace by appending the x and y value in args.

for selection in zip(tickers[::2],tickers[1::2]):
    select_buttons.append(dict(method='update',
                        label="+".join(selection), # join two tickers as label
                        args=[{'x': [
                                        df.query(f"ticker in ['{selection[0]}']").timestamp, # update x values for first trace
                                        df.query(f"ticker in ['{selection[1]}']").timestamp  # update x values for second trace
                                     ], 
                               'y': [
                                        df.query(f"ticker in ['{selection[0]}']").val, # update y values for first trace
                                        df.query(f"ticker in ['{selection[1]}']").val]}, # update y values for first trace
                              ]
                        )
                )

And here is the complete code.

# 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

# Suppose the button will update two traces in one Figure.
# I assume that the ticker have even value so it can be devided by 2
# so I customize your dataframe by adding new ticker value 'd'
df = pd.DataFrame(
    {'ticker' : ['a', 'b', 'c', 'd', 'a', 'b', 'c', 'd', 'a', 'b', 'c', 'd'], 
     'timestamp' : [1,1,1,1,2,2,2,2,3,3,3,3], 
     'val' : [10,110,12,130, 21,220,23,240, 100, 200, 300,400]
     }
    )

# you can use unique() method to get only unique values
tickers = df.ticker.unique().tolist() 

# if you are using graph object you need to define the colors
colors = ['blue','red']

# Create figure and add one scatter trace
fig = go.Figure()

# for initial lice only first two tickers, ex. 'a' and 'b'
dfa = df.query("ticker in ['a','b']")
# and using for loop to create two traces (trace 'a' an 'b' )
for idx,label in enumerate(tickers[:2]):

    fig.add_trace(go.Scatter(
        x=dfa[dfa['ticker'] == label].timestamp,
        y=dfa[dfa['ticker'] == label].val,
        visible=True,
        # mode='markers',
        marker=dict(size=12, line=dict(width=1, color=colors[idx])),
        ))

# Create selection buttons
select_buttons = []

# Convert the ticker  from ['a','b','c','d']
# into [['a','b'],['c','d']]
# by using slice and zip
for selection in zip(tickers[::2],tickers[1::2]):
    select_buttons.append(dict(method='update',
                        label="+".join(selection), # join two tickers as label
                        args=[{'x': [
                                        df.query(f"ticker in ['{selection[0]}']").timestamp, # update x values for first trace
                                        df.query(f"ticker in ['{selection[1]}']").timestamp  # update x values for second trace
                                     ], 
                               'y': [
                                        df.query(f"ticker in ['{selection[0]}']").val, # update y values for first trace
                                        df.query(f"ticker in ['{selection[1]}']").val]}, # update y values for first trace
                              ]
                        )
                )
    
# 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()
2 Likes

@farispriadi thank you very much!
That was exactly what I was looking for!

1 Like