Visualización de datos

Introducción

“El simple gráfico ha aportado más información a la mente del analista de datos que cualquier otro dispositivo.” — John Tukey

Este capítulo te enseñará a visualizar tus datos usando letsplot.

En términos generales, hay dos categorías de enfoques para crear visualizaciones de datos con código: el imperativo, en el que construyes lo que quieres, y el declarativo, en el que dices lo que quieres. Elegir cuál usar implica un compromiso: las librerías imperativas te ofrecen flexibilidad a costa de cierta verbosidad; las librerías declarativas te ofrecen una forma rápida de graficar tus datos, pero solo si ya están en el formato adecuado, y personalizar tipos de gráficos especiales es más difícil. Python tiene muchos paquetes de gráficos excelentes, incluido quizás el paquete de gráficos imperativo más potente que existe, matplotlib.

Sin embargo, llegaremos más lejos y más rápido aprendiendo un solo sistema y aplicándolo en muchos lugares; y la belleza de los gráficos declarativos es que cubren muchos gráficos estándar de forma sencilla y correcta. letsplot implementa la llamada gramática de gráficos (grammar of graphics), un sistema declarativo coherente para describir y construir gráficos.

Empezaremos creando un scatter plot (gráfico de dispersión) sencillo y lo usaremos para presentar los mapeos estéticos y los objetos geométricos, los componentes fundamentales de letsplot. Después te guiaremos en la visualización de distribuciones de variables individuales, así como en la visualización de relaciones entre dos o más variables. Terminaremos con cómo guardar tus gráficos y algunos consejos para solucionar problemas.

Requisitos previos

Para este capítulo necesitarás instalar el paquete letsplot. Para hacerlo, abre la línea de comandos de tu computadora, escribe uv add lets-plot y presiona Enter.

Nota

Puedes abrir la línea de comandos dentro de Visual Studio Code y Codespaces desde View -> Terminal.

Ten en cuenta que solo necesitas instalar un paquete una vez en cada entorno de Python.

También necesitaremos tener instalado el paquete pandas; este paquete, que veremos mucho, sirve para trabajar con datos. Puedes instalarlo de forma similar ejecutando uv add pandas en la línea de comandos.

Por último, también necesitaremos algunos datos (no se puede hacer ciencia sin datos). Usaremos el dataset Palmer penguins. Excepcionalmente, también se puede instalar como paquete: normalmente cargarías los datos desde un archivo, pero estos datos son tan populares en tutoriales que han terminado en un paquete instalable. Ejecuta uv add palmerpenguins para obtenerlos.

Nuestra siguiente tarea es cargarlos en nuestra sesión de Python, ya sea en una celda de un Jupyter Notebook, escribiéndolos en un script que luego enviamos a la ventana interactiva, o escribiéndolos directamente en la ventana interactiva y presionando Shift y Enter. Este es el código:

from lets_plot import *
from palmerpenguins import load_penguins

LetsPlot.setup_html()

Estas líneas importan partes de los paquetes pandas y palmerpenguins, y luego importan todas (*) las funciones del paquete letsplot. La última línea permite que los gráficos se muestren en HTML.

Primeros pasos

¿Los pingüinos con aletas más largas pesan más o menos que los pingüinos con aletas más cortas? Probablemente ya tengas una respuesta, pero intenta que sea precisa. ¿Cómo es la relación entre la longitud de la aleta y la masa corporal? ¿Es positiva? ¿Negativa? ¿Lineal? ¿No lineal? ¿Varía la relación según la especie del pingüino? ¿Y según la isla donde vive? Creemos visualizaciones que nos permitan responder estas preguntas.

El dataframe penguins

Puedes poner a prueba tus respuestas con el dataframe penguins que se encuentra en palmerpenguins (es decir, from palmerpenguins import load_penguins). Un dataframe es una colección rectangular de variables (en las columnas) y observaciones (en las filas). penguins contiene 344 observaciones recopiladas y puestas a disposición por la Dra. Kristen Gorman y la Palmer Station, Antarctica LTER (Horst et al. 2020).

