Cómo crear hermosos gráficos de barras con Seaborn y Matplotlib (incluyendo animación)

Create beautiful bar charts with Seaborn and Matplotlib (including animation).

Tutorial de Gráficos

Un tutorial de visualización de datos para Python

Gráfico creado por el autor

Hola y bienvenido a mi primer tutorial de Matplotlib y Seaborn. Hoy, te mostraré cómo convertir un gráfico de barras predeterminado en una visualización impresionante con iconos y animación.

Házmelo saber si te gusta este tipo de contenido. Si es así, ¡puedo crear más del mismo! 🙂

Puedes encontrar el código y los datos preprocesados ​​en este repositorio: simple-bar-chart-tutorial.

Empecemos.

Paso 1: Creando un gráfico de barras predeterminado

Para este tutorial, he creado un conjunto de datos simple con el número total de estrellas a lo largo del tiempo para tres marcos populares de aprendizaje profundo de código abierto (Tensorflow, PyTorch y Keras).

Captura de pantalla del autor

Y aquí hay una función simple que crea un gráfico de barras simple para una fila en el DataFrame.

def create_bar_chart(row, color):        return sns.barplot(        y=row.index.str.capitalize().values,        x=row.values,        orient="h",        saturation=1,        color=color,        width=0.75,    )

Para crear una primera visualización, ejecuto el código anterior de esta manera.

row = df.iloc[-1]fig = plt.figure(figsize=(12, 7))ax = create_bar_chart(row, color=bar_color)plt.title("Número total de estrellas en GitHub")plt.tight_layout()plt.show()

Aquí está el gráfico de barras predeterminado que obtengo si ejecuto esta función para la última fila en el conjunto de datos con el color naranja.

Gráfico de barras creado por el autor

En los siguientes pasos, crearé funciones adicionales que podemos ejecutar junto con el código anterior para mejorar los gráficos.

Intentemos crear algo más hermoso.

Paso 2: Creando un tema

Primero, quiero crear una función que me permita probar diferentes colores y fuentes para mis gráficos.

Hay un montón de métodos tanto en Matplotlib como en Seaborn que te permitirán alterar la apariencia de tus gráficos.

Prefiero primero crear un tema usando sns.set_style(). Aquí hay un fragmento de código que uso para crear un nuevo tema rápidamente.

def set_seaborn_style(font_family, background_color, grid_color, text_color):    sns.set_style({        "axes.facecolor": background_color,        "figure.facecolor": background_color,        "grid.color": grid_color,        "axes.edgecolor": grid_color,        "axes.grid": True,        "axes.axisbelow": True,                "axes.labelcolor": text_color,        "text.color": text_color,        "font.family": font_family,        "xtick.color": text_color,        "ytick.color": text_color,        "xtick.bottom": False,        "xtick.top": False,        "ytick.left": False,        "ytick.right": False,        "axes.spines.left": False,        "axes.spines.bottom": True,        "axes.spines.right": False,        "axes.spines.top": False,    })

Hay algunas opciones más para sns.set_style(), y es posible que desees usarlo de manera diferente. Pero esta es la configuración que mejor funciona para mí.

Para inspirarme y crear una paleta de colores, a menudo visito colorhunt.co o canva.com. Si tengo colores base que me gustan, voy a coolors.co si necesito algunos más.

Aquí está la paleta de colores que creé para este tutorial (y sí, me gusta el morado).

Captura de pantalla por el autor

Mi fuente de elección es “PT Mono”, y con esa decisión, puedo ejecutar el siguiente código.

background_color = "#2F195F"grid_color = "#582FB1"bar_color = "#835ED4"text_color = "#eee"set_seaborn_style(font_family, background_color, grid_color, text_color)

Si ahora ejecuto el código original para crear un gráfico de barras, obtengo lo siguiente.

Gráfico de barras creado por el autor

Es una mejora clara, pero no suficiente. Continuemos formateando el texto del título y las etiquetas de los ejes.

