Custom button to modify data source in plotly python offline


#1

I’ve played with an example at https://plot.ly/python/custom-buttons/ for plotting a Scatter plot with custom buttons to plot multiple traces and select which to show. The pattern used is to load everything and then show/hide using custom buttons to set a list with visibility set to True or False for each trace. I’m using offline mode.

I would like to instead have a button filter my dataset to a single trace and update the plot.

What arguments can I set to facilitate this?

For sake of discussion, I’ve included an example here. I’m working my way to control charts for a lab process, but for now I’m just trying to select which trace to display.

import plotly
import plotly.graph_objs as go
import pandas as pd

df = pd.read_csv('updated.csv')
filter_name = 'NameIsFoo'
filtered = df[df.name == filter_name]
metric = 'intensity'

trace = go.Scatter(
                    x=filtered['date'], y=filtered[metric], # Data
                    mode='lines', name=filter_name # Additional options
                   )

layout = go.Layout(
    title=filter_name,
    showlegend=True,
    width=1200,
    height=600,
    font=dict(
        family='Calibri, sans-serif', 
        size=18, 
        color='rgb(0,0,0)'),
        
    xaxis = dict(
        type='date',
        linewidth=1,
        title='Date',
        tickangle=-45,
        tickformat='%Y-%m-%d',
        tickfont=dict(
            size=12
            )
        ),
    yaxis = dict(
        linewidth=1,
        tickfont=dict(
            size=12
            )
        )
    )
    

fig = go.Figure(data=[trace], layout=layout)
plotly.offline.plot(fig, filename='line_chart')

I would like to use a custom button to re-define ‘data’ and replot.

I’ve tried something like the following (getTrace() returns a scatter object) but it doesn’t update the graph:

updatemenus=list([
    dict(
        buttons=list([   
            dict(label = 'Type1',
                 method = 'update', 
                 args=[{'data' : [getTrace('Type1')]}]
            ),
            dict(label = 'Type2',
                 method = 'update', 
                 args=[{'data' : [getTrace('Type2')]}]
            ),          
        ]),
        direction = 'left',
        pad = {'r': 10, 't': 10},
        showactive = True,
        type = 'buttons',
        x = 0.1,
        xanchor = 'left',
        y = 1.1,
        yanchor = 'top' 
    ),
])
layout['updatemenus'] = updatemenus
fig = go.Figure(data=[getTrace('Type1')], layout=layout)
plotly.offline.plot(fig, filename='line_chart.html')

#2

I found a solution, though it might not be the best.

I was not successful in trying to re-define ‘data’ in the update, but you can set x,y.

I created the following function:

def getDataByButton(filter_name):
    global metric
    global df
    # return arg list to set x, y and chart title
    filtered = df[df.name == filter_name]
    return [ {'x':[filtered['date']], 'y':[filtered[metric]], 'name':filter_name},
             {'title':filter_name} ]

and then in my button list I call it for the args parameter. Note that method must be update in order to have the chart title updated since chart title is part of layout:

updatemenus=list([
            dict(
                buttons=list([   
                    dict(label = 'Data Set 1',
                         method = 'update', 
                         args=getDataByButton('name1')
                    ),
                    dict(label = 'Data Set 2',
                         method = 'update', 
                         args=getDataByButton('name2')
                    ),                    
                ]),
                direction = 'left',
                pad = {'r': 10, 't': 10},
                showactive = True,
                type = 'buttons',
                x = 0.1,
                xanchor = 'left',
                y = 1.1,
                yanchor = 'top' 
            )
        ])

#3

Is there a way to explicit chose the trace, for which one wants to set x and y ?

I have numerous traces in my plot and I want to change only one of them. I’ve played with your solution, which let me change let me change all my traces to same data.


#4

I’m still very new to Plotly, so I don’t have a definite answer but finding a solution to your problem would be really useful.

At this update stage, I do not believe that you have direct access to individual scatter objects (that was my failure to re-define the data variable and have it update…it wouldn’t). I believe setting x and y merely changes the x and y values for the data already plotted so you will have to know where your particular trace lies in the entire set.

You may be able to target the particular trace with list comprehension of the pandas dataframe. Look at this page for an example:
https://chrisalbon.com/python/pandas_list_comprehension.html

If you can build the object to be returned in my example’s ‘getDataByButton’ function, you may be able to do it.

Feel free to post some code and we can all take a look and maybe steer you in the right direction. Good luck.


#5

[post deleted as the solution is incorrect - the legend is out of sync]


#6

I did manage to change more than one trace, but probably not in the most elegant way.
Here is an example :

def getDataByButton(df_1,df_2,axes,filter_name):
    filtered_1 = df_1.loc[df_1['name'] == filter_name]
    filtered_2 = df_2.loc[df_2['name'] == filter_name]
	
    return [
                {'type' : 'scatter3d',
                  'x' : [filtered_1['x'], filtered_2['x']]
                  'y' : [filtered_1['y'], filtered_2['y']]
                  'marker' : [{'color': '#88d498', 'size': 4, 'symbol': 'circle'},
                              {'color': '#63ace5', 'size': 4, 'symbol': 'circle'}],
                   'mode' : ['markers','markers'],
                   'name' : ['Trace_1','Trace_2'],
                   'text' : [filtered_1['label'], filtered_2['label']]}
		        ,{'scene' : dict(xaxis = dict(title=axes[0]),
                                 yaxis = dict(title=axes[1]))}
            ]

I still don’t really get how plotly saves the data. When I initiate a figure, I define them as a list of dicts:
fig = go.Figure(data = [trace_1, trace_2], layout = layout)
But these dicts are then merged to one and I can change that only to a certain extent?