Para facilitar la discusión, definamos algunos términos:

  • Una variable es una cantidad, cualidad o propiedad que puedes medir.

  • Un valor es el estado de una variable en el momento en que la mides. El valor de una variable puede cambiar de una medición a otra.

  • Una observación es un conjunto de mediciones realizadas en condiciones similares (normalmente haces todas las mediciones de una observación al mismo tiempo y sobre el mismo objeto). Una observación contendrá varios valores, cada uno asociado a una variable distinta. A veces nos referiremos a una observación como un punto de datos.

  • Los datos tabulares son un conjunto de valores, cada uno asociado a una variable y a una observación. Los datos tabulares son tidy si cada valor está en su propia “celda”, cada variable en su propia columna y cada observación en su propia fila.

En este contexto, una variable se refiere a un atributo de todos los pingüinos, y una observación se refiere a todos los atributos de un solo pingüino.

Escribe el nombre del dataframe en la ventana interactiva y Python mostrará una vista previa de su contenido.

penguins = load_penguins()
penguins
species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g sex year
0 Adelie Torgersen 39.1 18.7 181.0 3750.0 male 2007
1 Adelie Torgersen 39.5 17.4 186.0 3800.0 female 2007
2 Adelie Torgersen 40.3 18.0 195.0 3250.0 female 2007
3 Adelie Torgersen NaN NaN NaN NaN NaN 2007
4 Adelie Torgersen 36.7 19.3 193.0 3450.0 female 2007
... ... ... ... ... ... ... ... ...
339 Chinstrap Dream 55.8 19.8 207.0 4000.0 male 2009
340 Chinstrap Dream 43.5 18.1 202.0 3400.0 female 2009
341 Chinstrap Dream 49.6 18.2 193.0 3775.0 male 2009
342 Chinstrap Dream 50.8 19.0 210.0 4100.0 male 2009
343 Chinstrap Dream 50.2 18.7 198.0 3775.0 female 2009

344 rows × 8 columns

Para una vista alternativa, en la que puedes ver las primeras observaciones de cada variable, usa penguins.head().

penguins.head()
species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g sex year
0 Adelie Torgersen 39.1 18.7 181.0 3750.0 male 2007
1 Adelie Torgersen 39.5 17.4 186.0 3800.0 female 2007
2 Adelie Torgersen 40.3 18.0 195.0 3250.0 female 2007
3 Adelie Torgersen NaN NaN NaN NaN NaN 2007
4 Adelie Torgersen 36.7 19.3 193.0 3450.0 female 2007

Entre las variables de penguins se encuentran:

  1. species: la especie del pingüino (Adelie, Chinstrap o Gentoo).

  2. flipper_length_mm: longitud de la aleta del pingüino, en milímetros.

  3. body_mass_g: masa corporal del pingüino, en gramos.

Para saber más sobre penguins, abre la página de ayuda de su función de carga de datos ejecutando help(load_penguins).

Objetivo final

Nuestro objetivo final en este capítulo es recrear la siguiente visualización, que muestra la relación entre la longitud de las aletas y la masa corporal de estos pingüinos, teniendo en cuenta su especie.

(
    ggplot(penguins, aes(x="flipper_length_mm", y="body_mass_g"))
    + geom_point(aes(color="species", shape="species"))
    + geom_smooth(method="lm")
    + labs(
        title="Masa corporal y longitud de la aleta",
        subtitle="Dimensiones de los pingüinos Adelie, Chinstrap y Gentoo",
        x="Longitud de la aleta (mm)",
        y="Masa corporal (g)",
        color="Especie",
        shape="Especie",
    )
)

Crear un gráfico

Recreemos este gráfico paso a paso.

Con letsplot, comienzas un gráfico con la función ggplot(), que define un objeto gráfico al que luego añades capas.

El primer argumento de ggplot() es el dataset que se usará en el gráfico, así que ggplot(data = penguins) crea un gráfico vacío preparado para mostrar los datos de penguins, pero como todavía no le hemos dicho cómo visualizarlos, por ahora está vacío. Al estar vacío, ejecutarlo por sí solo produciría un mensaje de error: es un lienzo en blanco sobre el que pintarás las capas restantes de tu gráfico.

ggplot(data = penguins)

A continuación, tenemos que decirle a ggplot() cómo se representará visualmente la información de nuestros datos.

