Análisis exploratorio de datos en un caso "real"¶

Se usa un conjunto de datos simulado,que contiene información sobre pedidos diarios de comida a domicilio en distintas zonas o barrios. Las variables qson: fecha, zona, cantidad_pedidos, precio_promedio, y categoría_comida (20 entradas).

In [14]:
import pandas as pd
import numpy as np

Conjunto de datos: Pedidos diarios de comida a domicilio en distintas zonas o barrios Fuente: Google Gemini

In [15]:
# Crear el objeto de datos
data = {
    'fecha': [
        '2025-11-01', '2025-11-01', '2025-11-01', '2025-11-01', '2025-11-01',
        '2025-11-02', '2025-11-02', '2025-11-02', '2025-11-02', '2025-11-02',
        '2025-11-03', '2025-11-03', '2025-11-03', '2025-11-03', '2025-11-03',
        '2025-11-04', '2025-11-04', '2025-11-04', '2025-11-04', '2025-11-04',
        '2025-11-05', '2025-11-05', '2025-11-05', '2025-11-05', '2025-11-05'
    ],
    'zona': [
        'Centro Ciudad', 'Zona Norte', 'Zona Sur', 'Zona Este', 'Zona Oeste',
        'Centro Ciudad', 'Zona Norte', 'Zona Sur', 'Zona Este', 'Zona Oeste',
        'Centro Ciudad', 'Zona Norte', 'Zona Sur', 'Zona Este', 'Zona Oeste',
        'Centro Ciudad', 'Zona Norte', 'Zona Sur', 'Zona Este', 'Zona Oeste',
        'Centro Ciudad', 'Zona Norte', 'Zona Sur', 'Zona Este', 'Zona Oeste'
    ],
    'cantidad_pedidos': [
        85, 45, 30, 60, 20,
        92, 50, 35, 65, 25,
        150, 60, 40, 70, 30,
        100, 550, 50, 80, 40,
        110, 70, 180, 90, 50
    ],
    'precio_promedio': [
        18.50, 15.20, 22.00, 16.80, 19.10,
        18.90, 15.50, 22.50, 17.00, 19.50,
        18.75, 15.30, 22.10, 16.90, 19.20,
        19.00, 15.70, 22.60, 17.10, 19.60,
        19.15, 15.80, 22.80, 17.20, 19.70
    ],
    'categoría_comida': [
        'Italiana', 'Asiática', 'Saludable', 'Mediterránea', 'Rápida',
        'Mexicana', 'Asiática', 'Saludable', 'Mediterránea', 'Italiana',
        'Italiana', 'Asiática', 'Rápida', 'Mexicana', 'Saludable',
        'Rápida', 'Asiática', 'Italiana', 'Mediterránea', 'Mexicana',
        'Italiana', 'Rápida', 'Saludable', 'Asiática', 'Mediterránea'
    ]
}
#Crear el DataFrame
df = pd.DataFrame(data)
In [16]:
df
Out[16]:
fecha zona cantidad_pedidos precio_promedio categoría_comida
0 2025-11-01 Centro Ciudad 85 18.50 Italiana
1 2025-11-01 Zona Norte 45 15.20 Asiática
2 2025-11-01 Zona Sur 30 22.00 Saludable
3 2025-11-01 Zona Este 60 16.80 Mediterránea
4 2025-11-01 Zona Oeste 20 19.10 Rápida
5 2025-11-02 Centro Ciudad 92 18.90 Mexicana
6 2025-11-02 Zona Norte 50 15.50 Asiática
7 2025-11-02 Zona Sur 35 22.50 Saludable
8 2025-11-02 Zona Este 65 17.00 Mediterránea
9 2025-11-02 Zona Oeste 25 19.50 Italiana
10 2025-11-03 Centro Ciudad 150 18.75 Italiana
11 2025-11-03 Zona Norte 60 15.30 Asiática
12 2025-11-03 Zona Sur 40 22.10 Rápida
13 2025-11-03 Zona Este 70 16.90 Mexicana
14 2025-11-03 Zona Oeste 30 19.20 Saludable
15 2025-11-04 Centro Ciudad 100 19.00 Rápida
16 2025-11-04 Zona Norte 550 15.70 Asiática
17 2025-11-04 Zona Sur 50 22.60 Italiana
18 2025-11-04 Zona Este 80 17.10 Mediterránea
19 2025-11-04 Zona Oeste 40 19.60 Mexicana
20 2025-11-05 Centro Ciudad 110 19.15 Italiana
21 2025-11-05 Zona Norte 70 15.80 Rápida
22 2025-11-05 Zona Sur 180 22.80 Saludable
23 2025-11-05 Zona Este 90 17.20 Asiática
24 2025-11-05 Zona Oeste 50 19.70 Mediterránea

