Solución:
(Descargo de responsabilidad, ayudo a mantener Dash)
Consulte https://github.com/plotly/jupyterlab-dash. Esta es una extensión de JupyterLab que incorpora Dash dentro de Jupyter.
También vea soluciones alternativas en el Foro de la comunidad de Dash, como el tema ¿Puedo ejecutar la aplicación Dash en jupyter?
Ya existe una gran respuesta a esta pregunta, pero esta contribución se centrará directamente en:
1. Cómo usar Dash dentro de Jupyterlab, y
2. cómo seleccione la entrada gráfica colocando el cursor sobre sobre otro gráfico
Si sigue estos pasos, se lanzará Plotly Dash directamente en JupyterLab:
1. Instale la última versión de Plotly
2. Instale JupyterLab Dash con conda install -c plotly jupyterlab-dash
3. Usando el fragmento proporcionado un poco más abajo, inicie una aplicación Dash que contiene una animación construida en un marco de datos de pandas que se expande cada segundo.
Captura de pantalla de Dash en JupyterLab (código en el fragmento a continuación)
Esta imagen muestra Dash literalmente encendido dentro de JupyterLab. Las cuatro secciones resaltadas son:
1 – Celda. Una celda en un .ipynb
con el que probablemente ya estés muy familiarizado
2 – Dash. Una aplicación de tablero “en vivo” que expande los tres trazos con un número aleatorio y muestra la figura actualizada cada segundo.
3 – Consola. Una consola donde puede inspeccionar los elementos disponibles en su script usando, por ejemplo, fig.show
4 – mode
. Esto muestra dónde reside la verdadera magia:
app.run_server(mode="jupyterlab", port = 8090, dev_tools_ui=True, #debug=True,
dev_tools_hot_reload =True, threaded=True)
Puede optar por iniciar la aplicación del tablero en:
- Jupyterlab, como en la captura de pantalla con
mode="jupyterlab"
, - o en una celda, usando
mode="inline"
:
- o en su navegador predeterminado usando
mode="external"
Codigo 1:
import pandas as pd
import numpy as np
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
# code and plot setup
# settings
pd.options.plotting.backend = "plotly"
# sample dataframe of a wide format
np.random.seed(4); cols = list('abc')
X = np.random.randn(50,len(cols))
df=pd.DataFrame(X, columns=cols)
df.iloc[0]=0;
# plotly figure
fig = df.plot(template="plotly_dark")
app = JupyterDash(__name__)
app.layout = html.Div([
html.H1("Random datastream"),
dcc.Interval(
id='interval-component',
interval=1*1000, # in milliseconds
n_intervals=0
),
dcc.Graph(id='graph'),
])
# Define callback to update graph
@app.callback(
Output('graph', 'figure'),
[Input('interval-component', "n_intervals")]
)
def streamFig(value):
global df
Y = np.random.randn(1,len(cols))
df2 = pd.DataFrame(Y, columns = cols)
df = df.append(df2, ignore_index=True)#.reset_index()
df.tail()
df3=df.copy()
df3 = df3.cumsum()
fig = df3.plot(template="plotly_dark")
#fig.show()
return(fig)
app.run_server(mode="jupyterlab", port = 8090, dev_tools_ui=True, #debug=True,
dev_tools_hot_reload =True, threaded=True)
Pero la buena noticia no termina ahí, en cuanto a:
Mi intención es vincular gráficos dentro de un cuaderno de Jupyter para que al pasar el cursor sobre un gráfico se genere la entrada requerida para otro gráfico.
Hay un ejemplo perfecto en dash.plotly.com que hará exactamente eso por ti debajo del párrafo Update Graphs on Hover
:
Hice los pocos cambios necesarios en la configuración original para que sea posible ejecutarlo en JupyterLab.
Fragmento de código 2: seleccione la fuente del gráfico colocando el cursor sobre:
import pandas as pd
import numpy as np
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
import dash.dependencies
# code and plot setup
# settings
pd.options.plotting.backend = "plotly"
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = JupyterDash(__name__, external_stylesheets=external_stylesheets)
df = pd.read_csv('https://plotly.github.io/datasets/country_indicators.csv')
available_indicators = df['Indicator Name'].unique()
app.layout = html.Div([
html.Div([
html.Div([
dcc.Dropdown(
id='crossfilter-xaxis-column',
options=[{'label': i, 'value': i} for i in available_indicators],
value="Fertility rate, total (births per woman)"
),
dcc.RadioItems(
id='crossfilter-xaxis-type',
options=[{'label': i, 'value': i} for i in ['Linear', 'Log']],
value="Linear",
labelStyle={'display': 'inline-block'}
)
],
style={'width': '49%', 'display': 'inline-block'}),
html.Div([
dcc.Dropdown(
id='crossfilter-yaxis-column',
options=[{'label': i, 'value': i} for i in available_indicators],
value="Life expectancy at birth, total (years)"
),
dcc.RadioItems(
id='crossfilter-yaxis-type',
options=[{'label': i, 'value': i} for i in ['Linear', 'Log']],
value="Linear",
labelStyle={'display': 'inline-block'}
)
], style={'width': '49%', 'float': 'right', 'display': 'inline-block'})
], style={
'borderBottom': 'thin lightgrey solid',
'backgroundColor': 'rgb(250, 250, 250)',
'padding': '10px 5px'
}),
html.Div([
dcc.Graph(
id='crossfilter-indicator-scatter',
hoverData={'points': [{'customdata': 'Japan'}]}
)
], style={'width': '49%', 'display': 'inline-block', 'padding': '0 20'}),
html.Div([
dcc.Graph(id='x-time-series'),
dcc.Graph(id='y-time-series'),
], style={'display': 'inline-block', 'width': '49%'}),
html.Div(dcc.Slider(
id='crossfilter-year--slider',
min=df['Year'].min(),
max=df['Year'].max(),
value=df['Year'].max(),
marks={str(year): str(year) for year in df['Year'].unique()},
step=None
), style={'width': '49%', 'padding': '0px 20px 20px 20px'})
])
@app.callback(
dash.dependencies.Output('crossfilter-indicator-scatter', 'figure'),
[dash.dependencies.Input('crossfilter-xaxis-column', 'value'),
dash.dependencies.Input('crossfilter-yaxis-column', 'value'),
dash.dependencies.Input('crossfilter-xaxis-type', 'value'),
dash.dependencies.Input('crossfilter-yaxis-type', 'value'),
dash.dependencies.Input('crossfilter-year--slider', 'value')])
def update_graph(xaxis_column_name, yaxis_column_name,
xaxis_type, yaxis_type,
year_value):
dff = df[df['Year'] == year_value]
fig = px.scatter(x=dff[dff['Indicator Name'] == xaxis_column_name]['Value'],
y=dff[dff['Indicator Name'] == yaxis_column_name]['Value'],
hover_name=dff[dff['Indicator Name'] == yaxis_column_name]['Country Name']
)
fig.update_traces(customdata=dff[dff['Indicator Name'] == yaxis_column_name]['Country Name'])
fig.update_xaxes(title=xaxis_column_name, type="linear" if xaxis_type == 'Linear' else 'log')
fig.update_yaxes(title=yaxis_column_name, type="linear" if yaxis_type == 'Linear' else 'log')
fig.update_layout(margin={'l': 40, 'b': 40, 't': 10, 'r': 0}, hovermode="closest")
return fig
def create_time_series(dff, axis_type, title):
fig = px.scatter(dff, x='Year', y='Value')
fig.update_traces(mode="lines+markers")
fig.update_xaxes(showgrid=False)
fig.update_yaxes(type="linear" if axis_type == 'Linear' else 'log')
fig.add_annotation(x=0, y=0.85, xanchor="left", yanchor="bottom",
xref="paper", yref="paper", showarrow=False, align='left',
bgcolor="rgba(255, 255, 255, 0.5)", text=title)
fig.update_layout(height=225, margin={'l': 20, 'b': 30, 'r': 10, 't': 10})
return fig
@app.callback(
dash.dependencies.Output('x-time-series', 'figure'),
[dash.dependencies.Input('crossfilter-indicator-scatter', 'hoverData'),
dash.dependencies.Input('crossfilter-xaxis-column', 'value'),
dash.dependencies.Input('crossfilter-xaxis-type', 'value')])
def update_y_timeseries(hoverData, xaxis_column_name, axis_type):
country_name = hoverData['points'][0]['customdata']
dff = df[df['Country Name'] == country_name]
dff = dff[dff['Indicator Name'] == xaxis_column_name]
title="<b>{}</b><br>{}".format(country_name, xaxis_column_name)
return create_time_series(dff, axis_type, title)
@app.callback(
dash.dependencies.Output('y-time-series', 'figure'),
[dash.dependencies.Input('crossfilter-indicator-scatter', 'hoverData'),
dash.dependencies.Input('crossfilter-yaxis-column', 'value'),
dash.dependencies.Input('crossfilter-yaxis-type', 'value')])
def update_x_timeseries(hoverData, yaxis_column_name, axis_type):
dff = df[df['Country Name'] == hoverData['points'][0]['customdata']]
dff = dff[dff['Indicator Name'] == yaxis_column_name]
return create_time_series(dff, axis_type, yaxis_column_name)
app.run_server(mode="jupyterlab", port = 8090, dev_tools_ui=True, #debug=True,
dev_tools_hot_reload =True, threaded=True)