El argumento mapping de la función ggplot() define cómo se asignan las variables de tu dataset a las propiedades visuales (estéticas) de tu gráfico. El argumento mapping siempre se define en la función aes(), y los argumentos x e y de aes() especifican qué variables asignar a los ejes x e y. Por ahora, solo asignaremos la longitud de la aleta a la estética x y la masa corporal a la estética y. letsplot busca las variables asignadas en el argumento data, en este caso, penguins.

De nuevo, en realidad no hemos especificado nada que graficar, así que ejecutar

ggplot(
  data = penguins,
  mapping = aes(x = "flipper_length_mm", y = "body_mass_g")
)

produciría un error. Esto se debe a que todavía no hemos expresado, en nuestro código, cómo representar las observaciones de nuestro dataframe en el gráfico.

Para ello, necesitamos definir un geom: el objeto geométrico que usa un gráfico para representar los datos. Estos objetos geométricos están disponibles en letsplot mediante funciones que empiezan por geom_.

A menudo se describen los gráficos según el tipo de geom que usan. Por ejemplo, los gráficos de barras usan geoms de barras (geom_bar()), los gráficos de líneas usan geoms de líneas (geom_line()), los boxplots usan geoms de boxplot (geom_boxplot()), los scatter plots usan geoms de puntos (geom_point()), y así sucesivamente.

La función geom_point() añade una capa de puntos a tu gráfico, lo que crea un scatter plot. letsplot incluye muchas funciones geom, cada una de las cuales añade un tipo de capa diferente a un gráfico.

(
    ggplot(data=penguins, mapping=aes(x="flipper_length_mm", y="body_mass_g"))
    + geom_point()
)

Ahora tenemos algo que se parece a lo que podríamos considerar un “scatter plot”. Todavía no coincide con nuestro gráfico “objetivo final”, pero con este gráfico podemos empezar a responder la pregunta que motivó nuestra exploración: “¿Cómo es la relación entre la longitud de la aleta y la masa corporal?” La relación parece ser positiva (a medida que aumenta la longitud de la aleta, también lo hace la masa corporal), bastante lineal (los puntos se agrupan en torno a una línea en lugar de una curva) y moderadamente fuerte (no hay demasiada dispersión alrededor de esa línea). Los pingüinos con aletas más largas suelen ser más grandes en cuanto a su masa corporal.

Es un buen momento para señalar que, aunque hemos graficado todo el dataframe penguins, había un par de filas con valores no definidos, y por supuesto estas no se pueden graficar.

Añadir estéticas y capas

Los scatter plots son útiles para mostrar la relación entre dos variables numéricas, pero siempre es buena idea ser escéptico ante cualquier relación aparente entre dos variables y preguntarse si puede haber otras variables que expliquen o cambien la naturaleza de esa relación aparente. Por ejemplo, ¿difiere la relación entre la longitud de la aleta y la masa corporal según la especie?

Incorporemos la especie a nuestro gráfico y veamos si esto revela algo más sobre la relación aparente entre estas variables. Lo haremos representando cada especie con puntos de distintos colores.

Para lograrlo, ¿tendremos que modificar la estética o el geom? Si respondiste «en el mapeo estético, dentro de aes()», ¡ya le estás tomando la mano a crear visualizaciones de datos con letsplot! Y si no, no te preocupes.

A lo largo del libro harás muchos más gráficos y tendrás muchas más oportunidades de poner a prueba tu intuición mientras los creas.

(
    ggplot(
        data=penguins,
        mapping=aes(x="flipper_length_mm", y="body_mass_g", color="species"),
    )
    + geom_point()
)

Cuando una variable categórica se asigna a una estética, letsplot asigna automáticamente un valor único de la estética (aquí, un color único) a cada nivel único de la variable (cada una de las tres especies), un proceso conocido como escalado.

letsplot también añadirá una leyenda que explica qué valores corresponden a qué niveles.

Ahora añadamos una capa más: una curva suavizada que muestre la relación entre la masa corporal y la longitud de la aleta.

Antes de continuar, vuelve a mirar el código anterior y piensa cómo podríamos añadir esto a nuestro gráfico existente.

Como se trata de un nuevo objeto geométrico que representa nuestros datos, añadiremos un nuevo geom como una capa encima de nuestro geom de puntos: geom_smooth().