Realizar un análisis inicial con medidas estadísticas básicas como media, mediana, moda y rango.

In [17]:
# En esta celda usamos un lenguaje exportable a archivo .py para ver la diferencia. 
# El resto de celdas es formato notebook

# Media
media_pedidos = df['cantidad_pedidos'].mean()
print(f"Media de pedidos: {media_pedidos}")
media_precios = df['precio_promedio'].mean()
print(f"Media de precios: {media_precios}")
# Mediana
mediana_pedidos = df['cantidad_pedidos'].median()
print(f"Mediana de pedidos: {mediana_pedidos}")
mediana_precios = df['precio_promedio'].median()
print(f"Mediana de precios: {mediana_precios}")
# Moda
moda_pedidos = df['cantidad_pedidos'].mode().iloc[0]
print(f"Moda de pedidos: {moda_pedidos}")
moda_precios = df['precio_promedio'].mode().iloc[0]
print(f"Moda de precios: {moda_precios}")
moda_comida = df['categoría_comida'].mode().iloc[0]
print(f"Categoría de comida más repetida: {moda_comida}")
# Rango
print(f"Rango (máximo - mínimo) días: {df['fecha'].max()} - {df['fecha'].min()}")
print(f"Rango (máximo - mínimo) pedidos: {df['cantidad_pedidos'].max()} - {df['cantidad_pedidos'].min()}")
print(f"Rango (máximo - mínimo) precios: {df['precio_promedio'].max()} - {df['precio_promedio'].min()}")
Media de pedidos: 87.08
Media de precios: 18.636
Mediana de pedidos: 60.0
Mediana de precios: 18.9
Moda de pedidos: 50
Moda de precios: 15.2
Categoría de comida más repetida: Asiática
Rango (máximo - mínimo) días: 2025-11-05 - 2025-11-01
Rango (máximo - mínimo) pedidos: 550 - 20
Rango (máximo - mínimo) precios: 22.8 - 15.2

La muestra de datos abarca solo cinco días, así que es bastante pequeña y, por tanto, poco representativa. Los precios, que no son especialmente altos, se mantienen bastante estables, en cambio, la cantidad de pedidos es muy variable, con valores que van de 20 a 550 (quizás haya errores), una mediana de 60 y una media claramente superior, de 87.08.

Identificar valores atípicos (outliers) y, si se encuentran, discutir si deberían o no ser eliminados

Para analizar esto vamos a realizar nuevos cálculos relativos a los valores de pedidos y precios

In [18]:
# Eliminamos los valores alfanuméricos para los cálculos (se puede hacer así o mediante parametro en las operaciones)
numericos = df[['cantidad_pedidos', 'precio_promedio']]
numericos
Out[18]:
cantidad_pedidos precio_promedio
0 85 18.50
1 45 15.20
2 30 22.00
3 60 16.80
4 20 19.10
5 92 18.90
6 50 15.50
7 35 22.50
8 65 17.00
9 25 19.50
10 150 18.75
11 60 15.30
12 40 22.10
13 70 16.90
14 30 19.20
15 100 19.00
16 550 15.70
17 50 22.60
18 80 17.10
19 40 19.60
20 110 19.15
21 70 15.80
22 180 22.80
23 90 17.20
24 50 19.70
In [36]:
# Calcular la desviación estándar
desviacion_e= numericos.std()
print(f"Desviacion estandar:\n{desviacion_e}")
Desviacion estandar:
cantidad_pedidos    103.705480
precio_promedio       2.398373
dtype: float64
In [37]:
varianza= numericos.var()
varianza
Out[37]:
cantidad_pedidos    10754.826667
precio_promedio         5.752192
dtype: float64
In [38]:
percentil25= numericos.quantile(0.25)
print(f"Percentil 25 (Q1):\n{percentil25}")
percentil50= numericos.quantile(0.50)
print(f"Percentil 50 (Q2):\n{percentil50}")
percentil75= numericos.quantile(0.75)
print(f"Percentil 75 (Q3):\n{percentil75}")
Percentil 25 (Q1):
cantidad_pedidos    40.0
precio_promedio     16.9
Name: 0.25, dtype: float64
Percentil 50 (Q2):
cantidad_pedidos    60.0
precio_promedio     18.9
Name: 0.5, dtype: float64
Percentil 75 (Q3):
cantidad_pedidos    90.0
precio_promedio     19.6
Name: 0.75, dtype: float64