Paso 3: Formato de texto

Lo primero que noto es que las etiquetas de los ejes necesitan ser más grandes. Toda la información mostrada en un gráfico debe ser fácil de ver de inmediato.

Y no me gusta la forma en que se ven los números en el eje x. En lugar de escribir 75000, quiero escribir 75K para obtener una apariencia menos intimidante.

Es por eso que creé esta función.

def format_axes(ax):    ax.tick_params("x", labelsize=20, pad=16)    ax.tick_params("y", labelsize=20, pad=8)        plt.xticks(        ticks=ax.get_xticks()[:-1],        labels=["{}K".format(int(x / 1000)) for x in ax.get_xticks()[:-1]]    )

Para el título, agrego parámetros para aumentar el tamaño de la fuente y ajustar la posición.

Aquí está el código para crear el gráfico con mis nuevas modificaciones.

row = df.iloc[-1]fig = plt.figure(figsize=(12, 7))ax = create_bar_chart(row, color=bar_color)# Nueva funciónformat_axes(ax)plt.title("Número total de estrellas en GitHub", fontsize=34, y=1.2, x=0.46)plt.tight_layout()plt.show()

Y aquí está el resultado.

Gráfico de barras creado por el autor

Está empezando a verse bastante bien, pero ahora es hora de agregar un poco de magia con iconos.

Paso 4: Agregar iconos

Agregar imágenes e iconos a los gráficos es divertido pero complicado. No siempre es fácil colocarlos en la ubicación perfecta o con el tamaño ideal.

La siguiente función agrega iconos al final de cada barra en mi gráfico utilizando xycoords="data" y los valores de mi DataFrame.

El parámetro boxstyle dentro de bboxprops crea un fondo circular blanco.

def add_bar_icons(ax, row, background_color, zoom, pad):    for index, (name, value) in enumerate(row.items()):         icon = plt.imread("./icons/{}.png".format(name.lower()))        image = OffsetImage(icon, zoom=zoom, interpolation="lanczos", resample=True, visible=True)        image.image.axes = ax        ax.add_artist(AnnotationBbox(            image, (value, index), frameon=True,            xycoords="data",            bboxprops={                "facecolor": "#fff",                "linewidth": 3,                "edgecolor": background_color,                "boxstyle": "circle, pad={}".format(pad),            }        ))

Quiero poner el icono en un círculo blanco y agregar un borde del mismo color morado oscuro que el fondo del gráfico.

Hasta ahora, no he encontrado una buena manera de manejar el parámetro zoom dinámicamente, así que lo ajusto manualmente para obtener los tamaños correctos.

Ahora mi código para crear el gráfico se ve así.

row = df.iloc[-1]fig = plt.figure(figsize=(12, 7))ax = create_bar_chart(row, color=bar_color)# Nuevas funcionesformat_axes(ax)add_bar_icons(ax, row, background_color, zoom=0.09, pad=0.9)plt.title("Número total de estrellas en GitHub", fontsize=34, y=1.2, x=0.46)plt.tight_layout()plt.show()

Y esto es lo que obtengo.

Gráfico de barras creado por el autor

Para agregar la estrella, he creado otra función que agrega un icono personalizado en cualquier lugar del gráfico usando xycoords="axes fraction".

def add_icon(ax, icon_name, x, y, zoom):    icon = plt.imread("./icons/{}.png".format(icon_name))    image = OffsetImage(icon, zoom=zoom, interpolation="lanczos", resample=True, visible=True)    image.image.axes = ax    ax.add_artist(AnnotationBbox(        image, (x, y), frameon=False,        xycoords="axes fraction",    ))

En este próximo paso, hago espacio para el icono de la estrella agregando espacios adicionales al título y ajustando los parámetros x e y para colocar el icono en la ubicación correcta.