Y especificaremos que queremos dibujar la línea de mejor ajuste basada en un modelo lineal (linear model) con method = "lm".

(
    ggplot(
        data=penguins,
        mapping=aes(x="flipper_length_mm", y="body_mass_g", color="species"),
    )
    + geom_point()
    + geom_smooth(method="lm")
)

Hemos añadido las líneas con éxito, pero este gráfico no se parece al gráfico anterior, ya que aquel solo tenía una línea para todo el dataset, en lugar de líneas separadas para cada especie de pingüino.

Cuando los mapeos estéticos se definen en ggplot(), a nivel global, se transmiten a cada una de las capas geom posteriores del gráfico.

Sin embargo, cada función geom de letplot también puede recibir un argumento mapping, que permite definir mapeos estéticos a nivel local que se suman a los heredados del nivel global.

Como queremos que los puntos se coloreen según la especie, pero no queremos que las líneas se separen por especie, debemos especificar color = species solo para geom_point(): por lo tanto, lo quitamos del aes() global y lo añadimos únicamente a geom_point().

(
    ggplot(data=penguins, mapping=aes(x="flipper_length_mm", y="body_mass_g"))
    + geom_point(mapping=aes(color="species"))
    + geom_smooth(method="lm")
)

¡Voilà! Tenemos algo muy parecido a nuestro objetivo final, aunque todavía no es perfecto.

Aún necesitamos usar formas diferentes para cada especie de pingüino y mejorar las etiquetas.

En general, no es buena idea representar información usando solo colores en un gráfico, ya que las personas perciben los colores de forma diferente debido al daltonismo u otras diferencias en la visión del color. Por eso, además del color, también podemos asignar species a la estética shape.

(
    ggplot(data=penguins, mapping=aes(x="flipper_length_mm", y="body_mass_g"))
    + geom_point(mapping=aes(color="species", shape="species"))
    + geom_smooth(method="lm")
)

Observa que la leyenda también se actualiza automáticamente para reflejar las distintas formas de los puntos.

Y, por último, podemos mejorar las etiquetas de nuestro gráfico usando la función labs() en una nueva capa. Algunos de los argumentos de labs() se explican por sí mismos: title añade un título y subtitle añade un subtítulo al gráfico. Otros argumentos coinciden con los mapeos estéticos: x es la etiqueta del eje x, y es la etiqueta del eje y, y color y shape definen la etiqueta de la leyenda.

(
    ggplot(data=penguins, mapping=aes(x="flipper_length_mm", y="body_mass_g"))
    + geom_point(aes(color="species", shape="species"))
    + geom_smooth(method="lm")
    + labs(
        title="Masa corporal y longitud de la aleta",
        subtitle="Dimensiones de los pingüinos Adelie, Chinstrap y Gentoo",
        x="Longitud de la aleta (mm)",
        y="Masa corporal (g)",
        color="Especie",
        shape="Especie",
    )
)

¡Por fin tenemos un gráfico que coincide perfectamente con nuestro «objetivo final»!

Ejercicios

  1. ¿Cuántas filas hay en penguins? ¿Cuántas columnas?

  2. ¿Qué describe la variable bill_depth_mm del dataframe penguins? Lee la ayuda de load_penguins() para averiguarlo; por ejemplo, ejecuta help(load_penguins).

  3. Haz un scatter plot de bill_depth_mm frente a bill_length_mm. Es decir, haz un scatter plot con bill_depth_mm en el eje y y bill_length_mm en el eje x. Describe la relación entre estas dos variables.

  4. ¿Qué ocurre si haces un scatter plot de species frente a bill_depth_mm? ¿Cuál podría ser una mejor elección de geom?

  5. ¿Por qué lo siguiente produce un error y cómo lo corregirías?

    (ggplot(data = penguins) + 
      geom_point())
  6. Añade el siguiente pie de figura al gráfico que hiciste en el ejercicio anterior: «Data come from the palmerpenguins package.». Pista: consulta la documentación de labs().

  7. Recrea la siguiente visualización. ¿A qué estética debería asignarse bill_depth_mm? ¿Y debería asignarse a nivel global o a nivel del geom?

