Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Plotly-Dash: Adding new yaxis per selected df column

I have a basic dash app that graphs some data from a dictionary of dataframes. The first dropdown selects the df, while the second selects the columns of the df to be plotted.

This works well, but I can't seem to add a new yaxis for each of the plotted columns. I have a large number of columns in each df and they change depending on the df that is selected.

First, I tried to change the updateGraph callback to include yaxis=i after defining x, y & name. Looking at the documentation, it seems that I can define the yaxis in go.Scatter but that I would need to set them as 'y2', 'y3, 'y4' etc. I've also tried to update the layout via go.Figure.add_trace in this way but neither has worked. The code is below, where dict_main is a dictionary of dataframes of various sizes.

All help is appreciated!

data = list(dict_main.keys())
channels = dict_main[data[0]]

app.layout = html.Div(
    [
        html.Div([
            dcc.Dropdown(
                id='data-dropdown',
                options=[{'label': speed, 'value': speed} for speed in data],
                value=list(dict_main.keys())[0],
                searchable=False
            ),
        ], style={'width': '49%', 'display': 'inline-block'}),
        html.Div([
            dcc.Dropdown(
                id='channel-dropdown',
                multi=True
            ),
        ], style={'width': '49%', 'display': 'inline-block'}
        ),
        html.Div([
            dcc.Graph(
                id='Main-Graph',
            ),
        ], style={'width': '98%', 'display': 'inline-block'}
        )
    ]
)

@app.callback(
    Output('channel-dropdown', 'options'),
    [Input('data-dropdown', 'value')])
def update_date_dropdown(speed):
    return [{'label': i, 'value': i} for i in dict_main[speed]]

@app.callback(
    Output('Main-Graph', 'figure'),
    [Input('channel-dropdown', 'value')],
    [State('data-dropdown', 'value')])
def updateGraph(channels, speed):
    if channels:

        return go.Figure(data=[go.Scatter(x=dict_main[speed].index, y=dict_main[speed][i], name=i, yaxis='y2') for i in channels])
    else:
        return go.Figure(data=[])

if __name__ == '__main__':
    app.run_server()

UPDATE! This works, although some small changes to color and position are still needed - Thanks to @Philipp for all the help;

@app.callback(
    Output('Main-Graph', 'figure'),
    [Input('channel-dropdown', 'value')],
    [State('rpm-dropdown', 'value')])
def updateGraph(channels, test):
    if channels:
        j=1
        my_layout = {}
        my_axis = list("")
        for index, column in enumerate(list(channels)):
            my_layout['yaxis' + str(j) if j > 1 else 'yaxis'] = {}
            my_layout['yaxis' + str(j) if j > 1 else 'yaxis']['title'] = column
            my_layout['yaxis' + str(j) if j > 1 else 'yaxis']['overlaying'] = 'y' if j > 1 else 'free'
            my_layout['yaxis' + str(j) if j > 1 else 'yaxis']['anchor'] = 'free'
            my_layout['yaxis' + str(j) if j > 1 else 'yaxis']['side'] = 'left'
            my_axis.append('y' + str(j) if j > 1 else 'y')
            j+=1
        return go.Figure(data=[go.Scatter(x=dict_main[test].index, y=dict_main[test][column], name=column, yaxis=my_axis[index]) for index, column in enumerate(channels)],layout=my_layout)

    else:
        return go.Figure(data=[])
like image 890
Iceberg_Slim Avatar asked Nov 22 '19 09:11

Iceberg_Slim


1 Answers

You have to define every y-axis in the layout property of your graph (right now you're only setting the data property). See this example.

If you don't want to draw all y-axes (if your df has many columns) you have to set some of them invisible via setting variables like [overlaying, ticks, showticklabels, showgrid, zeroline] (you can find info about them here) but they still have to be defined accordingly in the layout so you can refer to them in the scatter function.

like image 77
Philipp Avatar answered Oct 27 '22 09:10

Philipp