precio_promedio tiene una desviación estándar de 2.39, una media de 18.6 y una mediana de 18.9.

Si aplicamos el criterio de valores situados a más de dos desviaciones estándar de la media, el rango sería 18.9 ± 2.39, es decir, entre 16.51 y 21.29.

Con esta definición aparecerían muchos posibles outliers pero, dado que la muestra es muy pequeña y no sigue una distribución normal, resulta más adecuado usar el método del rango intercuartílico.

Los valores relevantes son Q3 = 19.6 y Q1 = 16.9, de modo que el IQR es 2.70.

Los límites serían:

19.6 + 1.5×2.70 = 23.65

16.9 − 1.5×2.70 = 12.85

Ningún precio se encuentra fuera de este intervalo, por lo que el precio promedio por pedido se mantiene estable entre los distintos días y zonas. La mayoría de valores se concentran de forma bastante uniforme alrededor de la mediana.

In [39]:
#Tenemos una función para realizar todos los cálculos iniciales de una vez
df.describe()
Out[39]:
cantidad_pedidos precio_promedio
count 25.00000 25.000000
mean 87.08000 18.636000
std 103.70548 2.398373
min 20.00000 15.200000
25% 40.00000 16.900000
50% 60.00000 18.900000
75% 90.00000 19.600000
max 550.00000 22.800000

cantidad_pedidos tiene una desviación estándar de 103.70, media de 87.08 y mediana de 60.0 La desviación estándar es muy alta en relación con la media. Indica que deben existir valores atípicos.

In [40]:
q1 = numericos.quantile(0.25)
q3 = numericos.quantile(0.75)
iqr = q3-q1
outliers = numericos[((numericos < (q1-1.5*iqr))|(numericos > (q3+1.5*iqr))).any(axis=1)]
outliers
Out[40]:
cantidad_pedidos precio_promedio
16 550 15.7
22 180 22.8

En cantidad_pedidos Q1 es 40, así que cualquier valor muy inferior a 40 es sospechoso de ser atípico. El mínimo es 20, no parece que haya mucha variación pero haremos el mismo cálculo que antes.

Q3: 90 \ Q1: 40

IQR = 50

Limites:

90 + (1.5*50)=165

40 - (1.5*50)=-35

En cantidad_pedidos el límite inferior es negativo lo obviamos. Q3 es 90, así que cualquier valor muy superior a 90, a partir de 165, es sospechoso de ser atípico

In [41]:
# Podemos intentar ver estos outliers en el gráfico de un vistazo
import matplotlib.pyplot as plt
plt.boxplot(numericos)
Out[41]:
{'whiskers': [<matplotlib.lines.Line2D at 0x224ea7a8190>,
  <matplotlib.lines.Line2D at 0x224eaa08690>,
  <matplotlib.lines.Line2D at 0x224eaa08e10>,
  <matplotlib.lines.Line2D at 0x224eaa08f50>],
 'caps': [<matplotlib.lines.Line2D at 0x224eaa087d0>,
  <matplotlib.lines.Line2D at 0x224eaa08910>,
  <matplotlib.lines.Line2D at 0x224eaa09090>,
  <matplotlib.lines.Line2D at 0x224eaa091d0>],
 'boxes': [<matplotlib.lines.Line2D at 0x224ea886e90>,
  <matplotlib.lines.Line2D at 0x224eaa08cd0>],
 'medians': [<matplotlib.lines.Line2D at 0x224eaa08a50>,
  <matplotlib.lines.Line2D at 0x224eaa09310>],
 'fliers': [<matplotlib.lines.Line2D at 0x224eaa08b90>,
  <matplotlib.lines.Line2D at 0x224eaa09450>],
 'means': []}