(
    ggplot(data=penguins, mapping=aes(x="flipper_length_mm", y="body_mass_g"))
    + geom_point(aes(color="bill_depth_mm"))
    + geom_smooth()
)
  1. Ejecuta este código mentalmente y predice cómo será el resultado. Luego, ejecuta el código en Python y comprueba tus predicciones.

    
    (ggplot(
      data = penguins,
      mapping = aes(x = "flipper_length_mm", y = "body_mass_g", color = "island")
    ) +
      geom_point() +
      geom_smooth(se = False)
    )
  2. ¿Se verán diferentes estos dos gráficos? ¿Por qué sí o por qué no?

    
    (ggplot(
      data = penguins,
      mapping = aes(x = "flipper_length_mm", y = "body_mass_g")
    ) +
      geom_point() +
      geom_smooth()
    )
    (ggplot() +
      geom_point(
        data = penguins,
        mapping = aes(x = "flipper_length_mm", y = "body_mass_g")
      ) +
      geom_smooth(
        data = penguins,
        mapping = aes(x = "flipper_length_mm", y = "body_mass_g")
      )
    )

Llamadas de letsplot

A medida que avanzamos más allá de estas secciones introductorias, pasaremos a una forma más concisa de escribir el código de letsplot.

Hasta ahora hemos sido muy explícitos, lo cual es útil mientras aprendes:

(ggplot(
  data = penguins,
  mapping = aes(x = "flipper_length_mm", y = "body_mass_g")
) +
  geom_point())

Normalmente, los primeros uno o dos argumentos de una función son tan importantes que deberías sabértelos de memoria. Los dos primeros argumentos de ggplot() son data y mapping; en el resto del libro no escribiremos esos nombres: por la forma en que está escrita la función, Python sabe qué variables esperar según su posición. No escribirlos ahorra tecleo y, al reducir la cantidad de texto adicional, facilita ver qué cambia entre un gráfico y otro. Esa es una cuestión de programación realmente importante sobre la que volveremos más adelante.

Reescribir el gráfico anterior de forma más concisa da como resultado:

(
    ggplot(penguins, aes(x = "flipper_length_mm", y = "body_mass_g")) + 
  geom_point()
)

Visualizar distribuciones

La forma de visualizar la distribución de una variable depende del tipo de variable: categórica o numérica.

Una variable categórica

Una variable es categórica si solo puede tomar uno de un pequeño conjunto de valores. Para examinar la distribución de una variable categórica, puedes usar un gráfico de barras. La altura de las barras muestra cuántas observaciones hay para cada valor de x.

(ggplot(penguins, aes(x="species")) + geom_bar())

Quizá hayas visto antes que el tipo de dato de la columna "species" es string. Lo ideal es que sea categórica, para que no haya confusión sobre el hecho de que aquí trabajamos con un número finito de grupos mutuamente excluyentes. Otra ventaja es que permite a las herramientas de gráficos reconocer con qué tipo de datos están trabajando.

Podemos transformar la variable en una variable categórica usando pandas así:

penguins["species"] = penguins["species"].astype("category")
penguins.head()
species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g sex year
0 Adelie Torgersen 39.1 18.7 181.0 3750.0 male 2007
1 Adelie Torgersen 39.5 17.4 186.0 3800.0 female 2007
2 Adelie Torgersen 40.3 18.0 195.0 3250.0 female 2007
3 Adelie Torgersen NaN NaN NaN NaN NaN 2007
4 Adelie Torgersen 36.7 19.3 193.0 3450.0 female 2007

Aprenderás más sobre las variables categóricas más adelante en el libro.

Una variable numérica

Una variable es numérica (o cuantitativa) si puede tomar una amplia gama de valores numéricos y tiene sentido sumarlos, restarlos o calcular promedios con ellos. Las variables numéricas pueden ser continuas o discretas.

Una visualización muy utilizada para las distribuciones de variables continuas es el histograma.

(ggplot(penguins, aes(x="body_mass_g")) + geom_histogram(binwidth=200))

Un histograma divide el eje x en intervalos (bins) de igual ancho y luego usa la altura de una barra para mostrar el número de observaciones que caen en cada intervalo. En el gráfico anterior, la barra más alta muestra que 39 observaciones tienen un valor de body_mass_g entre 3.500 y 3.700 gramos, que son los bordes izquierdo y derecho de la barra.

