Curso

Crear gráficos sofisticados y fáciles de entender es clave para triunfar como científico de datos. Las visualizaciones te ayudan a contar la historia que hay detrás de tus datos, ponen de relieve relaciones y revelan patrones que el ojo humano capta mejor que una lista de números. ¿Y qué es aún mejor para el análisis de datos que las visualizaciones estáticas? ¡Las visualizaciones interactivas!
Por desgracia, cuando estás empezando puede parecer una tarea abrumadora. Python y R ofrecen un amplio abanico de herramientas y trucos que te facilitan el trabajo. En este tutorial te presentamos Altair. Con Altair podrás crear visualizaciones útiles, elegantes y efectivas con muy pocas líneas de código y en muy poco tiempo. ¡Vamos allá!
¿Qué es Altair?
Altair es una biblioteca de Python pensada para la visualización estadística. Es de naturaleza declarativa (volveremos a esta idea más adelante). Se basa en Vega y Vega-Lite, gramáticas de visualización que te permiten describir el aspecto visual y el comportamiento interactivo de un gráfico en formato JSON.
APIs imperativas vs. declarativas
Como decíamos, Python ya cuenta con grandes herramientas y bibliotecas para visualizar datos, muchas de ellas con soporte interactivo. Entonces, ¿qué hace que Altair destaque?
La respuesta está en su enfoque declarativo frente a otras APIs imperativas.
Con APIs imperativas, tienes que centrarte en «cómo hacerlo» en lugar de en «qué quieres hacer». Piensas más en el código necesario para construir la visualización y especificas manualmente pasos del trazado: límites de ejes, tamaño, rotación, leyendas, etc.
Como científico de datos, Altair te permite dedicar tu tiempo y esfuerzo a lo importante: entender, analizar y visualizar tus datos, y no tanto al código para lograrlo. Definirás los datos y el resultado que esperas ver (cómo debe quedar el gráfico) y Altair hará por ti las transformaciones necesarias.
Instalación
En este tutorial usaremos uno de los conjuntos de datos de Vega datasets. Para instalar Altair junto con los Vega datasets, escribe lo siguiente en tu consola:
$ pip install altair vega_datasets
Si utilizas el gestor de paquetes conda, el comando equivalente es:
$ conda install -c conda-forge altair vega_datasets
Después, ve a tu Jupyter Notebook e importa Altair para empezar. Si quieres saber más sobre Jupyter Notebook, no dejes de consultar la guía definitiva de Jupyter Notebook de DataCamp.
Datos
Ahora que hemos instalado Altair y los Vega datasets, es momento de cargar algunos datos. Empieza con estas importaciones:
import pandas as pd
import altair as alt
¿Por qué importar pandas?
Porque Altair se basa en DataFrame de pandas, lo que significa que puedes manipular los datos igual que lo harías con un DataFrame de pandas.
Aunque internamente Altair almacena los datos en formato DataFrame de pandas, hay varias formas de introducirlos:
- como un DataFrame de pandas
- como un objeto Data o relacionado (por ejemplo, UrlData, InlineData, NamedData)
- como una URL que apunte a un archivo de texto en formato JSON o CSV
- como un objeto que implemente geo_interface (p. ej., GeoDataFrame de Geopandas, geometrías de Shapely, objetos GeoJSON)
Conjunto de datos de películas
En este tutorial trabajaremos con el Movies Dataset de Vega. Este y otros conjuntos de datos de películas son muy populares en la comunidad de ciencia de datos, especialmente para sistemas de recomendación. Aquí nos limitaremos a analizarlo con visualizaciones en Altair.
El dataset incluye información como el título de la película, la recaudación en Estados Unidos y a nivel mundial, el presupuesto de producción, el género, y valoraciones de IMDB y Rotten Tomatoes. No usaremos todas las columnas; nos bastará con unas pocas para entender cómo funciona Altair como herramienta de visualización.
Vamos a por el dataset y hagamos un análisis sencillo para entenderlo mejor.
# Importing the Vega Dataset
from vega_datasets import data as vega_data
movies_df = pd.read_json(vega_data.movies.url)
# Checking the type of data that we get
print("movies_df is of the type: ", type(movies_df))
print("movies_df: ", movies_df.shape)
movies_df is of the type: <class 'pandas.core.frame.DataFrame'>
movies_df: (3201, 16)
Como ves, movies_df es un DataFrame de pandas y contiene 3201 películas (filas) con 16 características (columnas). Veamos qué hay dentro del dataframe.
movies_df.head(5)
| Title | US_Gross | Worldwide_Gross | US_DVD_Sales | Production_Budget | Release_Date | MPAA_Rating | Running_Time_min | Distributor | Source | Major_Genre | Creative_Type | Director | Rotten_Tomatoes_Rating | IMDB_Rating | IMDB_Votes | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | The Land Girls | 146083.0 | 146083.0 | NaN | 8000000.0 | Jun 12 1998 | R | NaN | Gramercy | None | None | None | None | NaN | 6.1 | 1071.0 |
| 1 | First Love, Last Rites | 10876.0 | 10876.0 | NaN | 300000.0 | Aug 07 1998 | R | NaN | Strand | None | Drama | None | None | NaN | 6.9 | 207.0 |
| 2 | I Married a Strange Person | 203134.0 | 203134.0 | NaN | 250000.0 | Aug 28 1998 | None | NaN | Lionsgate | None | Comedy | None | None | NaN | 6.8 | 865.0 |
| 3 | Let's Talk About Sex | 373615.0 | 373615.0 | NaN | 300000.0 | Sep 11 1998 | None | NaN | Fine Line | None | Comedy | None | None | 13.0 | NaN | NaN |
| 4 | Slam | 1009819.0 | 1087521.0 | NaN | 1000000.0 | Oct 09 1998 | R | NaN | Trimark | Original Screenplay | Drama | Contemporary Fiction | None | 62.0 | 3.4 | 165.0 |
Los nombres de columna son:
movies_df.columns
Index(['Title', 'US_Gross', 'Worldwide_Gross', 'US_DVD_Sales',
'Production_Budget', 'Release_Date', 'MPAA_Rating', 'Running_Time_min',
'Distributor', 'Source', 'Major_Genre', 'Creative_Type', 'Director',
'Rotten_Tomatoes_Rating', 'IMDB_Rating', 'IMDB_Votes'],
dtype='object')
Ahora usaremos la función to_datetime de pandas para convertir la columna Release_Date a una fecha real y extraer el año. Después utilizaremos esta nueva columna en nuestras visualizaciones.
def extract_year(value):
return pd.to_datetime(value, format='%b %d %Y').year
movies_df["Year"] = movies_df["Release_Date"].apply(extract_year)
Si vuelves a consultar las columnas, verás una nueva llamada Year que contiene el año correspondiente a cada película.
movies_df.columns
Index(['Title', 'US_Gross', 'Worldwide_Gross', 'US_DVD_Sales',
'Production_Budget', 'Release_Date', 'MPAA_Rating', 'Running_Time_min',
'Distributor', 'Source', 'Major_Genre', 'Creative_Type', 'Director',
'Rotten_Tomatoes_Rating', 'IMDB_Rating', 'IMDB_Votes', 'Year'],
dtype='object')
Pasemos a crear algunos gráficos. Antes, ejecuta este fragmento de código:
alt.renderers.enable('notebook')
RendererRegistry.enable('notebook')
Con esto Altair se renderiza inline en Jupyter Notebook, así podrás ver las figuras que generes. Si te surge algún problema, consulta la página de resolución de problemas de Altair.
Para un primer vistazo sería interesante ver la relación entre lo invertido en la película (Production_Budget) y lo que recaudó realmente (Worldwide_Gross). Representaremos estos valores en los ejes X e Y.
Sin embargo, el dataset es relativamente grande, y aquí queremos centrarnos en jugar con Altair. De momento trabajaremos con unos pocos datos. Veamos cuántas películas hay por año y elijamos uno en el que centrarnos.
movies_df["Year"].value_counts()
2006 220
2005 210
2002 208
2004 192
2000 188
...
1943 1
2015 1
2027 1
2037 1
1928 1
Name: Year, Length: 91, dtype: int64
Centremos el análisis en el año 2000 y creemos un nuevo dataframe movies_2000 con solo esos datos. Debería tener 188 filas (películas).
movies_2000 = movies_df[movies_df["Year"] == 2000]
movies_2000.shape
(188, 17)
Ahora vamos a por nuestra primera visualización. Profundicemos un poco en el formato de Altair para hacerlo.
Charts
Los charts son un objeto fundamental en Altair y aceptan un único argumento: un dataframe. Se definen así:
alt.Chart(DataFrame)
Por sí solo, el chart no hace mucho más que especificar los datos con los que trabajar; después tienes que indicar qué quieres hacer con ellos. Ahí entran en juego los marks.
Marks
La propiedad mark define cómo se representan los datos en el gráfico. Estas son las propiedades de mark básicas que ofrece Altair:

