Introducción

El objetivo es estimar el inventario ideal para cada producto utilizando la demanda histórica mensual y un nivel de servicio del 95%.

El modelo utilizado será:

\[ Inventario\ Ideal = \mu + Z\sigma \]

Donde:

Carga de librerías

library(readxl)
library(dplyr)
library(tidyr)
library(ggplot2)

Carga de datos

inventario <- read_excel(
"C:/Users/isaac/Searches/Downloads/Ejercicio Análisis de Inventario y Nivel de Servicio (1) (1) (1) (1).xlsx"
)

head(inventario)
## # A tibble: 6 × 14
##   Producto Enero Febrero Marzo Abril  Mayo Junio Julio Agosto Septiembre Octubre
##   <chr>    <dbl>   <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>  <dbl>      <dbl>   <dbl>
## 1 DAC0023…     0       0     0     0     0     0     0      0          0       0
## 2 DAC0023…     0       0     0     0     0     0     0      0          0       0
## 3 DAC0023…     0       0     0     0     0     0     0      0          0       0
## 4 DAX0041…     0       0     0     0     0     0     0      0          0       0
## 5 DAX0041…     0       0     0     0     0     0     0      0          0       0
## 6 DAX0041…     0       0     0     0     0     0     0      0          0       0
## # ℹ 3 more variables: Noviembre <dbl>, Diciembre <dbl>, `Costo unitario` <dbl>

Transformación de datos

La base original se encuentra en formato ancho. Se transforma a formato largo para facilitar el análisis.

inventario_long <- inventario %>%
  pivot_longer(
    cols = -c(Producto, `Costo unitario`),
    names_to = "Mes",
    values_to = "Cantidad"
  ) %>%
  mutate(
    `Costo unitario` = as.numeric(`Costo unitario`),
    Cantidad = as.integer(Cantidad)
  )

head(inventario_long)
## # A tibble: 6 × 4
##   Producto    `Costo unitario` Mes     Cantidad
##   <chr>                  <dbl> <chr>      <int>
## 1 DAC00238020                1 Enero          0
## 2 DAC00238020                1 Febrero        0
## 3 DAC00238020                1 Marzo          0
## 4 DAC00238020                1 Abril          0
## 5 DAC00238020                1 Mayo           0
## 6 DAC00238020                1 Junio          0

Exploración inicial

Número de productos

n_distinct(inventario_long$Producto)
## [1] 4770

Número de observaciones por producto

inventario_long %>%
  group_by(Producto) %>%
  summarise(n = n()) %>%
  count(n)
## # A tibble: 1 × 2
##       n    nn
##   <int> <int>
## 1    12  4770

Interpretación:

Cada producto tiene exactamente 12 observaciones correspondientes a los meses de enero a diciembre.

Clasificación de productos

Se clasifican las referencias según si presentan o no movimiento.

clasif <- inventario_long %>%
  group_by(Producto) %>%
  summarise(
    Total_Demanda = sum(Cantidad)
  ) %>%
  mutate(
    Estado = ifelse(
      Total_Demanda > 0,
      "Activo",
      "Sin movimiento"
    )
  )

head(clasif)
## # A tibble: 6 × 3
##   Producto    Total_Demanda Estado        
##   <chr>               <int> <chr>         
## 1 4135061001              0 Sin movimiento
## 2 523560                  0 Sin movimiento
## 3 525015                  0 Sin movimiento
## 4 AC000001222             0 Sin movimiento
## 5 AC000002222             0 Sin movimiento
## 6 AC000005020             2 Activo

Resumen de clasificación

clasificacion_resumen <- inventario_long %>%
  group_by(Producto) %>%
  summarise(
    Total = sum(Cantidad)
  ) %>%
  summarise(
    Productos_con_demanda = sum(Total > 0),
    Productos_sin_demanda = sum(Total == 0)
  )

clasificacion_resumen
## # A tibble: 1 × 2
##   Productos_con_demanda Productos_sin_demanda
##                   <int>                 <int>
## 1                  2449                  2320

Interpretación:

La base contiene productos activos y productos sin movimiento. Los productos sin demanda representan oportunidades potenciales de optimización del inventario.

Productos activos

Para el análisis del inventario ideal se consideran únicamente las referencias con movimiento.

productos_activos <- inventario_long %>%
  group_by(Producto) %>%
  filter(sum(Cantidad) > 0)