Puedes fijar el ancho de los intervalos de un histograma con el argumento binwidth, que se mide en las unidades de la variable x. Siempre deberías explorar varios anchos de intervalo cuando trabajes con histogramas, ya que distintos anchos pueden revelar patrones diferentes. En los gráficos siguientes, un ancho de 20 es demasiado estrecho y produce demasiadas barras, lo que dificulta determinar la forma de la distribución. De forma similar, un ancho de 2.000 es demasiado grande y hace que todos los datos queden agrupados en solo tres barras, lo que también dificulta determinar la forma de la distribución. Un ancho de 200 ofrece un equilibrio razonable, pero siempre deberías mirar tus datos de varias maneras, especialmente con los histogramas, ya que pueden ser engañosos.

Una alternativa para visualizar distribuciones de variables numéricas es el gráfico de densidad. Un gráfico de densidad es una versión suavizada de un histograma y una alternativa práctica, sobre todo para datos continuos que provienen de una distribución subyacente suave. No entraremos en cómo geom_density() estima la densidad (puedes leer más sobre ello en la documentación de la función), pero expliquemos con una analogía cómo se dibuja la curva de densidad. Imagina un histograma hecho de bloques de madera. Luego, imagina que dejas caer encima un espagueti cocido. La forma que toma el espagueti al posarse sobre los bloques puede considerarse la forma de la curva de densidad. Muestra menos detalles que un histograma, pero puede facilitar captar rápidamente la forma de la distribución, sobre todo en cuanto a modas y asimetría.

(ggplot(penguins, aes(x="body_mass_g")) + geom_density())

Ejercicios

  1. Haz un gráfico de barras de "species" de penguins, asignando "species" a la estética y. ¿En qué se diferencia este gráfico?

  2. ¿En qué se diferencian los dos gráficos siguientes? ¿Qué estética, color o fill, es más útil para cambiar el color de las barras?

    
    (ggplot(penguins, aes(x = species)) +
      geom_bar(color = "red"))
    
    (ggplot(penguins, aes(x = species)) +
      geom_bar(fill = "red"))
  3. ¿Qué hace el argumento bins de geom_histogram()?

Visualizar relaciones

Para visualizar una relación necesitamos tener al menos dos variables asignadas a estéticas de un gráfico; aunque debes recordar que correlación no implica causalidad, ¡y causalidad no implica correlación!

En las siguientes secciones aprenderás sobre gráficos muy utilizados para visualizar relaciones entre dos o más variables y los geoms que se usan para crearlos.

Una variable numérica y una categórica

Para visualizar la relación entre una variable numérica y una categórica podemos usar boxplots uno al lado del otro.

Un boxplot (diagrama de caja) es un tipo de resumen visual de medidas de posición dentro de una distribución (percentiles).

También es útil para identificar posibles valores atípicos. Cada boxplot consta de:

  • Una caja que indica el rango de la mitad central de los datos, una distancia conocida como rango intercuartílico (IQR), que va desde el percentil 25 de la distribución hasta el percentil 75. En el centro de la caja hay una línea que muestra la mediana, es decir, el percentil 50, de la distribución. Estas tres líneas te dan una idea de la dispersión de la distribución y de si la distribución es simétrica respecto a la mediana o está sesgada hacia un lado.

  • Puntos visuales que muestran las observaciones que se encuentran a más de 1,5 veces el IQR de cualquiera de los bordes de la caja. Estos puntos atípicos son inusuales, por lo que se grafican individualmente.

  • Una línea (o bigote) que se extiende desde cada extremo de la caja y llega hasta el punto no atípico más alejado de la distribución.

Veamos la distribución de la masa corporal por especie usando geom_boxplot():

(ggplot(penguins, aes(x="species", y="body_mass_g")) + geom_boxplot())

Como alternativa, podemos crear gráficos de densidad de probabilidad con geom_density().

(ggplot(penguins, aes(x="body_mass_g", color="species")) + geom_density(size=2))

También hemos personalizado el grosor de las líneas usando el argumento size para que destaquen un poco más sobre el fondo.

Además, podemos mapear species a las estéticas color y fill y usar la estética alpha para añadir transparencia a las curvas de densidad rellenas. Esta estética toma valores entre 0 (completamente transparente) y 1 (completamente opaco). En el siguiente gráfico está fijada en 0.5.