Para nuestro mark queremos que cada valor del DataFrame se muestre como un punto (círculo) en el gráfico. Por tanto, escribimos:
alt.Chart(DataFrame).mark_point()
Encodings
Una vez definidos los datos y su representación, toca indicar dónde y con qué atributos mostrarlos: posiciones, tamaño, color, etc. Aquí usamos los encodings:
alt.Chart(DataFrame).mark_point().encode()
Juntando todo y añadiendo detalles, creamos nuestra primera visualización:
alt.Chart(movies_2000).mark_point().encode(
alt.X('Production_Budget'),
alt.Y('Worldwide_Gross')
)
<vega.vegalite.VegaLite at 0x11709a1d0>

¿A que está bien? Con muy poco hemos montado un gráfico que muestra lo invertido frente a lo recaudado por las películas del año 2000 en nuestro dataset.
Hagámoslo más interesante. En lugar de que todos los puntos tengan el mismo tamaño, ajustemos su tamaño según su US_Gross. Para ello introducimos la codificación Size. Cuanto mayor el tamaño del círculo, mayor la recaudación en taquilla en EE. UU. También vamos a rellenar los círculos, no solo el contorno; para eso añadimos un argumento a mark_point().
alt.Chart(movies_2000).mark_point(filled=True).encode(
alt.X('Production_Budget'),
alt.Y('Worldwide_Gross'),
alt.Size('US_Gross')
)
<vega.vegalite.VegaLite at 0x117cce790>

