Plotly: Comment créer une option de liste déroulante à index multiples?

Nov 19 2020

J'ai des données avec le même numéro d'index pour une période différente comme ci-dessous

           Time CallOI  PutOI   CallLTP PutLTP  
29500   3:30 PM 502725  554775  343.70  85.50   
29500   3:15 PM 568725  629700  357.15  81.70   
29500   2:59 PM 719350  689850  337.85  95.45   
29500   2:45 PM 786975  641575  360.00  108.35  
29500   2:30 PM 823500  626875  336.50  127.80  
29500   2:15 PM 812450  631800  308.55  143.00  
29500   2:00 PM 974700  617750  389.80  120.00  
29500   1:45 PM 1072675 547100  262.55  186.85  
29500   1:30 PM 1272300 469600  206.85  232.00  
29600   3:30 PM 502725  554775  343.70  85.50   
29600   3:15 PM 568725  629700  357.15  81.70   
29600   2:59 PM 719350  689850  337.85  95.45   
29600   2:45 PM 786975  641575  360.00  108.35  
29600   2:30 PM 823500  626875  336.50  127.80  
29600   2:15 PM 812450  631800  308.55  143.00  
29600   2:00 PM 974700  617750  389.80  120.00  
29600   1:45 PM 1072675 547100  262.55  186.85  
29600   1:30 PM 1272300 469600  206.85  232.00  
29700   3:30 PM 502725  554775  343.70  85.50   
29700   3:15 PM 568725  629700  357.15  81.70   
29700   2:59 PM 719350  689850  337.85  95.45   
29700   2:45 PM 786975  641575  360.00  108.35  
29700   2:30 PM 823500  626875  336.50  127.80  
29700   2:15 PM 812450  631800  308.55  143.00  
29700   2:00 PM 974700  617750  389.80  120.00  
29700   1:45 PM 1072675 547100  262.55  186.85  
29700   1:30 PM 1272300 469600  206.85  232.00  

en utilisant le code ci-dessous, j'ai fait un graphique:

subfig = make_subplots(specs=[[{"secondary_y": True}]])

# create two independent figures with px.line each containing data from multiple columns
fig = px.line(df,x='Time', y='Call OI')
fig2 = px.line(df,x='Time', y='Call LTP')

fig2.update_traces(yaxis="y2")

subfig.add_traces(fig.data + fig2.data)
subfig.layout.xaxis.title="Time"
subfig.layout.yaxis.title="OI"
subfig.layout.yaxis2.type="log"
subfig.layout.yaxis2.title="Price"
# recoloring is necessary otherwise lines from fig und fig2 would share each color
# e.g. Linear-, Log- = blue; Linear+, Log+ = red... we don't want this
subfig.for_each_trace(lambda t: t.update(line=dict(color=t.marker.color)))
subfig.show()

Je veux un menu déroulant qui sélectionne différents index et les données du graphique changent en conséquence. Par exemple, si je sélectionne dans la liste déroulante 29600, il affiche uniquement les données pour ce numéro d'index et existe également un moyen de retourner l'axe des x (temps) de gauche à droite. Merci d'avance pour toute solution

Réponses

1 vestland Nov 22 2020 at 01:16

Edit 2 - Suggestion mise à jour avec ensemble de données lié

Pour utiliser l'ensemble de données complet fourni dans le lien , téléchargez simplement ce contenu sous forme de fichier csv, ouvrez-le et copiez le contenu, puis exécutez le code ci-dessous pour obtenir la figure suivante. Les données sont récupérées à l'aide de dfi = pd.read_clipboard(sep=','). Il n'y a vraiment pas besoin de se soucier de définir 'Strike Pricecomme index. Veuillez noter que l'ensemble de données a beaucoup de 0valeurs, mais la sélection, par exemple, 26100produira au moins une sortie significative:

Code complet pour l'édition 2

import collections
import dash
import pandas as pd

from dash.dependencies import Output, Input
from dash.exceptions import PreventUpdate

from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State, ClientsideFunction
import dash_bootstrap_components as dbc
import dash_core_components as dcc
import dash_html_components as html
from plotly.subplots import make_subplots
import plotly.graph_objects as go

dfi = pd.read_clipboard(sep=',')
df = dfi.copy()

idx = list(df['Strike Price'].unique())

app = JupyterDash()

app.layout = html.Div([
    dcc.Store(id='memory-output'),
    dcc.Dropdown(id='memory-countries', options=[
        {'value': x, 'label': x} for x in idx
    ], multi=False, value=idx[0]), 
        dcc.Dropdown(id='memory-field', options=[
        {'value': 'default', 'label': 'default'},
        {'value': 'reverse', 'label': 'reverse'},
    ], value='default'),
    
    html.Div([
        dcc.Graph(id='memory-graph'),
    ])
])