(
    ggplot(penguins, aes(x="body_mass_g", color="species", fill="species"))
    + geom_density(alpha=0.5)
)

Fíjate en la terminología que hemos usado aquí:

  • Mapeamos variables a estéticas si queremos que el atributo visual representado por esa estética varíe según los valores de esa variable.
  • En caso contrario, fijamos el valor de una estética.

Dos variables categóricas

Podemos usar gráficos de barras apiladas para visualizar la relación entre dos variables categóricas.

Por ejemplo, los dos gráficos de barras apiladas siguientes muestran la relación entre island y species o, concretamente, visualizan la distribución de species dentro de cada isla.

El primer gráfico muestra las frecuencias de cada especie de pingüinos en cada isla. El gráfico de frecuencias muestra que hay el mismo número de Adelie en cada isla.

Pero no tenemos una buena idea del equilibrio porcentual dentro de cada isla.

(ggplot(penguins, aes(x="island", fill="species")) + geom_bar())

El segundo gráfico es un gráfico de frecuencias relativas, creado al establecer position = "fill" en el geom, y es más útil para comparar la distribución de especies entre islas, ya que no se ve afectado por el número desigual de pingüinos en las islas.

Con este gráfico podemos ver que todos los pingüinos Gentoo viven en la isla Biscoe y constituyen aproximadamente el 75% de los pingüinos de esa isla, que todos los Chinstrap viven en la isla Dream y constituyen aproximadamente el 50% de los pingüinos de esa isla, y que los Adelie viven en las tres islas y constituyen la totalidad de los pingüinos de Torgersen.

(ggplot(penguins, aes(x="island", fill="species")) + geom_bar(position="fill"))

Al crear estos gráficos de barras, mapeamos la variable que se separará en barras a la estética x, y la variable que cambiará los colores dentro de las barras a la estética fill.

Dos variables numéricas

Hasta ahora has aprendido sobre los scatter plots (creados con geom_point()) y las curvas suavizadas (creadas con geom_smooth()) para visualizar la relación entre dos variables numéricas. Un scatter plot es probablemente el gráfico más usado para visualizar la relación entre dos variables numéricas.

(ggplot(penguins, aes(x="flipper_length_mm", y="body_mass_g")) + geom_point())

Tres o más variables

Como ya vimos, podemos incorporar más variables a un gráfico mapeándolas a estéticas adicionales.

Por ejemplo, en el siguiente scatter plot los colores de los puntos representan las especies y las formas de los puntos representan las islas.

(
    ggplot(penguins, aes(x="flipper_length_mm", y="body_mass_g"))
    + geom_point(aes(color="species", shape="island"))
)

Sin embargo, añadir demasiados mapeos estéticos a un gráfico lo vuelve recargado y difícil de interpretar.

Otra forma, especialmente útil para variables categóricas, es dividir el gráfico en facetas (también conocidas como pequeños múltiplos), subgráficos que muestran cada uno un subconjunto de los datos.

Para dividir tu gráfico en facetas según una sola variable, usa facet_wrap().

El primer argumento de facet_wrap() le indica a la función qué variable usar en los gráficos sucesivos. La variable que pases a facet_wrap() debe ser categórica.

(
    ggplot(penguins, aes(x="flipper_length_mm", y="body_mass_g"))
    + geom_point(aes(color="species", shape="species"))
    + facet_wrap(facets="island")
)

En capítulos posteriores aprenderás sobre muchos otros geoms para visualizar distribuciones de variables y relaciones entre ellas.

Ejercicios

  1. Crea un scatter plot de bill_depth_mm frente a bill_length_mm y colorea los puntos según species. ¿Qué revela colorear por especie sobre la relación entre estas dos variables? ¿Y dividir en facetas por species?

  2. ¿Por qué lo siguiente produce dos leyendas separadas? ¿Cómo lo corregirías para combinar las dos leyendas?

    (
        ggplot(
          data = penguins,
          mapping = aes(
            x = "bill_length_mm", y = "bill_depth_mm", 
            color = "species", shape = "species"
          )
        ) +
          geom_point() +
          labs(color = "Species")
    )
  3. Crea los dos gráficos de barras apiladas siguientes. ¿Qué pregunta puedes responder con el primero? ¿Qué pregunta puedes responder con el segundo?

    ggplot(penguins, aes(x = "island", fill = "species")) +
      geom_bar(position = "fill")
    ggplot(penguins, aes(x = "species", fill = "island")) +
      geom_bar(position = "fill")