No description has been provided for this image

Outlier 1: 16 El valor es inusualmente alto (550), además tiene una cifra repetida ¿error de introducción de datos?. Podemos pensar que es erroneo ya que la capacidad de preparar pedidos debe ser un valor limitado. Decisión: Eliminar o Corregir.

Outlier 2: 22 Gran volumen, 180, puede deberse a una promoción, oferta especial... No eliminar

In [42]:
# Después de contrastar datos dejamos los outliers por eventos y corregimos el error tipográfico
df.loc[16, 'cantidad_pedidos'] = 55

Visualizar la distribución de los pedidos en cada zona y en cada categoría de comida mediante gráficos de barras e histogramas.

In [43]:
import seaborn as sns
In [44]:
sns.barplot(data=df,y='cantidad_pedidos', x='zona')
Out[44]:
<Axes: xlabel='zona', ylabel='cantidad_pedidos'>
No description has been provided for this image
In [45]:
sns.barplot(data=df,y='cantidad_pedidos', x='categoría_comida')
Out[45]:
<Axes: xlabel='categoría_comida', ylabel='cantidad_pedidos'>
No description has been provided for this image
In [46]:
sns.barplot(data=df,y='cantidad_pedidos', x='zona', hue='categoría_comida')
Out[46]:
<Axes: xlabel='zona', ylabel='cantidad_pedidos'>
No description has been provided for this image
In [47]:
sns.histplot(
    data=df,
    x='cantidad_pedidos',
    hue='zona',
    multiple='stack',
    bins=10,
    palette='viridis'
)
Out[47]:
<Axes: xlabel='cantidad_pedidos', ylabel='Count'>
No description has been provided for this image
In [48]:
sns.histplot(
    data=df,
    x='cantidad_pedidos',
    hue='categoría_comida',
    multiple='stack',
    bins=10
)
Out[48]:
<Axes: xlabel='cantidad_pedidos', ylabel='Count'>
No description has been provided for this image

Vemos por los gráficos de barras que tanto la venta por zonas, como de categorías es muy variada. Los histogramas tampoco nos dan ninguna tendencia de numero de pedidos por zonas o categorías, pero sí podemos detectar algo especial con las zonas Centro y Sur y las categorías italiana y saludable, donde parece haber una cantidad inusual de pedidos. Aunque no los vimos como outliers sí que pueden ser valores especiales de venta.

Evaluar la tendencia de los pedidos en el tiempo (e.g., pedidos semanales o mensuales) y visualizar los resultados mediante una gráfica de líneas.

In [49]:
# Pedidos diarios
sns.lineplot(data=df, x='fecha', y='cantidad_pedidos', hue='zona')
Out[49]:
<Axes: xlabel='fecha', ylabel='cantidad_pedidos'>
No description has been provided for this image

Todas las zonas muestran una tendencia alcista de ventas

In [50]:
# Pedidos diarios
sns.lineplot(data=df, x='fecha', y='cantidad_pedidos', hue='categoría_comida')
Out[50]:
<Axes: xlabel='fecha', ylabel='cantidad_pedidos'>
No description has been provided for this image

¿Qué insights se pueden extraer sobre el rendimiento de pedidos por zona y por categoría de comida?

La zona con más pedidos es la zona centro y la que menos es la oeste. La categoría que más aparece es la italiana y la que menos la rápida, aunque en este caso la diferencia no es demasiado grande. Combinando las dos gráficas vemos que la zona este ofrece gran cantidad de pedidos, pero ninguno es de italiana (la más frecuente)

¿Existen patrones de demanda relacionados con el tiempo? ¿Algún mes o día de la semana muestra mayor actividad de pedidos?

En los 5 días examinados la tendencia es creciente en todas las zonas. Con nuestros datos poco más se puede decir. Si ampliamos los datos con el día de la semana, veremos que el día 1 era sábado y el 5 miércoles, así que no sabemos si la tendencia será coyuntural (seguirá subiendo) o es cíclica y el siguiente sabado volverá a bajar. Necesitamos más datos.

¿Se detectan anomalías en la cantidad de pedidos en alguna zona o categoría?