head(productos_activos)
## # A tibble: 6 × 4
## # Groups:   Producto [1]
##   Producto    `Costo unitario` Mes     Cantidad
##   <chr>                  <dbl> <chr>      <int>
## 1 DAB28248025          105484. Enero          0
## 2 DAB28248025          105484. Febrero        0
## 3 DAB28248025          105484. Marzo          0
## 4 DAB28248025          105484. Abril          0
## 5 DAB28248025          105484. Mayo           0
## 6 DAB28248025          105484. Junio          0

Construcción del modelo

Supuestos:

  1. La demanda histórica representa el comportamiento futuro.
  2. Se utiliza un nivel de servicio del 95%.
  3. La demanda puede aproximarse mediante una distribución normal.
  4. El inventario de seguridad se calcula con la desviación estándar histórica.
#Calculo Inventario Ideal
resumen <- productos_activos %>%
  group_by(Producto) %>%
  summarise(
    Demanda_Promedio = mean(Cantidad),
    Desv_Demanda = sd(Cantidad),
    Costo_Unitario = first(`Costo unitario`)
  ) %>%
  mutate(
    Inventario_Seguridad =
      1.645 * Desv_Demanda,

    Inventario_Ideal =
      Demanda_Promedio +
      Inventario_Seguridad,

    Nivel_Servicio = "95%"
  )

head(resumen)
## # A tibble: 6 × 7
##   Producto    Demanda_Promedio Desv_Demanda Costo_Unitario Inventario_Seguridad
##   <chr>                  <dbl>        <dbl>          <dbl>                <dbl>
## 1 AC000005020           0.167         0.577         1250                  0.950
## 2 AGBGRASA030          96.3         152.              26.8              249.   
## 3 AHB0AW68116           0.0833        0.289       317625.                 0.475
## 4 AHB80W90050           0.25          0.452       460139.                 0.744
## 5 AHB85W14050           0.0833        0.289       469317.                 0.475
## 6 AHBNTO68030           3.42          2.07        342304.                 3.40 
## # ℹ 2 more variables: Inventario_Ideal <dbl>, Nivel_Servicio <chr>

Visualización 1: Productos con y sin demanda

df_estado <- data.frame(
  Estado = c("Con demanda", "Sin demanda"),
  Cantidad = c(2449, 2320)
)

ggplot(
  df_estado,
  aes(
    x = Estado,
    y = Cantidad,
    fill = Estado
  )
) +
  geom_col() +
  labs(
    title = "Clasificación de productos según demanda",
    x = "",
    y = "Número de productos"
  ) +
  theme_minimal()

Interpretación:

La distribución muestra que una proporción importante de las referencias no presenta movimiento durante el período analizado.

#Visualización 2: Distribución de la demanda promedio

ggplot(
  resumen,
  aes(x = Demanda_Promedio)
) +
  geom_histogram(
    bins = 30,
    fill = "steelblue"
  ) +
  labs(
    title = "Distribución de la demanda promedio",
    x = "Demanda promedio",
    y = "Frecuencia"
  ) +
  theme_minimal()

Interpretación:

La mayoría de los productos presenta una demanda promedio baja, mientras que un grupo reducido concentra gran parte del consumo.

#Visualización 3: Inventario ideal vs demanda promedio

ggplot(
  resumen,
  aes(
    x = Demanda_Promedio,
    y = Inventario_Ideal
  )
) +
  geom_point(alpha = 0.5) +
  labs(
    title = "Inventario ideal vs demanda promedio",
    x = "Demanda promedio",
    y = "Inventario ideal"
  ) +
  theme_minimal()

Interpretación:

Existe una relación positiva entre demanda promedio e inventario ideal. Los productos con mayor demanda requieren mayores niveles de inventario y stock de seguridad.

Nivel de servicio

El modelo utiliza:

Z = 1.645 pnorm(1.645) Interpretación:

El valor corresponde aproximadamente a un nivel de servicio del 95%.

Conclusiones

Se analizaron las referencias con movimiento durante el período. Se utilizó un modelo probabilístico basado en demanda promedio y desviación estándar. El inventario ideal se calculó como:

Inventario Ideal = \(\mu\) + 1.645\(\sigma\)

El nivel de servicio teórico alcanzado es del 95%. Los productos con mayor variabilidad requieren un mayor inventario de seguridad.