Fabuła: Jak utworzyć opcję rozwijania wielu indeksów?
Mam dane o tym samym numerze indeksu dla różnych przedziałów czasowych, jak poniżej
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
korzystając z poniższego kodu zrobiłem wykres:
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()

Chcę menu rozwijane, które wybiera inny indeks, a dane wykresu odpowiednio się zmieniają. na przykład jeśli wybiorę z listy rozwijanej 29600, pokaże tylko dane dla tego numeru indeksu, a także istnieje sposób na odwrócenie osi x (czas) od lewej do prawej. Z góry dziękuję za wszelkie rozwiązania
Odpowiedzi
Edycja 2 - zaktualizowana sugestia z połączonym zbiorem danych
Aby skorzystać z pełnego zestawu danych podanego w linku , po prostu pobierz tę zawartość jako plik csv, otwórz go i skopiuj zawartość, a następnie uruchom poniższy kod, aby uzyskać następny rysunek. Dane są zbierane za pomocą dfi = pd.read_clipboard(sep=',')
. Naprawdę nie ma potrzeby zawracać sobie głowy ustawieniem 'Strike Price
jako indeks. Należy pamiętać, że zbiór danych zawiera wiele 0
wartości, ale wybranie na przykład 26100
da przynajmniej znaczące wyniki:

Kompletny kod do edycji 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)
Edycja - zaktualizowano sugestię z odwracaniem osi
Moja najnowsza propozycja opiera się na przykład w ramach sekcji Share data between callbacks
od dcc.Store i dokona niezbędnych dostosowań do pracy dla przypadku użycia. Dodałem również funkcję odwracania wartości osi X za pomocą:fig.update_layout(xaxis = dict(autorange='reversed'))
Oto wynik:

A oto pełny kod:
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)
Sugestia 1
Nie określiłeś, w jaki sposób używasz swoich danych. Ale zakładając, że jest w JupyterLab, poleciłbym użycie JupyterDash. Uważam, że jest to o wiele łatwiejsze niż włączenie funkcji rozwijanych bezpośrednio na rysunku, na co wskazali początkujący w linku w komentarzach.
Poniższy fragment kodu umożliwia wybranie indeksu, z którego mają być wyświetlane dane w poniższej aplikacji, która jest ustawiona do tworzenia liczby, 'inline'
co oznacza w samym notatniku. Jeśli jesteś zainteresowany zastosowaniem takiego podejścia, mogę zobaczyć, czy mogę zaimplementować przycisk do odwrócenia osi X.
Aplikacja:

Kompletny kod
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)