@app.callback(Output('memory-output', 'data'),
              [Input('memory-countries', 'value')])
def filter_countries(idx_selected):
    if not idx_selected:
        # Return all the rows on initial load/no country selected.
        return(idx_selected)
    return(idx_selected)

@app.callback(Output('memory-graph', 'figure'),
              [Input('memory-output', 'data'),
              Input('memory-field', 'value')])
def on_data_set_graph(data, field):
#     print(data)
#     global dff
    if data is None:
        raise PreventUpdate
    
    # figure setup
    fig = make_subplots(specs=[[{"secondary_y": True}]])

    dff = df[df['Strike Price']==data]
    fig.add_trace(go.Scatter(x=dff.Time, y = dff['Call OI'], name = 'Call'), secondary_y=True)
    fig.add_trace(go.Scatter(x=dff.Time, y = dff['Call LTP'], name = 'Put'), secondary_y=False)
    
    # flip axis
    if field != 'default':
        fig.update_layout(xaxis = dict(autorange='reversed'))
    
    return(fig)

app.run_server(mode='inline', port = 8072, dev_tools_ui=True,
          dev_tools_hot_reload =True, threaded=True, debug=True)

Modifier - Suggestion mise à jour avec retournement d'axe

Ma dernière suggestion s'appuie sur un exemple sous la section Share data between callbacksde dcc.Store et apporte les ajustements nécessaires pour fonctionner pour votre cas d'utilisation. J'ai également intégré une fonctionnalité pour inverser vos valeurs de l'axe des x en utilisant:fig.update_layout(xaxis = dict(autorange='reversed'))

Voici le résultat:

Et voici le code complet:

import collections
import dash
import pandas as pd

from dash.dependencies import Output, Input
from dash.exceptions import PreventUpdate

from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State, ClientsideFunction
import dash_bootstrap_components as dbc
import dash_core_components as dcc
import dash_html_components as html
from plotly.subplots import make_subplots
import plotly.graph_objects as go