Además de los outliers ya tratados que habría que comprobar, la tendencia es correcta tanto para fecha, como zona o para categoría. Solo podemos observar una pequeña bajada en los pedidos de la zona Norte el día 3

Resolución de preguntas clave a partir de los datos¶

Debes formular preguntas clave y responderlas mediante un análisis exhaustivo de los datos

  1. Formular tres preguntas clave basadas en los objetivos del conjunto de datos.
  2. Realizar el análisis correspondiente a cada pregunta que has formulado utilizando las técnicas de AED, como estadísticas descriptivas, visualización y correlación.
  3. Documentar los métodos, pasos y conclusiones de cada análisis, justificando las decisiones tomadas para responder a cada pregunta.

Preguntas:¶

  1. ¿Cuál es la beneficio económico real por zona geográfica más allá del volumen de pedidos?
  2. ¿Existe alguna correlación negativa entre precio y demanda?
  3. ¿Cómo rinde cada categoría de comida (distribución de pedidos, medias, variabilidad)?
In [51]:
df
Out[51]:
fecha zona cantidad_pedidos precio_promedio categoría_comida
0 2025-11-01 Centro Ciudad 85 18.50 Italiana
1 2025-11-01 Zona Norte 45 15.20 Asiática
2 2025-11-01 Zona Sur 30 22.00 Saludable
3 2025-11-01 Zona Este 60 16.80 Mediterránea
4 2025-11-01 Zona Oeste 20 19.10 Rápida
5 2025-11-02 Centro Ciudad 92 18.90 Mexicana
6 2025-11-02 Zona Norte 50 15.50 Asiática
7 2025-11-02 Zona Sur 35 22.50 Saludable
8 2025-11-02 Zona Este 65 17.00 Mediterránea
9 2025-11-02 Zona Oeste 25 19.50 Italiana
10 2025-11-03 Centro Ciudad 150 18.75 Italiana
11 2025-11-03 Zona Norte 60 15.30 Asiática
12 2025-11-03 Zona Sur 40 22.10 Rápida
13 2025-11-03 Zona Este 70 16.90 Mexicana
14 2025-11-03 Zona Oeste 30 19.20 Saludable
15 2025-11-04 Centro Ciudad 100 19.00 Rápida
16 2025-11-04 Zona Norte 55 15.70 Asiática
17 2025-11-04 Zona Sur 50 22.60 Italiana
18 2025-11-04 Zona Este 80 17.10 Mediterránea
19 2025-11-04 Zona Oeste 40 19.60 Mexicana
20 2025-11-05 Centro Ciudad 110 19.15 Italiana
21 2025-11-05 Zona Norte 70 15.80 Rápida
22 2025-11-05 Zona Sur 180 22.80 Saludable
23 2025-11-05 Zona Este 90 17.20 Asiática
24 2025-11-05 Zona Oeste 50 19.70 Mediterránea
  1. ¿Cuál es la beneficio económico real por zona geográfica más allá del volumen de pedidos?
In [52]:
df['Total_zona'] = df['precio_promedio'] * df['cantidad_pedidos']
beneficio_zona = df.groupby('zona')[['precio_promedio', 'cantidad_pedidos', 'Total_zona']].sum()
beneficio_zona
Out[52]:
precio_promedio cantidad_pedidos Total_zona
zona
Centro Ciudad 94.3 537 10130.3
Zona Este 85.0 365 6212.0
Zona Norte 77.5 280 4346.5
Zona Oeste 97.1 165 3214.5
Zona Sur 112.0 335 7565.5
In [53]:
sns.barplot(
    data=beneficio_zona,
    x='cantidad_pedidos',
    y='Total_zona',
    hue='zona'
)
Out[53]:
<Axes: xlabel='cantidad_pedidos', ylabel='Total_zona'>
No description has been provided for this image

Tomando como beneficio el total ingresado por zona, es decir, cantidad de pedidos multiplicada por el precio promedio registrado cada día vemos que:

Centro Ciudad es la principal sucursal, generando 10130.30 € gracias a su gran cantidad de pedidos (537).

Sin embargo, el dato más valioso es que la Zona Este procesa más pedidos (365) que la Sur (335). A pesar de trabajar menos, la Zona Sur facturó considerablemente más (7565.50 €) frente a la Este (6212.00 €).