Ya tenemos un gráfico que, en apenas cuatro líneas, representa los presupuestos de cada película. ¿Qué falta? ¡COLOR!
Pero no añadiremos un color cualquiera. Mantendremos el gráfico interesante introduciendo otra variable del dataset, Major_Genre. Daremos a cada género su color con la codificación Color y añadiremos algo de transparencia con OpacityValue.
alt.Chart(movies_2000).mark_point(filled=True).encode(
alt.X('Production_Budget'),
alt.Y('Worldwide_Gross'),
alt.Size('US_Gross'),
alt.Color('Major_Genre'),
alt.OpacityValue(0.7)
)
<vega.vegalite.VegaLite at 0x118e3ff50>

Va tomando forma, pero podemos mejorarlo. Estaría bien movernos por el gráfico y ver detalles de la película a la que corresponde cada punto. Para eso está Tooltip…
alt.Chart(movies_2000).mark_point(filled=True).encode(
alt.X('Production_Budget'),
alt.Y('Worldwide_Gross'),
alt.Size('US_Gross'),
alt.Color('Major_Genre'),
alt.OpacityValue(0.7),
tooltip = [alt.Tooltip('Title'),
alt.Tooltip('Production_Budget'),
alt.Tooltip('Worldwide_Gross'),
alt.Tooltip('US_Gross')
]
)
<vega.vegalite.VegaLite at 0x118e3ff90>