df = pd.DataFrame({'Time': {(29500, '3:30'): 'PM',
                              (29500, '3:15'): 'PM',
                              (29500, '2:59'): 'PM',
                              (29500, '2:45'): 'PM',
                              (29500, '2:30'): 'PM',
                              (29500, '2:15'): 'PM',
                              (29500, '2:00'): 'PM',
                              (29500, '1:45'): 'PM',
                              (29500, '1:30'): 'PM',
                              (29600, '3:30'): 'PM',
                              (29600, '3:15'): 'PM',
                              (29600, '2:59'): 'PM',
                              (29600, '2:45'): 'PM',
                              (29600, '2:30'): 'PM',
                              (29600, '2:15'): 'PM',
                              (29600, '2:00'): 'PM',
                              (29600, '1:45'): 'PM',
                              (29600, '1:30'): 'PM',
                              (29700, '3:30'): 'PM',
                              (29700, '3:15'): 'PM',
                              (29700, '2:59'): 'PM',
                              (29700, '2:45'): 'PM',
                              (29700, '2:30'): 'PM',
                              (29700, '2:15'): 'PM',
                              (29700, '2:00'): 'PM',
                              (29700, '1:45'): 'PM',
                              (29700, '1:30'): 'PM'},
                             'CallOI': {(29500, '3:30'): 502725,
                              (29500, '3:15'): 568725,
                              (29500, '2:59'): 719350,
                              (29500, '2:45'): 786975,
                              (29500, '2:30'): 823500,
                              (29500, '2:15'): 812450,
                              (29500, '2:00'): 974700,
                              (29500, '1:45'): 1072675,
                              (29500, '1:30'): 1272300,
                              (29600, '3:30'): 502725,
                              (29600, '3:15'): 568725,
                              (29600, '2:59'): 719350,
                              (29600, '2:45'): 786975,
                              (29600, '2:30'): 823500,
                              (29600, '2:15'): 812450,
                              (29600, '2:00'): 974700,
                              (29600, '1:45'): 1000000,
                              (29600, '1:30'): 1272300,
                              (29700, '3:30'): 502725,
                              (29700, '3:15'): 568725,
                              (29700, '2:59'): 719350,
                              (29700, '2:45'): 786975,
                              (29700, '2:30'): 823500,
                              (29700, '2:15'): 812450,
                              (29700, '2:00'): 974700,
                              (29700, '1:45'): 1172675,
                              (29700, '1:30'): 1272300},
                             'PutOI': {(29500, '3:30'): 554775,
                              (29500, '3:15'): 629700,
                              (29500, '2:59'): 689850,
                              (29500, '2:45'): 641575,
                              (29500, '2:30'): 626875,
                              (29500, '2:15'): 631800,
                              (29500, '2:00'): 617750,
                              (29500, '1:45'): 547100,
                              (29500, '1:30'): 469600,
                              (29600, '3:30'): 554775,
                              (29600, '3:15'): 629700,
                              (29600, '2:59'): 689850,
                              (29600, '2:45'): 641575,
                              (29600, '2:30'): 626875,
                              (29600, '2:15'): 631800,
                              (29600, '2:00'): 617750,
                              (29600, '1:45'): 547100,
                              (29600, '1:30'): 469600,
                              (29700, '3:30'): 554775,
                              (29700, '3:15'): 629700,
                              (29700, '2:59'): 689850,
                              (29700, '2:45'): 641575,
                              (29700, '2:30'): 626875,
                              (29700, '2:15'): 631800,
                              (29700, '2:00'): 617750,
                              (29700, '1:45'): 547100,
                              (29700, '1:30'): 469600},
                             'CallLTP': {(29500, '3:30'): 343.7,
                              (29500, '3:15'): 357.15,
                              (29500, '2:59'): 337.85,
                              (29500, '2:45'): 360.0,
                              (29500, '2:30'): 336.5,
                              (29500, '2:15'): 308.55,
                              (29500, '2:00'): 389.8,
                              (29500, '1:45'): 262.55,
                              (29500, '1:30'): 206.85,
                              (29600, '3:30'): 343.7,
                              (29600, '3:15'): 357.15,
                              (29600, '2:59'): 337.85,
                              (29600, '2:45'): 360.0,
                              (29600, '2:30'): 336.5,
                              (29600, '2:15'): 308.55,
                              (29600, '2:00'): 389.8,
                              (29600, '1:45'): 262.55,
                              (29600, '1:30'): 206.85,
                              (29700, '3:30'): 343.7,
                              (29700, '3:15'): 357.15,
                              (29700, '2:59'): 337.85,
                              (29700, '2:45'): 360.0,
                              (29700, '2:30'): 336.5,
                              (29700, '2:15'): 308.55,
                              (29700, '2:00'): 389.8,
                              (29700, '1:45'): 262.55,
                              (29700, '1:30'): 206.85},
                             'PutLTP': {(29500, '3:30'): 85.5,
                              (29500, '3:15'): 81.7,
                              (29500, '2:59'): 95.45,
                              (29500, '2:45'): 108.35,
                              (29500, '2:30'): 127.8,
                              (29500, '2:15'): 143.0,
                              (29500, '2:00'): 120.0,
                              (29500, '1:45'): 186.85,
                              (29500, '1:30'): 232.0,
                              (29600, '3:30'): 85.5,
                              (29600, '3:15'): 81.7,
                              (29600, '2:59'): 95.45,
                              (29600, '2:45'): 108.35,
                              (29600, '2:30'): 127.8,
                              (29600, '2:15'): 143.0,
                              (29600, '2:00'): 120.0,
                              (29600, '1:45'): 186.85,
                              (29600, '1:30'): 232.0,
                              (29700, '3:30'): 85.5,
                              (29700, '3:15'): 81.7,
                              (29700, '2:59'): 95.45,
                              (29700, '2:45'): 108.35,
                              (29700, '2:30'): 127.8,
                              (29700, '2:15'): 143.0,
                              (29700, '2:00'): 120.0,
                              (29700, '1:45'): 186.85,
                              (29700, '1:30'): 232.0}})

df = df.reset_index()
idx = list(df['level_0'].unique())

app = JupyterDash()

app.layout = html.Div([
    dcc.Store(id='memory-output'),
    dcc.Dropdown(id='memory-countries', options=[
        {'value': x, 'label': x} for x in idx
    ], multi=False, value=idx[0]), 
        dcc.Dropdown(id='memory-field', options=[
        {'value': 'default', 'label': 'default'},
        {'value': 'reverse', 'label': 'reverse'},
    ], value='default'),
    
    html.Div([
        dcc.Graph(id='memory-graph'),
    ])
])


@app.callback(Output('memory-output', 'data'),
              [Input('memory-countries', 'value')])
def filter_countries(idx_selected):
    if not idx_selected:
        # Return all the rows on initial load/no country selected.
        return(idx_selected)
    return(idx_selected)

@app.callback(Output('memory-graph', 'figure'),
              [Input('memory-output', 'data'),
              Input('memory-field', 'value')])