Guardar tus gráficos

Una vez que hayas creado un gráfico, quizá quieras guardarlo como una imagen que puedas usar en otro lugar. Esa es la tarea de ggsave(), que guardará en disco el gráfico creado más recientemente:

plotted_data = (
    ggplot(penguins, aes(x="flipper_length_mm", y="body_mass_g")) + geom_point()
)
ggsave(plotted_data, filename="penguin-plot.svg")
'/Users/jromero/personal/python4DS/es/lets-plot-images/penguin-plot.svg'

Esto guardó la figura en disco en la ubicación mostrada; por defecto, está en un subdirectorio llamado “lets-plot-images”.

Usamos el formato de archivo “svg”. Hay muchas opciones de salida entre las que elegir para guardar tu archivo. Recuerda que, para gráficos, los formatos vectoriales suelen ser mejores que los formatos rasterizados. En la práctica, esto significa guardar los gráficos en formato svg o pdf en lugar de jpg o png. El formato svg funciona en muchos contextos (incluido Microsoft Word) y es una buena opción por defecto. Para elegir entre formatos, solo tienes que indicar la extensión del archivo y el tipo de archivo cambiará automáticamente, por ejemplo, “chart.svg” para svg o “chart.png” para png. También puedes guardar figuras en formato HTML.

Si usas un formato rasterizado, tendrás que especificar el tamaño de la figura mediante el argumento de palabra clave scale.

Ejercicios

  1. Guarda la figura anterior como PNG. Prueba a variar la escala.

Problemas comunes

Cuando empieces a ejecutar código, es probable que te encuentres con problemas. No te preocupes: le pasa a todo el mundo. Todos llevamos años escribiendo código en Python, ¡pero cada día seguimos escribiendo código que no funciona al primer intento!

Empieza comparando cuidadosamente el código que estás ejecutando con el código del libro: ¡un carácter fuera de lugar puede marcar la diferencia! Asegúrate de que cada ( tenga su ) y de que cada " esté emparejada con otra ". En Visual Studio Code, puedes instalar extensiones que colorean los paréntesis emparejados para que veas fácilmente si los has cerrado o no.

A veces ejecutarás el código y no pasará nada.

Si vienes del lenguaje de programación estadística R, quizá te preocupe poner el + en el lugar equivocado. Pero no temas, ya que en la sintaxis de letsplot el + puede ir al principio o al final de la línea.

Si sigues atascado, prueba la ayuda. Puedes obtener ayuda sobre cualquier función de Python ejecutando help(function_name) en la ventana interactiva. No te preocupes si la ayuda no parece muy útil; en su lugar, baja hasta los ejemplos y busca código que coincida con lo que intentas hacer.

Si sigues atascado, consulta la documentación de letsplot o haz una búsqueda en Google (especialmente útil para los mensajes de error).

Resumen

En este capítulo has aprendido los fundamentos de la visualización de datos con letsplot. Empezamos con la idea básica que sustenta letsplot: una visualización es un mapeo de las variables de tus datos a propiedades estéticas como la posición, el color, el tamaño y la forma. Después aprendiste a aumentar la complejidad y mejorar la presentación de tus gráficos capa por capa. También aprendiste sobre los gráficos más usados para visualizar la distribución de una sola variable, así como para visualizar relaciones entre dos o más variables, aprovechando mapeos estéticos adicionales y/o dividiendo tu gráfico en pequeños múltiplos mediante facetas.

Usaremos visualizaciones una y otra vez a lo largo de este libro, introduciendo nuevas técnicas a medida que las necesitemos, y profundizaremos en la creación de visualizaciones con letsplot en capítulos posteriores.

Ahora que dominas los fundamentos de la visualización, en el próximo capítulo cambiaremos un poco de tema y te daremos algunos consejos prácticos sobre flujo de trabajo. Intercalamos consejos sobre flujo de trabajo con herramientas de ciencia de datos a lo largo de esta parte del libro porque te ayudará a mantenerte organizado a medida que escribas más código en Python.