Chulo, ¿verdad? Pero sigamos. ¿Recuerdas por qué queríamos usar Altair? Para crear visualizaciones interactivas. El tooltip ya aporta interacción al pasar el ratón por los puntos, pero vamos a añadir la función interactive() a lo que tenemos…
alt.Chart(movies_2000).mark_point(filled=True).encode(
alt.X('Production_Budget'),
alt.Y('Worldwide_Gross'),
alt.Size('US_Gross'),
alt.Color('Major_Genre'),
alt.OpacityValue(0.7),
tooltip = [alt.Tooltip('Title'),
alt.Tooltip('Production_Budget'),
alt.Tooltip('Worldwide_Gross'),
alt.Tooltip('US_Gross')
]
).interactive()
<vega.vegalite.VegaLite at 0x118e3c750>

Con ese pequeño añadido ya puedes hacer zoom en un área concreta y ajustar los ejes automáticamente para centrarte en lo que te interesa. Pero hay muchas más opciones interactivas avanzadas que puedes incorporar. Veamos una…
Ya tenemos un gráfico muy potente para mostrar nuestros datos. Ahora, incluyamos todos los puntos del dataset. Eso sí, no queremos saturar la visualización y además queremos separarlos por el Year que creamos antes. Para ello usaremos una propiedad avanzada de selection.
select_year = alt.selection_single(
name='Select', fields=['Year'], init={'Year': 1928},
bind=alt.binding_range(min=1928, max=2046, step=10)
)
alt.Chart(movies_df).mark_point(filled=True).encode(
alt.X('Production_Budget'),
alt.Y('Worldwide_Gross'),
alt.Size('US_Gross'),
alt.Color('Major_Genre'),
alt.OpacityValue(0.7),
tooltip = [alt.Tooltip('Title:N'),
alt.Tooltip('Production_Budget:Q'),
alt.Tooltip('Worldwide_Gross:Q'),
alt.Tooltip('US_Gross:Q')
]
).add_selection(select_year).transform_filter(select_year)
<vega.vegalite.VegaLite at 0x117cd4cd0>

Usamos add_selection para añadir select_year como selección única al gráfico y, después, aplicamos transform_filter() para filtrar los datos según esa selección.
¿Has notado otra novedad en el código? Las letras «Q» y «N» en el Tooltip, que especifican el tipo de dato: N = nominal (categórico), Q = cuantitativo (numérico), O = ordinal (ordenado) o T = temporal (puntos o intervalos de tiempo).
Comprueba el año más antiguo y el más reciente del dataset.
print("The oldest year is: ", movies_df["Year"].min())
print("The latest year is: ", movies_df["Year"].max())
The oldest year is: 1928
The latest year is: 2046
Como ves, al mover la barra "Select_Year" apenas hay películas entre 1928 y 1967. Y no hay datos de películas después de 2018 y mucho menos hasta 2046. Podemos usar binding_range() para acotar el rango de años y el paso.
En un proyecto real, hallazgos así son importantes y pueden señalar errores en la recogida de datos o en algún punto del proceso. Tendrás que gestionarlos en la fase de preprocesado. Consulta Data Preparation with pandas para una introducción a estos pasos clave.
Ajustemos los años y, con esto, tendremos nuestra visualización final lista…
select_year = alt.selection_single(
name='Select', fields=['Year'], init={'Year': 1968},
bind=alt.binding_range(min=1968, max=2008, step=10)
)
alt.Chart(movies_df).mark_point(filled=True).encode(
alt.X('Production_Budget'),
alt.Y('Worldwide_Gross'),
alt.Size('US_Gross'),
alt.Color('Major_Genre'),
alt.OpacityValue(0.7),
tooltip = [alt.Tooltip('Title:N'),
alt.Tooltip('Production_Budget:Q'),
alt.Tooltip('Worldwide_Gross:Q'),
alt.Tooltip('US_Gross:Q')
]
).add_selection(select_year).transform_filter(select_year)
<vega.vegalite.VegaLite at 0x1180cda10>

En este tutorial has conocido Altair. Has visto cómo, con pocas líneas, puedes crear gráficos bonitos e interactivos que te ayudan a contar mejor la historia de tus datos. Esperamos que te haya mostrado el potencial de Altair. Para saber más, visita su sitio web oficial.

