Plotly: ¿Cómo hacer una opción desplegable de índice múltiple?
Tengo datos con el mismo número de índice para diferentes períodos de tiempo como se muestra a continuación
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
usando el siguiente código, he hecho el gráfico:
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()
Quiero un menú desplegable que seleccione un índice diferente y los datos del gráfico cambien en consecuencia. Por ejemplo, si selecciono del menú desplegable 29600, solo muestra datos para ese número de índice y también hay una manera de voltear el eje x (tiempo) de izquierda a derecha. Gracias de antemano por cualquier solución.
Respuestas
Edición 2: sugerencia actualizada con conjunto de datos vinculado
Para utilizar el conjunto de datos completo proporcionado en el enlace , simplemente descargue ese contenido como un archivo csv, ábralo y copie el contenido, y luego ejecute el código a continuación para obtener la siguiente figura. Los datos se recogen utilizando dfi = pd.read_clipboard(sep=','). Realmente no hay necesidad de preocuparse por configurar 'Strike Pricecomo índice. Tenga en cuenta que el conjunto de datos tiene muchos 0valores, pero seleccionar, por ejemplo, 26100producirá al menos un resultado significativo:
Código completo para la edición 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)
Editar: sugerencia actualizada con giro de eje
Mi última sugerencia se basa en un ejemplo de la sección Share data between callbacksde dcc.Store y realiza los ajustes necesarios para que funcione en su caso de uso. También he incorporado una funcionalidad para cambiar los valores del eje x usando:fig.update_layout(xaxis = dict(autorange='reversed'))
Aquí está el resultado:
Y aquí está el código completo:
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)
Sugerencia 1
No ha especificado cómo está utilizando sus cifras. Pero suponiendo que esté en JupyterLab, recomendaría encarecidamente usar JupyterDash. Encuentro eso mucho más flexible que incorporar funciones desplegables directamente en la figura, como señalaron los principiantes en el enlace de los comentarios.
El fragmento de código a continuación le permitirá seleccionar de qué índice mostrar datos en la siguiente aplicación que está configurada para producir la figura 'inline'que significa en el propio cuaderno. Si está interesado en utilizar un enfoque como este, puedo ver si puedo implementar un botón para voltear el eje x también.
Aplicación:
Código completo
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)