Visualización Interactiva¶
En esta clase buscamos agregar valor a las visualizaciones con interactividad.
Jupyter Widgets¶
Ya sabemos que no hay mejor manera de describir algo que la forma con que los autores lo hacen, en el respositorio del proyecto nos cuentan lo siguiente:
ipywidgets are interactive HTML widgets for Jupyter notebooks and the IPython kernel.
Notebooks come alive when interactive widgets are used. Users gain control of their data and can visualize changes in the data.
Learning becomes an immersive, fun experience. Researchers can easily see how changing inputs to a model impact the results. We hope you will add ipywidgets to your notebooks, and we’re here to help you get started.
Toda la documentación la puedes encontrar en el siguiente link. En resumen, los widgets son mini-herramientas que brindan interactividad. En esta clase los utilizaremos para visualizaciones, con tal de entregar mayour control y facilitar la exploración.
Para motivar el uso de estos, se hará uso del Atractor de Lorenz.
from ipywidgets import interactive, fixed
import ipywidgets as widgets
from lorenz import solve_lorenz
%matplotlib inline
w=interactive(solve_lorenz,sigma=(0.0,50.0),rho=(0.0,50.0))
w
Para el conjunto de parámetros por defecto observamos trayectorias girando alrededor de dos puntos, llamados atractores.
El objeto devuelto por interactive
es de tipo Widget
y posee atributos que contienen el resultado actual y los argumentos.
t, x_t = w.result
w.kwargs ## Cambia el valor de algún widget de w y vuelve a ejecutar esta celda, verás que el valor cambió.
{'sigma': 10.0, 'beta': 2.6666666666666665, 'rho': 28.0}
Instalación¶
En la documentación oficial se encuentra detallada la forma de instalar ipywidgets
.
Para efectos del curso, se instalará en el mismo ambiente virtual que se ha utilizado a lo largo del semestre. Las instrucciones son:
En la terminal correspondiente activa el entorno virtual del curso, es decir, ejecuta
conda activate mat281
.Ejecutar en la terminal
conda install -c conda-forge ipywidgets
, recuerda quemat281
es el nombre del ambiente virtual.Si has seguido todas las instrucciones ya deberías tener instalado
nodejs
, puedes verificarlo ejecutandoconda list nodejs
, ahí debería aparecer la versión instalada.En la misma terminal (con el ambiente activado, no lo olvides!) ejecuta
jupyter labextension install @jupyter-widgets/jupyterlab-manager
from ipywidgets import interact, interactive, fixed, interact_manual
Interact¶
La función interact
crea automáticamente una interfaz de usuario (UI) de control para explorar código y datos interactivatemente.
def f(x):
return x
interact
genera automáticamente la interfaz dde control y luego llama a la función utilizando esos parámetros como argumentos para la función.
interact(f, x=10);
Si el argumento es booleano crea una checkbox en lugar de un slicer.
interact(f, x=True);
Si se le entrega un string interact
genera un text box.
interact(f, x='Hi there!');
Otra manera es utilizar interact
como un decorator. Esto permite definir una función e interactuar con ella en un solo paso.
Los decoradores se escapan de los contenidos del curso, pero básicamente son funciones tienen como argumento una función y extienden el comportamiento de esta última sin modificarla explícitamente.
@interact(x=True, y=1.0)
def g(x, y):
return (x, y)
Interactive¶
Además de ìnteract
, IPython proporciona otra función, interactive
, que es útil cuando desea reutilizar los widgets que se producen o acceder a los datos vinculados a los controles de la interfaz de usuario.
from IPython.display import display
def f(a, b):
output = a + b
display(output)
return output
w = interactive(f, a=10, b=20)
type(w)
ipywidgets.widgets.interaction.interactive
w.children # The children of the interactive are two integer-valued sliders and an output widget
(IntSlider(value=10, description='a', max=30, min=-10),
IntSlider(value=20, description='b', max=60, min=-20),
Output())
Para ver el widget, basta con utilizar la función display
de IPython.
display(w)
Finalmente, un ejemplo con un gráfico:
import matplotlib.pyplot as plt
import numpy as np
def f(m, b):
plt.figure(2)
x = np.linspace(-10, 10, num=1000)
plt.plot(x, m * x + b)
plt.ylim(-5, 5)
plt.show()
interactive_plot = interactive(f, m=(-2.0, 2.0), b=(-3, 3, 0.5))
interactive_plot
Si quisieras por ejemplo, que la pendiente m
solo sean números enteros y que el coeficiente de posición b
solo pueda ser escogido entre 0 y 3 puedes explicitar el tipo de widget para cada argumento.
Más detalle en la documentación! Link
interactive(
f,
m=widgets.IntSlider(0, -2, 2),
b=widgets.Dropdown(options=[0, 3])
)
Altair Interactivo¶
import altair as alt
from vega_datasets import data
Para que un gráfico en altair sea interactivo basta con agregar al final del chart el método .interactive()
. Para agregar mayor interactividad el encoding tooltip
es de mucha ayuda.
source = data.cars()
alt.Chart(source).mark_circle(size=60).encode(
x='Horsepower',
y='Miles_per_Gallon',
color='Origin',
tooltip=['Name', 'Origin', 'Horsepower', 'Miles_per_Gallon']
).interactive()
También es posible crear selecciones entrelazadas.
source = data.cars()
brush = alt.selection(type='interval', resolve='global')
base = alt.Chart(source).mark_point().encode(
y='Miles_per_Gallon',
color=alt.condition(brush, 'Origin', alt.ColorValue('gray')),
).add_selection(
brush
).properties(
width=250,
height=250
)
base.encode(x='Horsepower') | base.encode(x='Acceleration')
O que la selección repercuta en otro tipo de gráfico.
source = data.cars()
brush = alt.selection(type='interval')
points = alt.Chart(source).mark_point().encode(
x='Horsepower:Q',
y='Miles_per_Gallon:Q',
color=alt.condition(brush, 'Origin:N', alt.value('lightgray'))
).add_selection(
brush
)
bars = alt.Chart(source).mark_bar().encode(
y='Origin:N',
color='Origin:N',
x='count(Origin):Q'
).transform_filter(
brush
)
points & bars
Podríamos estar horas hablando de esto. Como siempre, más ejemplos en la galería de ejemplos, en particular la Sección de Gráficos Interactivos.
Interactive Scatter Plot and Linked Layered Histogram
import pandas as pd
# generate fake data
source = pd.DataFrame({'gender': ['M']*1000 + ['F']*1000,
'height':np.concatenate((np.random.normal(69, 7, 1000),
np.random.normal(64, 6, 1000))),
'weight': np.concatenate((np.random.normal(195.8, 144, 1000),
np.random.normal(167, 100, 1000))),
'age': np.concatenate((np.random.normal(45, 8, 1000),
np.random.normal(51, 6, 1000)))
})
selector = alt.selection_single(empty='all', fields=['gender'])
color_scale = alt.Scale(domain=['M', 'F'],
range=['#1FC3AA', '#8624F5'])
base = alt.Chart(source).properties(
width=250,
height=250
).add_selection(selector)
points = base.mark_point(filled=True, size=200).encode(
x=alt.X('mean(height):Q',
scale=alt.Scale(domain=[0,84])),
y=alt.Y('mean(weight):Q',
scale=alt.Scale(domain=[0,250])),
color=alt.condition(selector,
'gender:N',
alt.value('lightgray'),
scale=color_scale),
)
hists = base.mark_bar(opacity=0.5, thickness=100).encode(
x=alt.X('age',
bin=alt.Bin(step=5), # step keeps bin size the same
scale=alt.Scale(domain=[0,100])),
y=alt.Y('count()',
stack=None,
scale=alt.Scale(domain=[0,350])),
color=alt.Color('gender:N',
scale=color_scale)
).transform_filter(
selector
)
points | hists
Multi-Line Tooltip
np.random.seed(42)
source = pd.DataFrame(np.cumsum(np.random.randn(100, 3), 0).round(2),
columns=['A', 'B', 'C'], index=pd.RangeIndex(100, name='x'))
source = source.reset_index().melt('x', var_name='category', value_name='y')
# Create a selection that chooses the nearest point & selects based on x-value
nearest = alt.selection(type='single', nearest=True, on='mouseover',
fields=['x'], empty='none')
# The basic line
line = alt.Chart(source).mark_line(interpolate='basis').encode(
x='x:Q',
y='y:Q',
color='category:N'
)
# Transparent selectors across the chart. This is what tells us
# the x-value of the cursor
selectors = alt.Chart(source).mark_point().encode(
x='x:Q',
opacity=alt.value(0),
).add_selection(
nearest
)
# Draw points on the line, and highlight based on selection
points = line.mark_point().encode(
opacity=alt.condition(nearest, alt.value(1), alt.value(0))
)
# Draw text labels near the points, and highlight based on selection
text = line.mark_text(align='left', dx=5, dy=-5).encode(
text=alt.condition(nearest, 'y:Q', alt.value(' '))
)
# Draw a rule at the location of the selection
rules = alt.Chart(source).mark_rule(color='gray').encode(
x='x:Q',
).transform_filter(
nearest
)
# Put the five layers into a chart and bind the data
alt.layer(
line, selectors, points, rules, text
).properties(
width=600, height=300
)