row = df.iloc[-1]fig = plt.figure(figsize=(12, 7))ax = create_bar_chart(row, color=bar_color)# Nuevas funcionesformat_axes(ax)add_bar_icons(ax, row, background_color, zoom=0.09, pad=0.9)add_icon(ax, "star", x=0.46, y=1.26, zoom=0.13)plt.title("Número total de     estrellas en GitHub", fontsize=34, y=1.2, x=0.46)plt.tight_layout()plt.show()

Ahora nuestro gráfico de barras se ve así, y casi hemos terminado.

Gráfico de barras creado por el autor

Se ve excelente, pero ahora quiero convertir el gráfico en un formato más versátil.

Y hagamos algo al respecto de ese aspecto apretado agregando algo de relleno.

Paso 5: Convertir el gráfico en una imagen

Para esta parte, he creado una función que toma la figura y la convierte en una imagen PIL. Una imagen PIL es más fácil de trabajar para los últimos pasos de nuestro tutorial.

def create_image_from_figure(fig):    plt.tight_layout()    fig.canvas.draw()    data = np.frombuffer(fig.canvas.tostring_rgb(), dtype=np.uint8)    data = data.reshape((fig.canvas.get_width_height()[::-1]) + (3,))    plt.close()         return Image.fromarray(data)

También he creado la siguiente función para agregar relleno alrededor del gráfico.

def add_padding_to_chart(chart, left, top, right, bottom, background):    size = chart.size    image = Image.new("RGB", (size[0] + left + right, size[1] + top + bottom), background)    image.paste(chart, (left, top))    return image

Aquí hay una nueva versión del código para generar el gráfico donde he reemplazado el plt.show() estándar con nuestros nuevos métodos.

row = df.iloc[-1]fig = plt.figure(figsize=(12, 7))ax = create_bar_chart(row, color=bar_color)\plt.title("Número total de     estrellas en GitHub", fontsize=34, y=1.2, x=0.46)# Nuevas funcionesformat_axes(ax)add_bar_icons(ax, row, background_color)add_icon(ax, "star", 0.46, 1.26)image = create_image_from_figure(fig)image = add_padding_to_chart(image, 0, 10, 10, 10, background_color)

Y aquí está el resultado.

Gráfico de barras creado por el autor

¡Fantástico!

Ahora hemos convertido el gráfico de barras predeterminado en algo mucho más hermoso.

Terminemos con una sección de bonificación.

Bono: Creación de una animación

Como hemos separado el código en funciones reutilizables, es bastante fácil crear una animación.

Todo lo que tenemos que hacer es ejecutar el código varias veces con diferentes valores y unir los fotogramas.

Aquí hay un bucle for donde tomo las últimas 200 filas de las 2000 del conjunto de datos. También estoy arreglando el eje x estableciendo xlim=(0, 185000) para evitar valores parpadeantes.

images = []for i in tqdm.tqdm(range(1, 2000, 10)):    row = df.iloc[-i]    fig = plt.figure(figsize=(12, 7))    ax = create_bar_chart(row, color=bar_color)    ax.set(xlim=(0, 185000))    plt.title("Número total de estrellas en GitHub", fontsize=34, y=1.2, x=0.46)    format_axes(ax)    add_bar_icons(ax, row, background_color, zoom=0.09, pad=0.9)    add_icon(ax, "star", 0.46, 1.26)    image = create_image_from_figure(fig)    image = add_padding_to_chart(image, 20, 20, 40, 0, background_color)    images.append(image)    images.reverse()

A continuación, uso imageio para crear un GIF.

# Agregando algunas copias del último fotograma para crear un retrasoimages = images + [images[-1] for _ in range(20)]imageio.mimwrite('./animation.gif', images, duration=50)

Aquí está el resultado.

Visualización creada por el autor

Eso marca el final de este tutorial, y espero que te haya gustado. Si es así, comparte esta historia y suscríbete a mi canal.

También deberías seguirme en Twitter: @oscarle3o

Gracias por leer.

¡Nos vemos la próxima vez! 🙂