Esto hace que la Zona Sur sea la más eficiente: requiere menos pedidos para generar más facturación debido a su alto precio_promedio.

Por contrapartida, la Zona Oeste muestra el peor rendimiento tanto en volumen como en facturación total (3214.50 €), esto puede querer decir que necesita una intervención estratégica.

  1. ¿Existe alguna correlación negativa entre precio y demanda?

Para analizar la relación entre precio y demanda empezaremos con el cálculo del Coeficiente de Correlación de Pearson buscando un valor cercano a -1 (sube el precio, bajan los pedidos). Un valor cercano a 0 implicaría que no hay relación lineal.

In [54]:
df.corr(method='pearson', numeric_only=True)
Out[54]:
cantidad_pedidos precio_promedio Total_zona
cantidad_pedidos 1.000000 0.055939 0.977118
precio_promedio 0.055939 1.000000 0.243655
Total_zona 0.977118 0.243655 1.000000

Vemos que no parece haber correlación lineal de precio_promedio ni con la cantidad de pedidos, ni con el total de la zona. Vamos a comprobar otros tipso de correlaciones mediante el coeficiente de Spearman

In [55]:
df.corr(method='spearman', numeric_only=True)
Out[55]:
cantidad_pedidos precio_promedio Total_zona
cantidad_pedidos 1.000000 -0.211479 0.958399
precio_promedio -0.211479 1.000000 0.016154
Total_zona 0.958399 0.016154 1.000000

Tampoco parece haber correlación de otro tipo entre los datos. Para asegurarnos mostraremos un gráfico de dispersión

In [56]:
sns.scatterplot(data=df, x='precio_promedio', y='cantidad_pedidos')
Out[56]:
<Axes: xlabel='precio_promedio', ylabel='cantidad_pedidos'>
No description has been provided for this image

El gráfico no muestra tendencia alguna.

Tras ejecutar todos los pasos. El coeficiente de Pearson obtenido es 0.056 y el de Spearman es -0.21, esto es técnicamente una correlación nula. El precio no es la variable que determina el volumen de ventas.

  1. ¿Cómo rinde cada categoría de comida (distribución de pedidos, medias, variabilidad)?
In [91]:
beneficio_comida = df.groupby('categoría_comida')[['precio_promedio', 'cantidad_pedidos', 'Total_zona']].sum()
beneficio_comida
Out[91]:
precio_promedio cantidad_pedidos Total_zona
categoría_comida
Asiática 78.9 300 4788.5
Italiana 98.5 420 8109.0
Mediterránea 70.6 255 4466.0
Mexicana 55.4 202 3705.8
Rápida 76.0 230 4272.0
Saludable 86.5 275 6127.5
In [92]:
sns.scatterplot(data=beneficio_comida, x='Total_zona', y='cantidad_pedidos', hue='categoría_comida')
Out[92]:
<Axes: xlabel='Total_zona', ylabel='cantidad_pedidos'>
No description has been provided for this image

El análisis inicial revela que:

Comida Italiana es indiscutiblemente la categoría estrella, generando 8109.00 con 420 pedidos.

El dato más valioso aparece al comparar la categoría Asiática con la Saludable:

La Asiática procesa un volumen superior de pedidos (300) frente a la Saludable (275).

A pesar de tener menor carga de trabajo, la Saludable facturó considerablemente más (6127.50 €) frente a la Asiática (4788.50 €).

Esto convierte a la categoría Saludable en la más rentable del menú: tiene los pedidos más caros. La Asiática sufre muchos pedidos para un margen total más bajo.

La Comida Mexicana es la que peor funciona tanto en volumen (202) como en facturación total (3705.80 €), sugiriendo su posible eliminación si al analizar los costes no sale rentable.

Ahora vamos a asegurar el análisis analizando los datos atendiendo a su eficiencia diaria, para lo que calcularemos medias (por si los totales no son representativos) y el riesgo de venta mediante la varianza del número de pedidos (para ver si un buen dato puede ser engañoso por ser algo puntual).