def on_data_set_graph(data, field):
#     print(data)
    if data is None:
        raise PreventUpdate
    
    # figure setup
    fig = make_subplots(specs=[[{"secondary_y": True}]])

    dff = df[df['level_0']==data]
    fig.add_trace(go.Scatter(x=dff.level_1, y = dff.CallOI, name = 'Call'), secondary_y=True)
    fig.add_trace(go.Scatter(x=dff.level_1, y = dff.PutOI, name = 'Put'), secondary_y=False)
    
    # flip axis
    if field != 'default':
        fig.update_layout(xaxis = dict(autorange='reversed'))
    
    return(fig)

app.run_server(mode='inline', port = 8072, dev_tools_ui=True,
          dev_tools_hot_reload =True, threaded=True, debug=True)

Suggestion 1


Vous n'avez pas précisé comment vous utilisez vos chiffres. Mais en supposant que ce soit dans JupyterLab, je recommanderais vivement d'utiliser JupyterDash. Je trouve cela beaucoup plus agréable que d'incorporer des fonctionnalités de liste déroulante directement dans la figure, comme l'ont souligné les r-débutants dans le lien dans les commentaires.

L'extrait de code ci-dessous vous permettra de sélectionner l'index à partir duquel afficher les données dans l'application suivante qui est définie pour produire la figure 'inline'qui signifie dans le bloc-notes lui-même. Si vous souhaitez utiliser une approche comme celle-ci, je peux voir si je peux également implémenter un bouton pour inverser l'axe des x.

App:

Code complet

import numpy as np
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
from plotly.subplots import make_subplots
from dash.dependencies import Input, Output, State

# data
df = pd.DataFrame({'Time': {(29500, '3:30'): 'PM',
                              (29500, '3:15'): 'PM',
                              (29500, '2:59'): 'PM',
                              (29500, '2:45'): 'PM',
                              (29500, '2:30'): 'PM',
                              (29500, '2:15'): 'PM',
                              (29500, '2:00'): 'PM',
                              (29500, '1:45'): 'PM',
                              (29500, '1:30'): 'PM',
                              (29600, '3:30'): 'PM',
                              (29600, '3:15'): 'PM',
                              (29600, '2:59'): 'PM',
                              (29600, '2:45'): 'PM',
                              (29600, '2:30'): 'PM',
                              (29600, '2:15'): 'PM',
                              (29600, '2:00'): 'PM',
                              (29600, '1:45'): 'PM',
                              (29600, '1:30'): 'PM',
                              (29700, '3:30'): 'PM',
                              (29700, '3:15'): 'PM',
                              (29700, '2:59'): 'PM',
                              (29700, '2:45'): 'PM',
                              (29700, '2:30'): 'PM',
                              (29700, '2:15'): 'PM',
                              (29700, '2:00'): 'PM',
                              (29700, '1:45'): 'PM',
                              (29700, '1:30'): 'PM'},
                             'CallOI': {(29500, '3:30'): 502725,
                              (29500, '3:15'): 568725,
                              (29500, '2:59'): 719350,
                              (29500, '2:45'): 786975,
                              (29500, '2:30'): 823500,
                              (29500, '2:15'): 812450,
                              (29500, '2:00'): 974700,
                              (29500, '1:45'): 1072675,
                              (29500, '1:30'): 1272300,
                              (29600, '3:30'): 502725,
                              (29600, '3:15'): 568725,
                              (29600, '2:59'): 719350,
                              (29600, '2:45'): 786975,
                              (29600, '2:30'): 823500,
                              (29600, '2:15'): 812450,
                              (29600, '2:00'): 974700,
                              (29600, '1:45'): 1000000,
                              (29600, '1:30'): 1272300,
                              (29700, '3:30'): 502725,
                              (29700, '3:15'): 568725,
                              (29700, '2:59'): 719350,
                              (29700, '2:45'): 786975,
                              (29700, '2:30'): 823500,
                              (29700, '2:15'): 812450,
                              (29700, '2:00'): 974700,
                              (29700, '1:45'): 1172675,
                              (29700, '1:30'): 1272300},
                             'PutOI': {(29500, '3:30'): 554775,
                              (29500, '3:15'): 629700,
                              (29500, '2:59'): 689850,
                              (29500, '2:45'): 641575,
                              (29500, '2:30'): 626875,
                              (29500, '2:15'): 631800,
                              (29500, '2:00'): 617750,
                              (29500, '1:45'): 547100,
                              (29500, '1:30'): 469600,
                              (29600, '3:30'): 554775,
                              (29600, '3:15'): 629700,
                              (29600, '2:59'): 689850,
                              (29600, '2:45'): 641575,
                              (29600, '2:30'): 626875,
                              (29600, '2:15'): 631800,
                              (29600, '2:00'): 617750,
                              (29600, '1:45'): 547100,
                              (29600, '1:30'): 469600,
                              (29700, '3:30'): 554775,
                              (29700, '3:15'): 629700,
                              (29700, '2:59'): 689850,
                              (29700, '2:45'): 641575,
                              (29700, '2:30'): 626875,
                              (29700, '2:15'): 631800,
                              (29700, '2:00'): 617750,
                              (29700, '1:45'): 547100,
                              (29700, '1:30'): 469600},
                             'CallLTP': {(29500, '3:30'): 343.7,
                              (29500, '3:15'): 357.15,
                              (29500, '2:59'): 337.85,
                              (29500, '2:45'): 360.0,
                              (29500, '2:30'): 336.5,
                              (29500, '2:15'): 308.55,
                              (29500, '2:00'): 389.8,
                              (29500, '1:45'): 262.55,
                              (29500, '1:30'): 206.85,
                              (29600, '3:30'): 343.7,
                              (29600, '3:15'): 357.15,
                              (29600, '2:59'): 337.85,
                              (29600, '2:45'): 360.0,
                              (29600, '2:30'): 336.5,
                              (29600, '2:15'): 308.55,
                              (29600, '2:00'): 389.8,
                              (29600, '1:45'): 262.55,
                              (29600, '1:30'): 206.85,
                              (29700, '3:30'): 343.7,
                              (29700, '3:15'): 357.15,
                              (29700, '2:59'): 337.85,
                              (29700, '2:45'): 360.0,
                              (29700, '2:30'): 336.5,
                              (29700, '2:15'): 308.55,
                              (29700, '2:00'): 389.8,
                              (29700, '1:45'): 262.55,
                              (29700, '1:30'): 206.85},
                             'PutLTP': {(29500, '3:30'): 85.5,
                              (29500, '3:15'): 81.7,
                              (29500, '2:59'): 95.45,
                              (29500, '2:45'): 108.35,
                              (29500, '2:30'): 127.8,
                              (29500, '2:15'): 143.0,
                              (29500, '2:00'): 120.0,
                              (29500, '1:45'): 186.85,
                              (29500, '1:30'): 232.0,
                              (29600, '3:30'): 85.5,
                              (29600, '3:15'): 81.7,
                              (29600, '2:59'): 95.45,
                              (29600, '2:45'): 108.35,
                              (29600, '2:30'): 127.8,
                              (29600, '2:15'): 143.0,
                              (29600, '2:00'): 120.0,
                              (29600, '1:45'): 186.85,
                              (29600, '1:30'): 232.0,
                              (29700, '3:30'): 85.5,
                              (29700, '3:15'): 81.7,
                              (29700, '2:59'): 95.45,
                              (29700, '2:45'): 108.35,
                              (29700, '2:30'): 127.8,
                              (29700, '2:15'): 143.0,
                              (29700, '2:00'): 120.0,
                              (29700, '1:45'): 186.85,
                              (29700, '1:30'): 232.0}})

