Qué gráfico para qué pregunta
En resumen: El tipo de gráfico se elige según la pregunta: barras ordenadas para una comparación, una línea para el cambio en el tiempo y otra vez barras, no un gráfico circular, para la parte de un todo.
Problema
Cuatro preguntas, un mismo conjunto de datos
El gerente tiene cuatro preguntas sobre el año:
- ¿Qué productos aportan más dinero?
- ¿Cómo van los ingresos día a día?
- ¿Qué parte de los ingresos es cada categoría?
- ¿Qué es un pedido habitual?
Los datos son los mismos: los pedidos y sus líneas. El gráfico, sin embargo, es distinto para cada pregunta. No hay un gráfico que las responda todas. Hay un gráfico que responde bien a una pregunta, y muchos que la responden mal.
La idea
Primero la pregunta, después el gráfico
No empieces por «quiero un gráfico circular» o «quiero una línea». Empieza por la pregunta y mira de qué tipo es:
- comparación («cuál es mayor»): barras ordenadas por valor; con nombres largos, horizontales (
ax.barh); - cambio en el tiempo («cómo va»): una línea;
- parte de un todo («qué parte»): otra vez barras ordenadas, en porcentaje; un gráfico circular es más difícil de leer;
- distribución («qué es habitual, cuánto se dispersa»): un histograma, en la próxima lección;
- relación («crecen juntos»): un diagrama de dispersión, también en la próxima lección.
Veamos las tres primeras con los mismos datos.
La idea
Comparación: barras horizontales ordenadas
«¿Qué productos aportan más?» es una comparación de 25 productos. Sus nombres son largos, así que las barras son horizontales: ax.barh(names, values). Los nombres quedan a la izquierda y se pueden leer sin girarlos.
Ordenar con sort_values() convierte el gráfico en una clasificación. barh dibuja la primera fila abajo, así que el orden ascendente pone el mayor arriba. figsize=(7, 7) da a la figure un tamaño en pulgadas: 25 barras necesitan altura.
El queso blanco aporta más: 10 325,30 €. Después vienen el café en grano (9585,10 €) y el queso kashkaval (8616,50 €). Ordenadas, las barras responden a la pregunta antes de que leas los números.
Más precisamente, para curiosos
Sin sort_values() los productos salen de groupby en orden alfabético. El orden alfabético es cómodo cuando buscas un producto por su nombre. Para la pregunta «cuál es el mayor» necesitas el orden por valor.
Pruébalo
Los más vendidos en unidades
¿Qué 10 productos vendieron más unidades en el año? Guárdalos en top (el producto es el índice, las unidades son el valor) y dibújalos como barras horizontales con el mayor arriba.
Solución
import pandas as pd
import matplotlib.pyplot as plt
orders = pd.read_csv('year_orders.csv')
items = pd.read_csv('year_items.csv')
products = pd.read_csv('products.csv')
# Los ingresos no incluyen los pedidos cancelados.
sold = orders[orders['status'] != 'cancelled']
lines = items.merge(sold, left_on='order_id', right_on='id')
lines['revenue'] = lines['quantity'] * lines['unit_price']
lines['month'] = lines['ordered_at'].str[:7]
lines['product'] = lines['product_id'].map(products.set_index('id')['name'])
top = lines.groupby('product')['quantity'].sum().nlargest(10).sort_values()
fig, ax = plt.subplots()
ax.barh(top.index, top.values)
ax.set_title('Los productos más vendidos')
ax.set_xlabel('Unidades en el año')
fig.tight_layout()lines.groupby('product')['quantity'].sum().nlargest(10).sort_values() da los diez. Arriba está el pan blanco con 4223 unidades, después la leche fresca (3882) y el agua mineral (3707). El queso blanco, que aporta más dinero, es solo décimo en unidades (1307). «Más vendido» depende de si cuentas unidades o euros. nlargest pone el mayor primero, y sin sort_values() acaba abajo.
La idea
Cambio en el tiempo: una línea
«¿Cómo van los ingresos día a día?» es un cambio en el tiempo. Una línea une cada día con el siguiente, y el ojo sigue la dirección. 365 barras serían una valla en la que no se ve nada.
Los ingresos diarios saltan por el ritmo semanal. Una segunda línea con la media de 7 días (rolling(7) de la lección sobre fechas) muestra la dirección bajo el ruido:
Son 365 días. El más fuerte es el 26 de diciembre de 2025 con 852,90 €. La línea fina hace zigzag cada semana, y la gruesa muestra el gran pico de diciembre y las bajadas de enero y septiembre. Abajo hay fechas: ordered_at se ha convertido en fechas con pd.to_datetime, y matplotlib pone los meses por sí mismo.
La idea
Gráficos pequeños uno al lado del otro
En la lección anterior las siete categorías se enredaron en unos mismos axes. Otra manera es poner cada categoría en su propio gráfico pequeño y colocar los gráficos uno al lado del otro. Se llaman small multiples (múltiplos pequeños).
plt.subplots(2, 3) da una figure con 2 filas y 3 columnas de axes. axes es un array de NumPy de forma (2, 3), y axes.flat los va entregando uno a uno. sharey=True les da a todos la misma escala vertical. fig.suptitle es un título para toda la figure. Abajo solo hay el número del mes (pv.index.str[5:]), para que quepa:
Con una escala compartida se pueden comparar las alturas: los lácteos son lo más alto todo el año, y la cocina es baja con un pico en diciembre. Cada línea se lee por sí sola, sin leyenda y sin colores para distinguirlas.
La idea
Parte de un todo: barras frente a un gráfico circular
«¿Qué parte de los ingresos es cada categoría?» es una pregunta sobre proporciones. A menudo se dibuja como gráfico circular. Aquí están el círculo y las barras ordenadas con los mismos números, uno al lado del otro. plt.subplots(1, 2) da dos axes: aquí se llaman left y right:
El círculo muestra que los lácteos son el sector más grande, un poco más de un cuarto. La limpieza (9,8 %) y la cocina (10,2 %), sin embargo, parecen iguales. El pan (11,9 %) también está cerca. Las barras de la derecha los ponen en orden, y las diferencias se ven.
La idea
Por qué un gráfico circular engaña
Las barras se comparan por la longitud desde la misma línea. El ojo lo hace bien. Un círculo hace que el ojo compare los ángulos y las áreas de sectores girados en direcciones distintas. El ojo lo hace mal, sobre todo cuando las proporciones son parecidas.
Un círculo tiene otros problemas:
- con más de 4–5 sectores las etiquetas se amontonan;
- dos círculos uno al lado del otro (diciembre frente al año) apenas se pueden comparar;
- el color es lo único que une un sector con su nombre.
Un círculo cumple su función en un caso: dos o tres sectores y la pregunta «¿más o menos de la mitad?». Para todo lo demás, las barras ordenadas dicen lo mismo con más claridad.
La idea
Distribución y relación
Quedan dos tipos de pregunta. Necesitan gráficos que vienen en la próxima lección:
- distribución: «¿qué es un pedido habitual?». La respuesta no es un número, sino cómo se reparten 10 459 pedidos: cuántos son pequeños y cuántos grandes. Un histograma lo muestra;
- relación: «¿son más caros los pedidos con más líneas?». Cada pedido se convierte en un punto con dos números. La nube de puntos es un diagrama de dispersión.
Ejercicio
Qué trae diciembre
Diciembre es el mes más fuerte. ¿Tiene más pedidos, o más grandes? Guarda en orders_n el número de pedidos de cada mes (sin los cancelados) y en avg el pedido medio en euros, y dibújalos como dos líneas en dos axes uno al lado del otro, cada uno con un título.
Solución
import pandas as pd
import matplotlib.pyplot as plt
orders = pd.read_csv('year_orders.csv')
items = pd.read_csv('year_items.csv')
products = pd.read_csv('products.csv')
# Los ingresos no incluyen los pedidos cancelados.
sold = orders[orders['status'] != 'cancelled']
lines = items.merge(sold, left_on='order_id', right_on='id')
lines['revenue'] = lines['quantity'] * lines['unit_price']
lines['month'] = lines['ordered_at'].str[:7]
lines['product'] = lines['product_id'].map(products.set_index('id')['name'])
orders_n = sold.groupby(sold['ordered_at'].str[:7]).size()
revenue = lines.groupby('month')['revenue'].sum()
avg = revenue / orders_n
fig, (left, right) = plt.subplots(1, 2, figsize=(10, 4))
left.plot(orders_n.index, orders_n.values)
left.set_title('Pedidos por mes')
right.plot(avg.index, avg.values)
right.set_title('Pedido medio, €')
left.tick_params(axis='x', labelrotation=90)
right.tick_params(axis='x', labelrotation=90)
fig.tight_layout()orders_n da 1211 pedidos en diciembre frente a 701 a 961 en los demás meses. avg = revenue / orders_n da un pedido medio de 13,14 € en diciembre, y entre 9,93 € y 10,49 € en los demás meses. Diciembre tiene a la vez más pedidos y más grandes. En un solo axes el pedido medio (cerca de 10) quedaría sobre el cero bajo los pedidos (cientos). lines.groupby('month')['revenue'].mean() es la línea media, no el pedido medio.
¿Qué parte de los ingresos de diciembre de 2025 es cada categoría, con la tarjeta de regalo? Guarda la parte en porcentaje en share, ordenada, y dibújala como barras horizontales con la mayor arriba.
Solución
import pandas as pd
import matplotlib.pyplot as plt
orders = pd.read_csv('year_orders.csv')
items = pd.read_csv('year_items.csv')
products = pd.read_csv('products.csv')
# Los ingresos no incluyen los pedidos cancelados.
sold = orders[orders['status'] != 'cancelled']
lines = items.merge(sold, left_on='order_id', right_on='id')
lines['revenue'] = lines['quantity'] * lines['unit_price']
lines['month'] = lines['ordered_at'].str[:7]
lines['product'] = lines['product_id'].map(products.set_index('id')['name'])
categories = pd.read_csv('categories.csv')
cat_of = products.set_index('id')['category_id']
cat_name = categories.set_index('id')['name']
lines['category'] = lines['product_id'].map(cat_of).map(cat_name).fillna('Tarjeta de regalo')
dec = lines[lines['month'] == '2025-12']
share = (dec.groupby('category')['revenue'].sum() / dec['revenue'].sum() * 100).sort_values()
fig, ax = plt.subplots()
ax.barh(share.index, share.values)
ax.set_title('Parte de cada categoría en diciembre')
ax.set_xlabel('Parte de los ingresos, %')
fig.tight_layout()El filtro lines['month'] == '2025-12' conserva diciembre. La suma por categoría, dividida entre la suma del mes y por 100, da las proporciones. Los lácteos están arriba con el 22,4 %, pero la cocina está cerca con el 20,4 % (en todo el año es el 10,2 %). La tarjeta de regalo es el 13,6 % de diciembre, y el 3,3 % del año. Con un círculo estas diferencias serían adivinar. Sin el filtro obtendrías las proporciones de todo el año.
Test
Ponte a prueba
Lecciones relacionadas
Sección «Lecciones relacionadas»- Se basa en: Unir tablas: merge · Ancho y largo: pivot_table y melt · Fechas y series temporales · El primer gráfico
- Continúa en: Distribuciones · Gráficos honestos · ¿Por qué bajaron los ingresos?
- Ejercicios sobre el tema: Gráficos