In [93]:
media_pedidos = df['cantidad_pedidos'].mean()
media_por_categoria = df.groupby('categoría_comida')['cantidad_pedidos'].mean()
diferencial = media_por_categoria - media_pedidos
diferencial
Out[93]:
cantidad_pedidos
categoría_comida
Asiática -7.280000
Italiana 16.720000
Mediterránea -3.530000
Mexicana 0.053333
Rápida -9.780000
Saludable 1.470000

In [94]:
varianza_pedidos = df['cantidad_pedidos'].var()
varianza_por_categoria = df.groupby('categoría_comida')['cantidad_pedidos'].var()
diferencial_v = varianza_por_categoria - varianza_pedidos
diferencial_v
Out[94]:
cantidad_pedidos
categoría_comida
Asiática -1147.876667
Italiana 957.123333
Mediterránea -1304.126667
Mexicana -779.043333
Rápida -235.376667
Saludable 4045.873333

In [95]:
resumen_final = pd.concat([beneficio_comida, diferencial, diferencial_v], axis=1)
resumen_final.columns = [
    'Precio_promedio',
    'Total_Pedidos',
    'Ingresos_Totales',
    'Dif_Media',
    'Riesgo_Variabilidad'
]
resumen_final
Out[95]:
Precio_promedio Total_Pedidos Ingresos_Totales Dif_Media Riesgo_Variabilidad
categoría_comida
Asiática 78.9 300 4788.5 -7.280000 -1147.876667
Italiana 98.5 420 8109.0 16.720000 957.123333
Mediterránea 70.6 255 4466.0 -3.530000 -1304.126667
Mexicana 55.4 202 3705.8 0.053333 -779.043333
Rápida 76.0 230 4272.0 -9.780000 -235.376667
Saludable 86.5 275 6127.5 1.470000 4045.873333

Al cruzar los ingresos totales con el rendimiento medio diario y la variabilidad, el diagnóstico cambia respecto a la visión inicial.

La comida mexicana al ser la última en facturación total (3705.8), parecía la candidata a eliminar. Su bajo importe total se debe a su baja frecuencia de aparición, sin embargo, su diferencial es positivo (+0.05) respecto a la media global. El día que se ha vendido, funciona. Quizá con intentar venderla más días se solucione el problema.

Comida Rápida y Asiática, ambas tienen diferenciales muy negativos(-9.78 y -7.28). Sistemáticamente rindieron por debajo del promedio del negocio. La comida Rápida es la menos eficiente: genera un volumen que consume recursos sin aportar margen ni estabilidad.

La comida Saludable parecía la más rentable, pero su variabilidad es extrema. No se pueden basar los costes fijos en ella aunque puede hacer que entren altos importes en caja. Sin embargo, la comida Mediterránea aunque factura menos, su variabilidad es mínima. Es la categoría que permite planificar las ventas.

En lo que sí acertamos fue en que la comida Italiana es la líder indiscutible en todas las métricas.

Preguntas para reflexionar:

  • ¿Qué preguntas han podido responderse de manera concluyente, y cuáles no?
  • ¿Cómo ayuda cada análisis realizado a comprender mejor el conjunto de datos?
  • ¿Qué recomendaciones pueden hacerse basadas en las respuestas obtenidas?

Las tres preguntas han quedado resueltas. Los cálculos y las visualizaciones han revelado patrones que no se apreciaban a simple vista, por ejemplo que la categoría Mexicana, aunque es la última en facturación total, muestra una media diaria superior al promedio del negocio.

Resumiendo las conclusiones principales, Centro Ciudad es el núcleo del flujo de caja, ya que destaca en todos los indicadores, y la comida Italiana lidera tanto en volumen como en media diaria. La combinación de ambos factores es el pilar del negocio.

También se confirma que reducir precios no incrementaría la demanda de forma proporcional, por lo que no es una estrategia recomendable. Al revisar la variabilidad por categorías vimos que el stock de la comida mediterránea puede automatizarse, dado que presenta una varianza muy baja, mientras que en la saludable conviene mantener un stock más alto por los picos de venta que pueden surgir.

En cuanto a la comida Mexicana, aumentar su frecuencia de venta podría mejorar la rentabilidad, aunque solo contamos con tres días de datos y no puede descartarse que exista algún elemento que esté inflando sus métricas. Sería necesario ampliar la muestra.

Del mismo modo, con solo cinco días registrados no es posible determinar si los picos detectados son patrones habituales o eventos puntuales.