df = df.reset_index()

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = JupyterDash(__name__, external_stylesheets=external_stylesheets)

# options for dropdown
criteria = list(df['level_0'].unique())
options = [{'label': i, 'value': i} for i in criteria]
options.append

# app layout
app.layout = html.Div([
                    html.Div([
                        html.Div([
                                  dcc.Dropdown(id='linedropdown',
                                               options=options,                    
                                               value=options[0]['value'],),
                                 ],
                                ),
                                ],className='row'),

                    html.Div([
                        html.Div([
                                  dcc.Graph(id='linechart'),
                                 ],
                                ),
                             ],
                            ),
])

@app.callback(
    [Output('linechart', 'figure')],
    [Input('linedropdown', 'value')]
)

def update_graph(linedropdown):

    # selection using linedropdown
    dff = df[df['level_0']==linedropdown]

    # Create figure with secondary y-axis
    fig = make_subplots(specs=[[{"secondary_y": True}]])

    # Add trace 1
    fig.add_trace(
        go.Scatter(x=dff['level_1'], y=dff['CallOI'], name="Call OI"),
        secondary_y=True,
    )

    # Add trace 2
    fig.add_trace(
        go.Scatter(x=dff['level_1'], y=dff['CallLTP'], name="Call LTP"),
        secondary_y=False,
    )
    fig.update_layout(title = 'Index: ' + str(linedropdown))
    
    return ([fig])

# Run app and display result inline in the notebook
app.run_server(mode='inline', port = 8040, dev_tools_ui=True, debug=True,
              dev_tools_hot_reload =True, threaded=True)