`

Introducción

Adidas AG es una de las compañías más reconocidas a nivel mundial en la industria de artículos deportivos, especializándose en el diseño, fabricación y comercialización de calzado, ropa y accesorios deportivos. Fundada en 1949 en Alemania por Adolf Dassler, a lo largo de estos 77 años de historia ha logrado posicionar su presencia en más de 160 países, con una alta participación de mercado en Europa, Estados Unidos y China. Actualmente cuenta con más de 2000 tiendas propias y aproximadamente 15000 franquicias.

El enfoque en innovación, tecnología y marketing deportivo ha permitido que la marca mantenga una fuerte presencia en mercados internacionales y en diversas disciplinas deportivas.

A través del presente informe se analizarán diferentes variables relacionadas con el desempeño de la compañia, mediante del uso de técnicas estadísticas y visualización de información, que busca identificar patrones, tendencias y relaciones que permitan comprender mejor el comportamiento de los datos asociados a la compañía.

library(readxl)
Caso_1 <- read_excel("Caso 1.xlsx")

** Análisis descriptivo #Estado de Resultados por producto**

# Cargar librerías necesarias
library(readxl)
library(tidyverse)
library(knitr)
library(kableExtra)
library(scales)
Caso_1 <- read_excel("Caso 1.xlsx")

head(Caso_1)
distribuidor region estado ciudad producto precio_unidad unidades_vendidas ventas_total utilidad_operativa margen_operativo metodo_venta
Foot Locker Northeast New York New York Men’s Street Footwear 50 1200 60000 30000.0 0.50 In-store
Foot Locker Northeast New York New York Men’s Athletic Footwear 50 1000 50000 15000.0 0.30 In-store
Foot Locker Northeast New York New York Women’s Street Footwear 40 1000 40000 14000.0 0.35 In-store
Foot Locker Northeast New York New York Women’s Athletic Footwear 45 850 38250 13387.5 0.35 In-store
Foot Locker Northeast New York New York Men’s Apparel 60 900 54000 16200.0 0.30 In-store
Foot Locker Northeast New York New York Women’s Apparel 50 1000 50000 12500.0 0.25 In-store


## Estructura financiera general

Gráfico 1: Las ventas de la compañía ascendieron a 120.17 Millones de dólares con 2.48 Millones de unidades vendidas, un costo operacional de 72.94 millones, generado una utilidad operativa de 47.22 Millones de dólares, representadas en un margen operativo ponderado del 39%.3.

Estado de Resultados por producto

library(readxl)
library(dplyr)
library(knitr)
library(kableExtra)
library(scales)



# Tabla resumen
tabla_resumen <- Caso_1 %>%
  group_by(producto) %>%
  summarise(
    Unidades_vendidas   = sum(unidades_vendidas, na.rm = TRUE),
    ventas_total        = sum(ventas_total, na.rm = TRUE),
    utilidad_operativa  = sum(utilidad_operativa, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  mutate(
    Costo_operacional   = ventas_total - utilidad_operativa,
    Margen_operacional  = utilidad_operativa / ventas_total
  ) %>%
  arrange(desc(ventas_total)) %>%
  select(producto, Unidades_vendidas, ventas_total, Costo_operacional,
         utilidad_operativa, Margen_operacional)

# Fila de totales
totales <- tabla_resumen %>%
  summarise(
    producto            = "Totales",
    Unidades_vendidas   = sum(Unidades_vendidas, na.rm = TRUE),
    ventas_total        = sum(ventas_total, na.rm = TRUE),
    Costo_operacional   = sum(Costo_operacional, na.rm = TRUE),
    utilidad_operativa  = sum(utilidad_operativa, na.rm = TRUE),
    Margen_operacional  = sum(utilidad_operativa, na.rm = TRUE) / sum(ventas_total, na.rm = TRUE)
  )

tabla_final <- bind_rows(tabla_resumen, totales)

# Convertir a formato de presentación
tabla_mostrar <- tabla_final %>%
  mutate(
    Unidades_vendidas  = comma(Unidades_vendidas, accuracy = 1, big.mark = ","),
    ventas_total       = dollar(ventas_total, accuracy = 1),
    Costo_operacional  = dollar(Costo_operacional, accuracy = 1),
    utilidad_operativa = dollar(utilidad_operativa, accuracy = 1),
    Margen_operacional = number(Margen_operacional, accuracy = 0.01)
  )

# Crear tabla
kbl <- tabla_mostrar %>%
  kable(
    format = "html",
    escape = FALSE,
    align = c("l", "c", "c", "c", "c", "c"),
    col.names = c("Producto",
                  "Unidades vendidas",
                  "ventas_total",
                  "Costo operacional",
                  "utilidad_operativa",
                  "Margen operacional total"),
    caption = "Estado de Resultados por producto"
  ) %>%
  kable_styling(
    full_width = FALSE,
    position = "left",
    font_size = 16
  ) %>%
  row_spec(
    0,
    bold = TRUE,
    color = "white",
    background = "#0B2E6F"
  ) %>%
  row_spec(
    nrow(tabla_mostrar),
    bold = TRUE,
    color = "white",
    background = "#0B2E6F"
  ) %>%
  column_spec(1, bold = TRUE, width = "24em") %>%
  column_spec(6, color = "#1A8E2D", bold = TRUE)

# Aplicar colores alternos a las filas intermedias
for(i in 1:(nrow(tabla_mostrar) - 1)) {
  if(i %% 2 == 1) {
    kbl <- kbl %>% row_spec(i, background = "#FFFFFF")
  } else {
    kbl <- kbl %>% row_spec(i, background = "#E0F7FA")
  }
}

kbl
Estado de Resultados por producto
Producto Unidades vendidas ventas_total Costo operacional utilidad_operativa Margen operacional total
Men’s Street Footwear 593,320 $27,680,769 $16,051,723 $11,629,046 0.42
Women’s Apparel 433,827 $23,870,985 $14,185,764 $9,685,221 0.41
Men’s Athletic Footwear 435,526 $20,577,180 $13,139,723 $7,437,457 0.36
Women’s Street Footwear 392,269 $17,201,563 $10,707,546 $6,494,017 0.38
Men’s Apparel 306,683 $16,520,632 $10,139,227 $6,381,405 0.39
Women’s Athletic Footwear 317,236 $14,315,521 $8,717,699 $5,597,822 0.39
Totales 2,478,861 $120,166,650 $72,941,682 $47,224,968 0.39

En la tabla 1, se observa un análisis preliminar general por producto, donde se detallan las unidades vendidas totales, el costo operacional calculado respecto de las ventas totales menos la utilidad operativa, así como el margen operacional de cada uno.

Conforme a ello, se pueden observar en orden descendente respecto a las ventas, los productos con mayor y menor venta, en donde los mayores ingresos 27.68 Millones de dólares, provienen de la linea de calzado Men´s Street Footwear los cuales a su vez, cuentan con un margen operacional ponderado del 42%, siendo el más alto frente líneas de producto restantes, así mismo cuentan con la mayor cantidad de unidades vendidas. En cuanto al producto con menores ingresos, se identifica la línea femenina de calzado deportivo Women´s Athletic footwear, la cual vendió 14.31 Millones de dólares generando un margen operativo ponderado del 39%, apesar de no ser en términos de volúmen la línea de producto de menores ventas.

Los márgenes ponderados por producto, se determinaron tomando la utilidad operativa total de cada uno sobre sus ventas totales; para el margen global se aplica la misma metodología, pero sumando la utilidad operativa total de todos los productos, divida sobre la suma de las ventas totales de cada producto.

El análisis anterior se realiza con el objetivo de obtener un panorama general financiero como hoja de ruta para el análisis exploratorio que se realizará a continuación.

###Análisis exploratorio

Comportamiento de ventas generales por método de venta

library(dplyr)
library(ggplot2)
library(scales)

# =========================================================
# 1. Resumir ventas y unidades por metodo de venta
# =========================================================

ventas_metodo <- Caso_1 %>%
  mutate(
    ventas_total = as.numeric(ventas_total),
    unidades_vendidas = as.numeric(unidades_vendidas),
    metodo_venta = as.character(metodo_venta)
  ) %>%
  filter(
    !is.na(metodo_venta),
    !is.na(ventas_total),
    !is.na(unidades_vendidas)
  ) %>%
  group_by(metodo_venta) %>%
  summarise(
    ventas_total = sum(ventas_total, na.rm = TRUE),
    unidades_vendidas = sum(unidades_vendidas, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  mutate(
    participacion_ventas = ventas_total / sum(ventas_total)
  ) %>%
  arrange(desc(ventas_total)) %>%
  mutate(
    metodo_venta = factor(metodo_venta, levels = metodo_venta)
  )

# =========================================================
# 2. Colores de referencia
# =========================================================

colores_referencia <- c(
  "Online"   = "#22C55E",
  "Outlet"   = "#7EA6F7",
  "In-store" = "#f28b82"
)

# Si los nombres reales no coinciden exactamente, usar asignación automática
if (!all(levels(ventas_metodo$metodo_venta) %in% names(colores_referencia))) {
  colores_referencia <- c("#F28B82", "#22C55E", "#7EA6F7")
  names(colores_referencia) <- levels(ventas_metodo$metodo_venta)
}

# =========================================================
# 3. Escala para eje secundario
# =========================================================

max_ventas_dato <- max(ventas_metodo$ventas_total, na.rm = TRUE)
max_unidades_dato <- max(ventas_metodo$unidades_vendidas, na.rm = TRUE)

max_ventas <- max_ventas_dato * 1.32
max_unidades <- max_unidades_dato * 1.10
factor_escala <- max_ventas / max_unidades

# =========================================================
# 4. Posiciones corregidas de etiquetas
#    - ventas: arriba de la barra
#    - unidades: debajo del punto/línea
# =========================================================

ventas_metodo <- ventas_metodo %>%
  mutate(
    y_linea = unidades_vendidas * factor_escala,
    y_etiqueta_ventas = ventas_total + max_ventas * 0.025,
    y_etiqueta_unidades = y_linea - max_ventas * 0.020
  )

# Evitar que la etiqueta de unidades caiga dentro de la barra
ventas_metodo <- ventas_metodo %>%
  mutate(
    y_etiqueta_unidades = ifelse(
      y_etiqueta_unidades <= ventas_total + max_ventas * 0.006,
      ventas_total + max_ventas * 0.010,
      y_etiqueta_unidades
    )
  )

# =========================================================
# 5. Gráfico combinado ajustado
# =========================================================

ggplot(ventas_metodo, aes(x = metodo_venta)) +
  
  # Barras de ventas más angostas
  geom_col(
    aes(y = ventas_total, fill = metodo_venta),
    width = 0.48,
    alpha = 0.95
  ) +
  
  # Participación dentro de la barra
  geom_text(
    aes(
      y = ventas_total * 0.90,
      label = percent(participacion_ventas, accuracy = 0.1)
    ),
    size = 4.6,
    color = "white",
    fontface = "bold"
  ) +
  
  # Línea de unidades vendidas
  geom_line(
    aes(
      y = y_linea,
      group = 1
    ),
    linewidth = 1.2,
    color = "#D62728"
  ) +
  
  geom_point(
    aes(y = y_linea),
    size = 3.1,
    color = "#D62728"
  ) +
  
  # Etiquetas de ventas totales (encima de la barra)
  geom_text(
    aes(
      y = y_etiqueta_ventas,
      label = paste0(round(ventas_total / 1e6, 2), "M")
    ),
    size = 4.5,
    color = "#2F2F2F",
    fontface = "bold"
  ) +
  
  # Etiquetas de unidades vendidas (debajo de la línea/punto)
  geom_text(
    aes(
      y = y_etiqueta_unidades,
      label = comma(unidades_vendidas)
    ),
    size = 4.2,
    color = "#4D4D4D"
  ) +
  
  scale_fill_manual(values = colores_referencia) +
  
  scale_y_continuous(
    limits = c(0, max_ventas),
    breaks = seq(0, ceiling(max_ventas / 10000000) * 10000000, by = 10000000),
    labels = label_number(scale = 1e-6, suffix = "M"),
    expand = expansion(mult = c(0, 0.02)),
    sec.axis = sec_axis(
      ~ . / factor_escala,
      labels = comma
    )
  ) +
  
  labs(
    title = "VENTAS TOTALES POR METODO DE VENTA",
    x = NULL,
    y = "Ventas totales",
    fill = "Metodo de venta"
  ) +
  
  theme_minimal() +
  theme(
    plot.title = element_text(
      hjust = 0.5,
      size = 22,
      face = "bold",
      color = "#303030",
      margin = margin(b = 18)
    ),
    axis.text.x = element_text(size = 13, color = "#595959"),
    axis.text.y.left = element_text(size = 11, color = "#595959"),
    axis.text.y.right = element_text(size = 11, color = "#595959"),
    axis.title.y.left = element_text(size = 13, color = "#303030"),
    panel.grid.major.y = element_line(color = "#D9D9D9"),
    panel.grid.major.x = element_blank(),
    panel.grid.minor = element_blank(),
    legend.position = "bottom",
    legend.title = element_text(size = 12),
    legend.text = element_text(size = 11),
    plot.background = element_rect(fill = "white", color = NA),
    panel.background = element_rect(fill = "white", color = NA),
    plot.margin = margin(t = 20, r = 35, b = 20, l = 20)
  )

En el gráfico 1, se muestran las ventas y unidades totales clasificadas por método de venta, donde, se puede observar que las ventas online tienen una participación del 37.4% con 939.093 unidades vendidas equivalentes a 45Millones de dólares, mientras que las ventas en tiendas físicas en In-store y Outlet representan en conjunto el 62.58% del gran total, lo cual equivale a 75.2 Millones de dólares con 1.5 millones de unidades vendidas,siendo el método online el segundo con participación más alta con un 32.9%.

Este panorama refleja de manera evidente cómo el canal digital en el siglo XXI, se ha convertido en un medio estratégico para la compañía, consolidándose como un actor clave para llegar a más mercados, sin perder de vista la conexión con los clientes a través de las tiendas físicas.

##Comportamiento de ventas generales por Región y Método de venta

library(dplyr)
library(ggplot2)
library(scales)

# 1. Resumen por región y metodo de venta
resumen_region_metodo <- Caso_1 %>%
  group_by(region, metodo_venta) %>%
  summarise(
    ventas_totales = sum(ventas_total, na.rm = TRUE),
    utilidad_operativa = sum(utilidad_operativa, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  mutate(
    margen_operativo = ifelse(
      ventas_totales > 0,
      utilidad_operativa / ventas_totales,
      NA_real_
    ),
    etiqueta_ventas = label_number(scale = 1e-6, suffix = "M")(ventas_totales),
    etiqueta_margen = percent(margen_operativo, accuracy = 0.1)
  )

# 2. Factor de escala
factor_escala <- max(resumen_region_metodo$ventas_totales, na.rm = TRUE)

# 3. Grafico
ggplot(resumen_region_metodo, aes(x = metodo_venta)) +
  geom_col(
    aes(y = ventas_totales, fill = metodo_venta),
    alpha = 0.85,
    width = 0.65
  ) +
  
  geom_text(
    aes(y = ventas_totales, label = etiqueta_ventas),
    vjust = -0.35,
    size = 3.2
  ) +
  
  geom_line(
    aes(y = margen_operativo * factor_escala, group = 1),
    color = "red",
    linewidth = 1
  ) +
  geom_point(
    aes(y = margen_operativo * factor_escala),
    color = "red",
    size = 2.8
  ) +
  
  geom_text(
    aes(
      y = margen_operativo * factor_escala,
      label = etiqueta_margen
    ),
    color = "red",
    vjust = -1.1,
    size = 3.1
  ) +
  
  facet_wrap(~ region, scales = "free_y") +
  scale_y_continuous(
    name = "Ventas totales",
    labels = label_number(scale = 1e-6, suffix = "M"),
    expand = expansion(mult = c(0.05, 0.18)),
    sec.axis = sec_axis(
      ~ . / factor_escala,
      name = "Margen operativo",
      labels = percent_format(accuracy = 0.1)
    )
  ) +
  
  labs(
    title = "Ventas totales y margen operativo por region y metodo de venta",
    x = "Metodo de venta",
    fill = "Metodo de venta"
  ) +
  
  theme_minimal(base_size = 12) +
  theme(
    legend.position = "none",
    plot.title = element_text(face = "bold", size = 15, hjust = 0.5),
    strip.text = element_text(face = "bold"),
    axis.text.x = element_text(angle = 25, hjust = 1)
  )

En el gráfico 2, se observa la distribución de las ventas por regiones y método de venta donde:

Midwest concentra ventas en los métodos online e In-store, siendo el primero el más predominante con 165.992 unidades vendidas equivalentes a 7millones de dólares.

Northeast, tiene una concentración de ventas en In-store y Outlet de 11 millones y 8.8 millones de dólares respectivamente, siendo menos predominante en esta región, las ventas online.

Entre tanto en South la mayor proporción de las ventas registradas, provienen de los métodos Outlet y online, con 11 y 9.2 millones de dólares respectivamente, siendo el canal de outlet el de menor participación con 0,34 millones de dólares.

En la región de Southeast, el método online predomina de manera significativa con 12millones de dólares, mientras que el canal de Oulet es el menos representativo con 2 millones.

En la región West, aunque las mayores ventas provienen de Outlet con 13.95 millones, frente a las demás regiones, se observa una menor variación entre los tres métodos de venta utilizados.

En cuanto a los margenes operativos ponderados, estos evidencian que el método on line es el más rentable frente a los métodos In- Store y Outlet donde el margen operativo de estos no alcanza el 40%, destacándose la región de Northeats donde a pesar de que las ventas por este método son las menos representativas, este margen alcanza el 48.6% siendo el rendimiento más alto por regiones en términos porcentuales.

Esto se puede explicar, porque, los costos indirectos de venta como lo son, locales, sales persons, servicios y demás, resultan optimizados mediante los canales virtuales, en los cuales las ventas se procesan de manera masiva al igual que el proceso logistico de almacenamiento y distribución al consumidor final que generalmente se manejan en centros de distribución, lo que hace que estos costos resulten inferiores frente a los costos asumidos en las tiendas físicas.


Comportamiento de ventas por región y producto

Grafico 3, Por producto, la región West concentra la mayor parte de las ventas con un 30.32% equivalentes a 36.5 millones de dólares, seguido de Northeast con un 20.9%; entre tanto las regiones de South y Southeast tienen una partipación menor pero casi igual entre ellas, con un 17.15% y 17.8% respectivamente.

En cuanto a la región de Midwest, esta concentra la menor proporción de las ventas con un 13.9% con 16.7 millones de dólares.

Los productos Men´s Street footwear, women´s apparel y Men´s athetlic footwear, registran la mayor participación en unidades vendidas e ingresos facturados por región, concentrando entre ellos alrededor del 58% de las ventas.

Ventas por distribuidor y método de venta

Gráfico 4. Para el presente análisis, se estudian seis distribuidores de la marca, los cuales operan bajo los tres métodos de venta antes mencionados, en donde el distribuidor West Gear concentra el 30% de las ventas las cuales ascienden a 32.4 Millones de dólares con 625 mil unidades vendidas, y un margen operativo ponderado del 37.63%, gestionando sus ventas principalmente en tiendas In-store, las cuales representan a su vez casi un 50% del total vendido, en tanto el restante se encuentra segmentado en tiendas online y outlets.

En el segundo lugar de partipación de ingresos, se encuentra Foot locker con 604 mil unidades vendidas e ingresos por 29 millones de dólares y un margen operacional ponderado del 39%, en donde se puede observar que el canal on line genera alrededor del 42% de sus ventas.

Seguido a estos dos distribuidores, se encuentra Sports direct con 557mil unidades vendidas, 24 millones de dólares en ingresos y un margen operativo ponderado del 43.23%, siendo el más alto de los seis distribuidores estudiados; por método de venta, se puede observar que los canales on line y outlet concentran alrededor del 77% de sus ingresos con una participación del 40.7% y 26.96% respectivamente.

Estos tres distribuidores representan el 72% de las ventas totales.

El 28% restante, lo componen Kohl´s y walmart con gestión de ventas principalmente en los canales Online y Outlet seguido de Amazon con ventas principalmente en canal online y oulet.


## Ventas y margenes por distribuidor y region

library(dplyr)
library(ggplot2)
library(scales)
library(grid)

# =========================================================
# 1. Resumen de ventas y margen por región y distribuidor
# =========================================================

ventas_region_distribuidor <- Caso_1 %>%
  mutate(
    region = as.character(region),
    distribuidor = as.character(distribuidor),
    ventas_total = as.numeric(ventas_total),
    utilidad_operativa = as.numeric(utilidad_operativa)
  ) %>%
  filter(
    !is.na(region),
    !is.na(distribuidor),
    !is.na(ventas_total),
    !is.na(utilidad_operativa)
  ) %>%
  group_by(region, distribuidor) %>%
  summarise(
    ventas_totales = sum(ventas_total, na.rm = TRUE),
    utilidad_operativa_total = sum(utilidad_operativa, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  mutate(
    margen_operativo = ifelse(
      ventas_totales > 0,
      utilidad_operativa_total / ventas_totales,
      NA_real_
    )
  )

# =========================================================
# 2. Variable auxiliar para ordenar dentro de cada región
# =========================================================

ventas_region_distribuidor <- ventas_region_distribuidor %>%
  arrange(region, ventas_totales) %>%
  mutate(
    distribuidor_region = paste(distribuidor, region, sep = "___")
  )

niveles_ordenados <- ventas_region_distribuidor %>%
  arrange(region, ventas_totales) %>%
  pull(distribuidor_region)

ventas_region_distribuidor <- ventas_region_distribuidor %>%
  mutate(
    distribuidor_region = factor(distribuidor_region, levels = niveles_ordenados),
    etiqueta_ventas = paste0(round(ventas_totales / 1e6, 2), "M"),
    etiqueta_margen = percent(margen_operativo, accuracy = 0.1),
    x_etiqueta_ventas = ventas_totales + max(ventas_totales, na.rm = TRUE) * 0.015,
    x_etiqueta_margen = ventas_totales * 0.82
  )

# =========================================================
# 3. Gráfico horizontal con facet por región
# =========================================================

ggplot(
  ventas_region_distribuidor,
  aes(
    x = ventas_totales,
    y = distribuidor_region,
    fill = distribuidor
  )
) +
  geom_col(width = 0.65, show.legend = FALSE) +
  
  # Etiqueta interna: margen operativo
  geom_text(
    aes(
      x = x_etiqueta_margen,
      label = etiqueta_margen
    ),
    color = "white",
    size = 3.8,
    fontface = "bold"
  ) +
  
  # Etiqueta externa: ventas totales
  geom_text(
    aes(
      label = etiqueta_ventas,
      x = x_etiqueta_ventas
    ),
    hjust = 0,
    size = 3.8,
    color = "#303030",
    fontface = "bold"
  ) +
  
  facet_wrap(~ region, scales = "free_y", ncol = 2) +
  
  scale_y_discrete(
    labels = function(x) sub("___.*$", "", x)
  ) +
  
  scale_x_continuous(
    labels = label_number(scale = 1e-6, suffix = "M"),
    expand = expansion(mult = c(0, 0.14))
  ) +
  
  labs(
    title = "VENTAS TOTALES Y MARGENES POR DISTRIBUIDOR Y REGION",
    subtitle = "Etiqueta interna: margen operativo | Etiqueta externa: ventas totales",
    x = "Ventas totales",
    y = NULL
  ) +
  
  theme_minimal() +
  theme(
    plot.title = element_text(
      hjust = 0.5,
      size = 20,
      face = "bold",
      color = "#2F2F2F",
      margin = margin(b = 8)
    ),
    plot.subtitle = element_text(
      hjust = 0.5,
      size = 11,
      color = "#5A5A5A",
      margin = margin(b = 15)
    ),
    strip.text = element_text(
      size = 13,
      face = "bold",
      color = "#2F2F2F"
    ),
    axis.text.y = element_text(
      size = 10.5,
      color = "#4D4D4D"
    ),
    axis.text.x = element_text(
      size = 10.5,
      color = "#4D4D4D"
    ),
    axis.title.x = element_text(
      size = 12,
      color = "#2F2F2F"
    ),
    panel.grid.major.y = element_blank(),
    panel.grid.minor = element_blank(),
    panel.grid.major.x = element_line(color = "#D9D9D9"),
    panel.spacing = unit(1.1, "lines"),
    plot.background = element_rect(fill = "white", color = NA),
    panel.background = element_rect(fill = "white", color = NA),
    plot.margin = margin(t = 15, r = 35, b = 20, l = 20)
  )

Gráfico 5, En este gráfico se observa la distribucion de las ventas de cada distribuidor por región observando que:

El distribuidor West gear tiene presencia en las seis regiones, sin embargo, el 57,7% de sus ventas están centradas en la región West de donde es originario y en la cual se generan ganancias del 35.3%, llamando la atención de que en el restante, donde se encuentran segregados en proporciones menores los demás ingresos, sus margenes ponderados resultan más altos siendo south la region con el resultado más alto, 41.8%

Coherente con la participación global en regiones, se encuentra que Foot locker a diferencia de West Gear, tiene una distribución por regiones más segregada, siendo Northeast, southeast y Midwest las regiones en las cuales concentra alrededor del 78% de sus ventas con ganancias del 38.9% y 40%, respectivamente.

como tercero en el pareto de distribuidores, Sport Direct también muestra presencia en todas las regiones siendo South y southeast las de mayor participación con el 37.8% y 28.6% respectivamente. Por margenes operativos se observa que este distribuidor genera rendimientos sobre el 40% en cinco de las seis regiones, siendo south el mayor generador con un 48.4% y Northeast la excepción, puesto que, sus ganancias se encuentran sobre el 36%.

En cuanto a los distribuidores más pequeños en participación se refiere, como Walmart, amazon, y kohl´s, estos hacen presencia por lo menos en cinco de las seis regiones.

Como conclusión en este segmento, se destaca que la región de West es donde a nivel general los distribuidores presentan menores rendimientos, lo cual puede estar relacionado con el costo de vida, el cual es uno de los más altos de Estados Unidos.

br>

Ventas por distribuidor región y metodo

library(dplyr)
library(ggplot2)
library(scales)
library(grid)

# =========================================================
# 1. Resumen de ventas
# =========================================================

ventas_3d <- Caso_1 %>%
  mutate(
    region = as.character(region),
    distribuidor = as.character(distribuidor),
    metodo_venta = as.character(metodo_venta),
    ventas_total = as.numeric(ventas_total)
  ) %>%
  filter(
    !is.na(region),
    !is.na(distribuidor),
    !is.na(metodo_venta),
    !is.na(ventas_total)
  ) %>%
  group_by(region, distribuidor, metodo_venta) %>%
  summarise(
    ventas_totales = sum(ventas_total, na.rm = TRUE),
    .groups = "drop"
  )

# =========================================================
# 2. Ordenar distribuidores por ventas dentro de cada región
# =========================================================

orden_distribuidor <- ventas_3d %>%
  group_by(region, distribuidor) %>%
  summarise(
    total = sum(ventas_totales),
    .groups = "drop"
  ) %>%
  arrange(region, desc(total))

ventas_3d <- ventas_3d %>%
  mutate(
    distribuidor_region = paste(distribuidor, region, sep = "___")
  )

niveles <- orden_distribuidor %>%
  mutate(
    distribuidor_region = paste(distribuidor, region, sep = "___")
  ) %>%
  pull(distribuidor_region)

ventas_3d <- ventas_3d %>%
  mutate(
    distribuidor_region = factor(distribuidor_region, levels = niveles),
    etiqueta = paste0(round(ventas_totales / 1e6, 1), "M")
  )

# =========================================================
# 3. Colores por método de venta
# =========================================================

colores_metodo <- c(
  "In-store" = "#F28B82",
  "Online"   = "#22C55E",
  "Outlet"   = "#6495ED"
)

# =========================================================
# 4. Parámetros visuales
# =========================================================

dodge_barras <- 0.92
ancho_barras <- 0.50

# =========================================================
# 5. Gráfico final
# =========================================================

ggplot(
  ventas_3d,
  aes(
    x = distribuidor_region,
    y = ventas_totales,
    fill = metodo_venta
  )
) +
  
  geom_col(
    position = position_dodge(width = dodge_barras),
    width = ancho_barras
  ) +
  
  geom_text(
    aes(label = etiqueta),
    position = position_dodge(width = dodge_barras),
    vjust = -0.6,
    size = 2.6,
    color = "#555555"
  ) +
  
  facet_wrap(~ region, scales = "free_x", ncol = 3) +
  
  scale_fill_manual(values = colores_metodo) +
  
  scale_x_discrete(
    labels = function(x) sub("___.*$", "", x),
    expand = expansion(add = c(0.45, 0.75))
  ) +
  
  scale_y_continuous(
    labels = label_number(scale = 1e-6, suffix = "M"),
    expand = expansion(mult = c(0, 0.16))
  ) +
  
  labs(
    title = "VENTAS POR DISTRIBUIDOR, REGION Y METODO DE VENTA",
    x = NULL,
    y = "Ventas totales",
    fill = "Método de venta"
  ) +
  
  theme_minimal() +
  theme(
    plot.title = element_text(
      hjust = 0.5,
      size = 21,
      face = "bold",
      color = "#1F1F1F",
      margin = margin(b = 18)
    ),
    strip.text = element_text(
      size = 12,
      face = "bold",
      color = "#2F2F2F"
    ),
    axis.text.x = element_text(
      angle = 45,
      hjust = 1,
      size = 10.5,
      color = "#4D4D4D"
    ),
    axis.text.y = element_text(
      size = 10.5,
      color = "#4D4D4D"
    ),
    panel.grid.major.x = element_blank(),
    panel.grid.minor = element_blank(),
    panel.grid.major.y = element_line(color = "#D9D9D9"),
    panel.spacing = unit(1.5, "lines"),
    legend.position = "right",
    legend.title = element_text(size = 12),
    legend.text = element_text(size = 11),
    plot.background = element_rect(fill = "white", color = NA),
    panel.background = element_rect(fill = "white", color = NA),
    plot.margin = margin(t = 20, r = 65, b = 20, l = 20)
  )

Gráfico 6. En este gráfico se observa la composición de las ventas de cada distribuidor clasificadas por región y método de venta, observando que los métodos de venta de cada uno por región, no son iguales como por ejemplo Sport Direct, quien tiene en southeast concentración de ventas mayoritariamente Online, mientras que en South tiene tiendas físicas tipo outlet y una proporción más pequeña on line.

Lo anterior es consecuente con la distribución de ventas por método en cada región y distribuidor “graficos 2 y 4”, y que se explica por el enfoque de penetración de mercado que tiene cada distribuidor de acuerdo con el perfil de preferencias de su población objetivo.

Ventas por distribuidor y producto

Gráfico 7. En cuanto a la participación en ventas de cada producto, se observa que el comportamiento por distribuidor se mantiene de acuerdo al pareto descrito anteriormente, en el cual las ventas en el portafolio general, se encuentran concentradas principalmente en West Gear, Foot locker y Sports Direct, los cuales tienen en común, que son tiendas minoristas especializadas en calzado, ropa y accesorios deportivos con presencia internacional.

Análisis por Producto

library(dplyr)
library(ggplot2)
library(scales)

# 1. Preparar base y limpiar textos
datos_limpios <- Caso_1 %>%
  mutate(
    producto = enc2utf8(iconv(as.character(producto), from = "", to = "UTF-8", sub = "")),
    metodo_venta = enc2utf8(iconv(as.character(metodo_venta), from = "", to = "UTF-8", sub = "")),
    ventas_total = as.numeric(ventas_total),
    utilidad_operativa = as.numeric(utilidad_operativa)
  ) %>%
  filter(
    !is.na(producto),
    !is.na(metodo_venta),
    !is.na(ventas_total),
    !is.na(utilidad_operativa)
  )

# 2. Resumen por producto y metodo de venta
resumen_producto_metodo <- datos_limpios %>%
  group_by(producto, metodo_venta) %>%
  summarise(
    ventas_totales = sum(ventas_total, na.rm = TRUE),
    utilidad_operativa = sum(utilidad_operativa, na.rm = TRUE),
    .groups = "drop"
  )

# 3. Calcular margen operativo total por producto
orden_productos <- datos_limpios %>%
  group_by(producto) %>%
  summarise(
    ventas_totales_producto = sum(ventas_total, na.rm = TRUE),
    utilidad_operativa_producto = sum(utilidad_operativa, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  mutate(
    margen_operativo_total = ifelse(
      ventas_totales_producto > 0,
      utilidad_operativa_producto / ventas_totales_producto,
      NA_real_
    )
  ) %>%
  arrange(desc(margen_operativo_total))

# 4. Unir el margen total del producto al resumen por metodo
resumen_producto_metodo <- resumen_producto_metodo %>%
  left_join(
    orden_productos %>%
      select(producto, margen_operativo_total),
    by = "producto"
  ) %>%
  mutate(
    margen_operativo = ifelse(
      ventas_totales > 0,
      utilidad_operativa / ventas_totales,
      NA_real_
    ),
    etiqueta_ventas = label_number(scale = 1e-6, suffix = "M", accuracy = 0.01)(ventas_totales),
    etiqueta_margen = percent(margen_operativo, accuracy = 0.1),
    metodo_venta = factor(
      metodo_venta,
      levels = c("In-store", "Online", "Outlet")
    ),
    producto = factor(
      producto,
      levels = orden_productos$producto
    )
  )

# 5. Factor de escala para superponer margen sobre ventas
factor_escala <- max(resumen_producto_metodo$ventas_totales, na.rm = TRUE)

# 6. Grafico combinado ordenado por margen operativo total del producto
ggplot(resumen_producto_metodo, aes(x = metodo_venta)) +
  geom_col(
    aes(y = ventas_totales, fill = metodo_venta),
    alpha = 0.85,
    width = 0.65
  ) +
  geom_text(
    aes(y = ventas_totales, label = etiqueta_ventas),
    vjust = -0.35,
    size = 3.2
  ) +
  geom_line(
    aes(y = margen_operativo * factor_escala, group = 1),
    color = "red",
    linewidth = 1
  ) +
  geom_point(
    aes(y = margen_operativo * factor_escala),
    color = "red",
    size = 2.8
  ) +
  geom_text(
    aes(
      y = margen_operativo * factor_escala,
      label = etiqueta_margen
    ),
    color = "red",
    vjust = -1.1,
    size = 3.1
  ) +
  facet_wrap(~ producto, scales = "free_y") +
  scale_fill_manual(
    values = c(
      "In-store" = "#F8766D",
      "Online"   = "#00BA38",
      "Outlet"   = "#619CFF"
    )
  ) +
  scale_y_continuous(
    name = "Ventas totales",
    labels = label_number(scale = 1e-6, suffix = "M"),
    expand = expansion(mult = c(0.05, 0.18)),
    sec.axis = sec_axis(
      ~ . / factor_escala,
      name = "Margen operativo",
      labels = percent_format(accuracy = 0.1)
    )
  ) +
  labs(
    title = "Ventas totales y margen operativo por producto y metodo de venta",
    subtitle = "Productos ordenados de mayor a menor segun margen operativo total",
    x = "Metodo de venta",
    fill = "Metodo de venta"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    legend.position = "none",
    plot.title = element_text(face = "bold", size = 15, hjust = 0.5),
    plot.subtitle = element_text(size = 11, hjust = 0.5),
    strip.text = element_text(face = "bold"),
    axis.text.x = element_text(angle = 25, hjust = 1)
  )

En el gráfico 8, se observan las ventas por producto con los margenes de rentabilidad ponderados, en los cuales se evidencia que a nivel general, el margen de rentabilidad más alto por método, se obtiene a través de ventas online, determinando así, que este método ha sido eficiente desde lo financiero para el total del portafolio, debido como se mencionó, a la optimización de costos tanto operativos, logisticos y de ventas que permite lograr la comercialización en linea de productos, lo cual es un factor diferenciador frente a la estructura de costos y gastos que se generan con la base instalada de tiendas físicas.

Por producto se puede observar como hallazgo relevante que:

El producto con mayores margenes en los tres métodos, es el Men´s street foot wear con el 45.5% online, 40,6% outlet y 39.2% In-Store, lo cual sugiere que estos productos podrían representar líneas estratégicas dentro del portafolio comercial de la empresa.

Men´s Athelic foot wear tiene a nivel general los margenes de rentabilidad más bajos a nivel general con 41,2% online, 33.2% In-store y 33.1% en Outlet.

Lo anterior es consecuente con los margenes generales ponderados que se observaron en la tabla 1, en donde, el producto con mejor desempeño en los tres métodos tiene un margen operativo total del 42%, mientras que el de menor desempeño, presenta un margen general del 36%.

Estos hallazgos pueden servir como base para definir estrategias comerciales orientadas a fortalecer los canales más rentables, optimizar la estructura de costos en los canales físicos y priorizar aquellos productos que generan mayor valor para la organización.

A continuación se realiza el análisis estadístico de margenes operativos de cada producto para comprender mejor los resultados observados anteriormente:

ANÁLISIS FINANCIERO-ESTADÍSTICO

En este punto, se abordarán desde el enfoque estadístico, las variables priorizadas para el análisis como lo son los productos, precios, margenes, distribuidores, regiones y métodos de venta, con el objetivo de establecer cómo el comportamiento individual y/o correlacionado de cada una, impacta sobre el desempeño financiero del portafolio de productos y de la compañía en general.

Distribución Margenes de rentabilidad por Región y Método

library(dplyr)
library(scales)
library(plotly)

# 1. Preparar datos
datos_box <- Caso_1 %>%
  rename(
    region_graf = region,
    metodo_venta_graf = metodo_venta,
    margen_operativo_graf = margen_operativo
  ) %>%
  filter(
    !is.na(region_graf),
    !is.na(metodo_venta_graf),
    !is.na(margen_operativo_graf)
  ) %>%
  mutate(
    region_graf = enc2utf8(iconv(as.character(region_graf), from = "", to = "UTF-8", sub = "")),
    metodo_venta_graf = enc2utf8(iconv(as.character(metodo_venta_graf), from = "", to = "UTF-8", sub = "")),
    margen_operativo_graf = as.numeric(margen_operativo_graf)
  ) %>%
  mutate(
    metodo_venta_graf = factor(
      metodo_venta_graf,
      levels = c("In-store", "Online", "Outlet")
    )
  ) %>%
  filter(!is.na(metodo_venta_graf)) %>%
  mutate(
    region_chr = as.character(region_graf),
    metodo_chr = as.character(metodo_venta_graf),
    texto_tooltip = paste0(
      "Region: ", region_chr,
      "<br>Metodo de venta: ", metodo_chr,
      "<br>Margen operativo: ", percent(margen_operativo_graf, accuracy = 0.1)
    )
  )

# 2. Colores fijos por metodo
colores_metodo <- c(
  "In-store" = "#f8766d",
  "Online"   = "#00BA38",
  "Outlet"   = "#619CFF"
)

# 3. Calcular medianas por region y metodo
medianas_box <- datos_box %>%
  group_by(region_graf, metodo_venta_graf) %>%
  summarise(
    mediana_margen = median(margen_operativo_graf, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  mutate(
    region_chr = as.character(region_graf),
    metodo_chr = as.character(metodo_venta_graf),
    texto_mediana = paste0(
      "Region: ", region_chr,
      "<br>Metodo de venta: ", metodo_chr,
      "<br>Mediana: ", percent(mediana_margen, accuracy = 0.1)
    )
  )

# 4. Orden de regiones
regiones <- unique(as.character(datos_box$region_graf))

# 5. Crear subgraficos por region
graficos_region <- lapply(regiones, function(reg) {
  
  datos_reg <- datos_box %>% filter(as.character(region_graf) == reg)
  medianas_reg <- medianas_box %>% filter(as.character(region_graf) == reg)
  
  p <- plot_ly()
  
  for (met in levels(datos_box$metodo_venta_graf)) {
    
    datos_met <- datos_reg %>% filter(as.character(metodo_venta_graf) == met)
    
    if (nrow(datos_met) > 0) {
      p <- p %>%
        add_trace(
          data = datos_met,
          x = ~metodo_venta_graf,
          y = ~margen_operativo_graf,
          type = "box",
          name = met,
          marker = list(color = colores_metodo[met]),
          line = list(color = colores_metodo[met]),
          fillcolor = colores_metodo[met],
          opacity = 0.45,
          boxpoints = FALSE,
          showlegend = FALSE,
          hoverinfo = "skip"
        ) %>%
        add_markers(
          data = datos_met,
          x = ~metodo_venta_graf,
          y = ~margen_operativo_graf,
          text = ~texto_tooltip,
          hoverinfo = "text",
          marker = list(
            size = 5,
            opacity = 0.30,
            color = colores_metodo[met]
          ),
          showlegend = FALSE
        )
    }
  }
  
  # Agregar puntos de mediana
  if (nrow(medianas_reg) > 0) {
    p <- p %>%
      add_markers(
        data = medianas_reg,
        x = ~metodo_venta_graf,
        y = ~mediana_margen,
        text = ~texto_mediana,
        hoverinfo = "text",
        marker = list(
          size = 10,
          color = "red",
          line = list(color = "white", width = 1.5)
        ),
        showlegend = FALSE
      )
  }
  
  p %>%
    layout(
      xaxis = list(
        title = "",
        categoryorder = "array",
        categoryarray = c("In-store", "Online", "Outlet")
      ),
      yaxis = list(
        title = "Margen operativo",
        tickformat = ".0%"
      ),
      annotations = list(
        list(
          text = reg,
          x = 0.5,
          y = 1.08,
          xref = "paper",
          yref = "paper",
          showarrow = FALSE,
          font = list(size = 14, family = "Arial", color = "black")
        )
      )
    )
})

# 6. Unir paneles
subplot(
  graficos_region,
  nrows = 2,
  shareY = TRUE,
  titleX = TRUE,
  titleY = TRUE,
  margin = 0.08
) %>%
  layout(
    title = list(
      text = "Distribucion del margen operativo por metodo de venta",
      x = 0.5,
      y = 0.95,
      font = list(size = 22)
    ),
    margin = list(
      t = 100,
      b = 50,
      l = 60,
      r = 40
    )
  )

Gráfico 9. Por otra parte, para complementar el análisis realizado en el gráfico anterior, en el gráfico 3 se puede confirmar que el método con mejor margen de rentabilidad es el On line, donde su mediana se encuentra en el 48% para Northeast, 49% en South, 44% en west, 47% en Midwest, y 45% en southeast; no obstante, se observan dispersiones importantes a nivel general respecto de la mediana , con unos mínimos y máximos significativamente alejados como por ejemplo, en south donde se registran márgenes mínimos del 25% y máximos del 80%.

En cuanto a los métodos outlet e In-Store estos presentan una mediana a nivel general de entre el 35%- 38% y del 35% respectivamente, sin embargo, en el caso de la región south para ambos métodos esta resulta mayor con el 44% y 40%.

En ese orden de ideas seria conveniente revisar de manera más detallada, la variabilidad observada para…….


library(dplyr)
library(ggplot2)
library(plotly)
library(stringi)

# =========================================================
# 0. Limpieza UTF-8
# =========================================================

names(Caso_1) <- stringi::stri_enc_toutf8(names(Caso_1))

Caso_1 <- Caso_1 %>%
  mutate(
    across(where(is.character), ~ stringi::stri_enc_toutf8(as.character(.x)))
  )

# =========================================================
# 1. Preparar datos
# =========================================================

datos_precio <- Caso_1 %>%
  filter(
    !is.na(producto),
    !is.na(metodo_venta),
    !is.na(precio_unidad)
  ) %>%
  mutate(
    producto = as.character(producto),
    metodo_venta = as.character(metodo_venta),
    precio_unidad = as.numeric(precio_unidad)
  )

# =========================================================
# 2. Ordenar productos por mediana
# =========================================================

orden_productos <- datos_precio %>%
  group_by(producto) %>%
  summarise(
    mediana_precio = median(precio_unidad, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  arrange(desc(mediana_precio)) %>%
  pull(producto)

datos_precio <- datos_precio %>%
  mutate(
    producto = factor(producto, levels = orden_productos),
    metodo_venta = factor(
      metodo_venta,
      levels = c("In-store", "Online", "Outlet")
    )
  )

# =========================================================
# 3. Crear gráfico ggplot (con tooltip limpio)
# =========================================================

grafico_base <- ggplot(
  datos_precio,
  aes(
    x = metodo_venta,
    y = precio_unidad,
    fill = metodo_venta,
    text = paste0(
      "Producto: ", producto,
      "<br>Metodo: ", metodo_venta,
      "<br>Precio: ", round(precio_unidad, 2)
    )
  )
) +
  geom_boxplot(
    alpha = 0.65,
    outlier.alpha = 0.8,
    width = 0.55
  ) +
  facet_wrap(~ producto, ncol = 2) +
  labs(
    title = "Distribucion del Precio por Unidad por Producto y Metodo de Venta",
    x = "Metodo de Venta",
    y = "Precio"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    legend.position = "none",
    strip.text = element_text(size = 11, face = "bold"),
    plot.title = element_text(size = 16, hjust = 0.5, face = "bold"),
    panel.spacing = unit(0.9, "lines")
  )

# =========================================================
# 4. Convertir a interactivo (CLAVE)
# =========================================================

ggplotly(grafico_base, tooltip = "text")
library(dplyr)
library(ggplot2)
library(plotly)

# =========================================================
# 1. Preparar datos (LIMPIEZA UTF-8)
# =========================================================

datos_box <- Caso_1 %>%
  filter(
    !is.na(producto),
    !is.na(metodo_venta),
    !is.na(margen_operativo)
  ) %>%
  mutate(
    producto = enc2utf8(iconv(as.character(producto), from = "", to = "UTF-8", sub = "")),
    metodo_venta = enc2utf8(iconv(as.character(metodo_venta), from = "", to = "UTF-8", sub = "")),
    margen_operativo = as.numeric(margen_operativo)
  )

# =========================================================
# 2. Ranking por mediana
# =========================================================

orden_productos <- datos_box %>%
  group_by(producto) %>%
  summarise(
    mediana_margen = median(margen_operativo, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  arrange(desc(mediana_margen)) %>%
  pull(producto)

# =========================================================
# 3. Aplicar orden
# =========================================================

datos_box <- datos_box %>%
  mutate(
    producto = factor(producto, levels = orden_productos),
    metodo_chr = as.character(metodo_venta),
    producto_chr = as.character(producto)
  )

# =========================================================
# 4. Grafico base (SIN tildes)
# =========================================================

grafico_base <- ggplot(
  datos_box,
  aes(
    x = metodo_venta,
    y = margen_operativo,
    fill = metodo_venta,
    text = paste0(
      "Producto: ", producto_chr,
      "<br>Metodo: ", metodo_chr,
      "<br>Margen: ", round(margen_operativo, 3)
    )
  )
) +
  geom_boxplot(outlier.color = "red", alpha = 0.7) +
  facet_wrap(~ producto, scales = "free_y") +
  labs(
    title = "Distribucion del Margen Operativo por Producto y Metodo de Venta",
    subtitle = "Productos ordenados de mayor a menor segun la mediana del margen",
    x = "Metodo de venta",
    y = "Margen operativo"
  ) +
  theme_minimal() +
  theme(
    legend.position = "none",
    strip.text = element_text(size = 12, face = "bold"),
    plot.title = element_text(hjust = 0.5, size = 16),
    plot.subtitle = element_text(hjust = 0.5, size = 12)
  )

# =========================================================
# 5. Convertir a plotly (estable)
# =========================================================

ggplotly(grafico_base, tooltip = "text")

Análisis de margen operativo por producto y método:

**Men´s Street Foot wear:

Mediana general 45%

Comportamiento por métodos In-store: Mediana 40%, Minimo 30% Máximo 50%, comportamiento estable, lo que implica poca variabilidad respecto a márgenes rentabilidad en el proceso de fijación de precios.

Online: Mediana 50% con tendencia hacia el Q3, Mínimo 30% Máximo 70%, Margenes de rentabilidad variables lo que indica variabilidad en precios, entre distribuidores, zonas y/o estados.

Outlet: Mediana 42%, Mínimo 25% Máximo 62%; Margenes de rentabilidad variables, lo que implica posible variabilidad de precios entre distribuidores, zonas y/o estados.

** Women´s apparel:

In-store: Mediana 40% sobre el Q3, Minimo 15% Máximo 55%, margenes relativamente estables entre la mediana y el máximo, sin embargo, se evidencian picos muy bajos que están afectando negativamente, por ende, convendría revisar el comportamiento de precios entre distribuidores y zonas.

Online: Mediana 50%, Mínimo 21% Máximo 80%: Volatilidad marcada entre márgenes de rentabilidad mínimos y máximos los cuales se encuentran entre los bigotes es decir, el 50% de las ventas presentan una variabilidad significativa de precios.

Outlet: Mediana 40%, Mínimo 10% Máximo 72%: Volatilidad marcada entre márgenes de rentabilidad mínimos y máximos los cuales se encuentran entre los bigotes es decir, el 50% de las ventas presentan una variabilidad significativa de precios hacia los extremos.

**Women´s Athetic footwear:

In-Store: Mediana 35%, Mínimo 25% Máximo 45%; márgenes con comportamiento moderado

Online: Mediana 46%, Mínimo 30% Máximo 75%; Variabilidad en márgenes de rentabilidad, lo que implica variación en precios entre distribuidores; el máximo registrado representa un dato atípico.

Outlet: Mediana 40%, Mínimo 25% Máximo 67%: Tiende a la variabilidad, por ende, es posible diversidad de precios entre distribuidores.

** Men´s Apparel

In-store: Mediana 35%, Mínimo 20% Máximo 65%: Presenta variabilidad

Online: Mediana 45%, Mínimo 25% Máximo 77%: Presenta variabilidad, aunque el máximo corresponde a dato atípico al situarse por fuera del bigote superior.

Outlet: Mediana 37%, Mínimo 15% Máximo 66%: los picos altos y bajos se encuentran fuera de los bigotes, por ende, se consideran datos atípicos; no obstante tiende a la variabilidad entre los bigotes con margenes entre el 20%-32% inferior y superior 43-59%.

**Men´s Athletic footwear

In-store: Mediana 35%, Mínimo 20% Máximo 45%: Poca variabilidad en términos generales, aunque presenta datos atípicos correspondientes a los puntos altos y bajos registrados.

Online: Mediana 46%, Mínimo 25% Máximo 65%:Presenta variabilidad; los altos y bajos corresponden a datos atípicos.

Outlet: Mediana 38%, Mínimo 15% Máximo 55: Presenta variabilidad marcada entre márgenes de rentabilidad mínimos y máximos los cuales se encuentran entre los bigotes, es decir, el 50% de las ventas presentan una variabilidad significativa de precios y/o costos.

** Women´s stret footwear

In-store: Mediana 35%, Mínimo 25% Máximo 50%: poca variabilidad; el margen máximo corresponde a un dato atípico.

Online: Mediana 46%, Mínimo 25% Máximo 64%: Alta variabilidad

Outlet: Mediana 40%, Mínimo 25% Máximo 55%: Alta variabilidad; tiene datos atípicos del 56%.

library(dplyr)
library(ggplot2)

# =========================================================
# 1. Preparar datos (asegurar tipo numérico)
# =========================================================

datos <- Caso_1 %>%
  mutate(
    precio_unidad = as.numeric(precio_unidad)
  ) %>%
  filter(
    !is.na(producto),
    !is.na(region),
    !is.na(precio_unidad)
  )

# =========================================================
# 2. Calcular variabilidad (CV) por producto y región
# =========================================================

variacion_region <- datos %>%
  group_by(producto, region) %>%
  summarise(
    media = mean(precio_unidad, na.rm = TRUE),
    sd = sd(precio_unidad, na.rm = TRUE),
    cv = ifelse(media > 0, sd / media, NA_real_),
    .groups = "drop"
  ) %>%
  filter(!is.na(cv))

# =========================================================
# 3. Ranking de regiones (CV promedio)
# =========================================================

ranking_region <- variacion_region %>%
  group_by(region) %>%
  summarise(
    cv_promedio = mean(cv, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  arrange(desc(cv_promedio))

# =========================================================
# 4. Gráfico ranking (CLARO Y EJECUTIVO)
# =========================================================

ggplot(
  ranking_region,
  aes(
    x = reorder(region, cv_promedio),
    y = cv_promedio
  )
) +
  geom_col(fill = "#2E86C1") +
  coord_flip() +
  geom_text(
    aes(label = round(cv_promedio, 2)),
    hjust = -0.2,
    size = 4
  ) +
  labs(
    title = "Ranking de Variabilidad de Precios por Region",
    subtitle = "Mayor valor indica mayor dispersion de precios",
    x = "Region",
    y = "CV promedio"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    plot.subtitle = element_text(hjust = 0.5)
  ) +
  ylim(0, max(ranking_region$cv_promedio) * 1.15)

library(dplyr)
library(ggplot2)
library(stringi)

# =========================================================
# 0. Limpieza basica
# =========================================================

names(Caso_1) <- stringi::stri_enc_toutf8(names(Caso_1))

Caso_1 <- Caso_1 %>%
  mutate(
    across(where(is.character), ~ stringi::stri_enc_toutf8(as.character(.x))),
    precio_unidad = as.numeric(precio_unidad)
  )

# =========================================================
# 1. Calcular variabilidad por producto y region
# =========================================================

variacion_region <- Caso_1 %>%
  filter(
    !is.na(producto),
    !is.na(region),
    !is.na(precio_unidad)
  ) %>%
  group_by(producto, region) %>%
  summarise(
    media = mean(precio_unidad, na.rm = TRUE),
    sd = sd(precio_unidad, na.rm = TRUE),
    cv = ifelse(media > 0, sd / media, NA_real_),
    .groups = "drop"
  ) %>%
  filter(!is.na(cv))

# =========================================================
# 2. Ordenar productos por CV promedio
# =========================================================

orden_productos <- variacion_region %>%
  group_by(producto) %>%
  summarise(
    cv_promedio = mean(cv, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  arrange(desc(cv_promedio)) %>%
  pull(producto)

variacion_region <- variacion_region %>%
  mutate(
    producto = factor(producto, levels = orden_productos)
  )

# =========================================================
# 3. Grafico de puntos por region, facet por producto
# =========================================================

ggplot(
  variacion_region,
  aes(
    x = cv,
    y = reorder(region, cv),
    color = region
  )
) +
  geom_point(size = 3) +
  geom_segment(
    aes(
      x = 0,
      xend = cv,
      y = region,
      yend = region
    ),
    color = "gray75",
    linewidth = 0.6,
    show.legend = FALSE
  ) +
  geom_text(
    aes(label = round(cv, 2)),
    hjust = -0.25,
    size = 3.5,
    show.legend = FALSE
  ) +
  facet_wrap(~ producto, ncol = 2, scales = "free_y") +
  labs(
    title = "Variabilidad del precio por region y producto",
    subtitle = "Coeficiente de variacion (CV): mas a la derecha = mayor dispersion",
    x = "CV del precio",
    y = "Region"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    legend.position = "none",
    plot.title = element_text(face = "bold", hjust = 0.5),
    plot.subtitle = element_text(hjust = 0.5),
    strip.text = element_text(face = "bold", size = 11)
  ) +
  expand_limits(x = max(variacion_region$cv) * 1.12)

jjkj

library(dplyr)
library(ggplot2)
library(stringi)

# =========================================================
# 0. Limpieza
# =========================================================

names(Caso_1) <- stringi::stri_enc_toutf8(names(Caso_1))

datos <- Caso_1 %>%
  mutate(
    across(where(is.character), ~ stringi::stri_enc_toutf8(as.character(.x))),
    precio_unidad = as.numeric(precio_unidad)
  ) %>%
  filter(
    !is.na(producto),
    !is.na(distribuidor),
    !is.na(metodo_venta),
    !is.na(precio_unidad)
  )

# =========================================================
# 1. Ordenar distribuidores por precio promedio
# =========================================================

orden_dist <- datos %>%
  group_by(distribuidor) %>%
  summarise(
    precio_promedio = mean(precio_unidad, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  arrange(desc(precio_promedio)) %>%
  pull(distribuidor)

datos <- datos %>%
  mutate(
    distribuidor = factor(distribuidor, levels = orden_dist)
  )

# =========================================================
# 2. Boxplot
# =========================================================

ggplot(
  datos,
  aes(
    x = distribuidor,
    y = precio_unidad,
    fill = metodo_venta
  )
) +
  geom_boxplot(
    alpha = 0.7,
    outlier.alpha = 0.6
  ) +
  facet_wrap(~ producto, ncol = 2) +
  labs(
    title = "Distribucion del precio por producto, distribuidor y metodo de venta",
    subtitle = "Permite evaluar nivel de precio, dispersion y consistencia",
    x = "Distribuidor",
    y = "Precio por unidad",
    fill = "Metodo de venta"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    strip.text = element_text(face = "bold"),
    plot.title = element_text(face = "bold", hjust = 0.5),
    plot.subtitle = element_text(hjust = 0.5)
  )

gggssges

library(dplyr)
library(knitr)
library(kableExtra)

# =========================================================
# 1. Preparar datos
# =========================================================

datos <- Caso_1 %>%
  mutate(
    precio_unidad = as.numeric(precio_unidad),
    margen_operativo = as.numeric(margen_operativo)
  ) %>%
  filter(
    !is.na(producto),
    !is.na(distribuidor),
    !is.na(precio_unidad),
    !is.na(margen_operativo)
  )

# =========================================================
# 2. Resumen por producto y distribuidor
# =========================================================

resumen_dist <- datos %>%
  group_by(producto, distribuidor) %>%
  summarise(
    n = n(),
    precio_promedio = mean(precio_unidad, na.rm = TRUE),
    margen_promedio = mean(margen_operativo, na.rm = TRUE),
    sd_precio = sd(precio_unidad, na.rm = TRUE),
    cv_precio = ifelse(precio_promedio > 0, sd_precio / precio_promedio, NA_real_),
    .groups = "drop"
  ) %>%
  filter(!is.na(cv_precio))

# =========================================================
# 3. Clasificacion tipo semaforo dentro de cada producto
# =========================================================

resumen_dist <- resumen_dist %>%
  group_by(producto) %>%
  mutate(
    margen_p50 = median(margen_promedio, na.rm = TRUE),
    cv_p50 = median(cv_precio, na.rm = TRUE),
    clasificacion = case_when(
      margen_promedio >= margen_p50 & cv_precio <= cv_p50 ~ "Eficiente",
      margen_promedio < margen_p50 & cv_precio > cv_p50 ~ "Problematico",
      TRUE ~ "Medio"
    )
  ) %>%
  ungroup()

# =========================================================
# 4. Tabla semaforo con HTML correcto
# =========================================================

tabla_semaforo <- resumen_dist %>%
  mutate(
    precio_promedio = round(precio_promedio, 2),
    margen_promedio = round(margen_promedio, 3),
    cv_precio = round(cv_precio, 3),
    clasificacion = case_when(
      clasificacion == "Eficiente" ~ cell_spec(
        "Eficiente",
        format = "html",
        bold = TRUE,
        color = "white",
        background = "#1E8449"
      ),
      clasificacion == "Medio" ~ cell_spec(
        "Medio",
        format = "html",
        bold = TRUE,
        color = "black",
        background = "#F4D03F"
      ),
      clasificacion == "Problematico" ~ cell_spec(
        "Problematico",
        format = "html",
        bold = TRUE,
        color = "white",
        background = "#C0392B"
      )
    )
  ) %>%
  select(
    producto,
    distribuidor,
    n,
    precio_promedio,
    margen_promedio,
    cv_precio,
    clasificacion
  )

tabla_semaforo %>%
  kbl(
    format = "html",
    caption = "Clasificacion tipo semaforo por producto y distribuidor",
    align = "c",
    escape = FALSE
  ) %>%
  kable_styling(
    full_width = FALSE,
    bootstrap_options = c("striped", "hover", "condensed", "responsive")
  ) %>%
  row_spec(0, bold = TRUE, color = "white", background = "#2C3E50")
Clasificacion tipo semaforo por producto y distribuidor
producto distribuidor n precio_promedio margen_promedio cv_precio clasificacion
Men’s Apparel Amazon 157 54.88 0.371 0.172 Medio
Men’s Apparel Foot Locker 433 50.61 0.393 0.218 Medio
Men’s Apparel Kohl’s 171 49.44 0.463 0.185 Eficiente
Men’s Apparel Sports Direct 339 46.94 0.396 0.235 Medio
Men’s Apparel Walmart 113 49.45 0.341 0.351 Problematico
Men’s Apparel West Gear 393 51.73 0.466 0.299 Medio
Men’s Athletic Footwear Amazon 159 47.16 0.439 0.198 Eficiente
Men’s Athletic Footwear Foot Locker 442 43.07 0.400 0.265 Eficiente
Men’s Athletic Footwear Kohl’s 172 43.47 0.385 0.239 Medio
Men’s Athletic Footwear Sports Direct 337 39.99 0.433 0.266 Medio
Men’s Athletic Footwear Walmart 104 47.30 0.363 0.369 Problematico
Men’s Athletic Footwear West Gear 396 45.65 0.384 0.360 Problematico
Men’s Street Footwear Amazon 159 50.99 0.452 0.223 Eficiente
Men’s Street Footwear Foot Locker 449 45.14 0.447 0.279 Medio
Men’s Street Footwear Kohl’s 172 42.28 0.458 0.250 Eficiente
Men’s Street Footwear Sports Direct 335 39.39 0.455 0.341 Medio
Men’s Street Footwear Walmart 101 47.00 0.417 0.349 Problematico
Men’s Street Footwear West Gear 394 44.74 0.437 0.350 Problematico
Women’s Apparel Amazon 159 54.21 0.447 0.242 Medio
Women’s Apparel Foot Locker 433 51.20 0.450 0.299 Eficiente
Women’s Apparel Kohl’s 172 51.97 0.343 0.264 Medio
Women’s Apparel Sports Direct 341 47.77 0.512 0.304 Medio
Women’s Apparel Walmart 107 53.92 0.482 0.363 Medio
Women’s Apparel West Gear 396 53.50 0.400 0.375 Problematico
Women’s Athletic Footwear Amazon 158 43.73 0.389 0.223 Medio
Women’s Athletic Footwear Foot Locker 438 40.13 0.419 0.299 Problematico
Women’s Athletic Footwear Kohl’s 171 40.37 0.425 0.238 Eficiente
Women’s Athletic Footwear Sports Direct 338 39.85 0.460 0.272 Eficiente
Women’s Athletic Footwear Walmart 102 41.73 0.433 0.421 Medio
Women’s Athletic Footwear West Gear 395 42.51 0.411 0.394 Problematico
Women’s Street Footwear Amazon 157 41.55 0.408 0.264 Medio
Women’s Street Footwear Foot Locker 438 38.82 0.398 0.343 Problematico
Women’s Street Footwear Kohl’s 172 40.12 0.441 0.288 Eficiente
Women’s Street Footwear Sports Direct 342 38.29 0.413 0.335 Eficiente
Women’s Street Footwear Walmart 99 43.01 0.407 0.431 Problematico
Women’s Street Footwear West Gear 400 42.36 0.409 0.428 Medio

gfkgkgkgkg

library(dplyr)
library(ggplot2)

# =========================================================
# 1. Preparar datos
# =========================================================

datos <- Caso_1 %>%
  mutate(
    precio_unidad = as.numeric(precio_unidad)
  ) %>%
  filter(
    !is.na(distribuidor),
    !is.na(producto),
    !is.na(precio_unidad)
  )

# =========================================================
# 2. Calcular CV por producto y distribuidor
# =========================================================

cv_producto_dist <- datos %>%
  group_by(distribuidor, producto) %>%
  summarise(
    media = mean(precio_unidad, na.rm = TRUE),
    sd = sd(precio_unidad, na.rm = TRUE),
    cv = ifelse(media > 0, sd / media, NA_real_),
    .groups = "drop"
  ) %>%
  filter(!is.na(cv))

# =========================================================
# 3. CV global por distribuidor + clasificacion
# =========================================================

cv_global <- cv_producto_dist %>%
  group_by(distribuidor) %>%
  summarise(
    cv_global = mean(cv, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  mutate(
    clasificacion = case_when(
      cv_global < 0.25 ~ "Estable",
      cv_global < 0.35 ~ "Moderado",
      TRUE ~ "Critico"
    )
  ) %>%
  arrange(desc(cv_global))

# =========================================================
# 4. Grafico de barras horizontal con semaforo
# =========================================================

ggplot(
  cv_global,
  aes(
    x = reorder(distribuidor, cv_global),
    y = cv_global,
    fill = clasificacion
  )
) +
  geom_col() +
  coord_flip() +
  geom_text(
    aes(label = round(cv_global, 3)),
    hjust = -0.2,
    size = 4
  ) +
  scale_fill_manual(
    values = c(
      "Estable" = "#1E8449",
      "Moderado" = "#F4D03F",
      "Critico" = "#C0392B"
    )
  ) +
  labs(
    title = "Variabilidad global del precio por distribuidor",
    subtitle = "Coeficiente de variacion (CV promedio)",
    x = "Distribuidor",
    y = "CV global",
    fill = "Clasificacion"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5),
    plot.subtitle = element_text(hjust = 0.5)
  ) +
  ylim(0, max(cv_global$cv_global) * 1.15)

efssgges

library(dplyr)
library(ggplot2)
library(scales)

# =========================================================
# 1. Preparar datos
# =========================================================

datos_box <- Caso_1 %>%
  mutate(
    producto = as.character(producto),
    region = as.character(region),
    metodo_venta = as.character(metodo_venta),
    margen_operativo = as.numeric(margen_operativo)
  ) %>%
  filter(
    !is.na(producto),
    !is.na(region),
    !is.na(metodo_venta),
    !is.na(margen_operativo)
  )

# =========================================================
# 2. Ordenar productos por mediana global
# =========================================================

orden_productos <- datos_box %>%
  group_by(producto) %>%
  summarise(
    mediana = median(margen_operativo, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  arrange(desc(mediana)) %>%
  pull(producto)

datos_box <- datos_box %>%
  mutate(
    producto = factor(producto, levels = orden_productos)
  )

# =========================================================
# 3. Colores por método
# =========================================================

colores_metodo <- c(
  "In-store" = "#F28B82",
  "Online"   = "#22C55E",
  "Outlet"   = "#6495ED"
)

# =========================================================
# 4. Boxplot
# =========================================================

ggplot(
  datos_box,
  aes(
    x = producto,
    y = margen_operativo,
    fill = metodo_venta
  )
) +
  
  geom_boxplot(
    position = position_dodge(width = 0.75),
    outlier.alpha = 0.5,
    width = 0.65
  ) +
  
  # Mediana destacada (opcional)
  stat_summary(
    fun = median,
    geom = "point",
    position = position_dodge(width = 0.75),
    color = "red",
    size = 1.8
  ) +
  
  facet_wrap(~ region, ncol = 3) +
  
  scale_fill_manual(values = colores_metodo) +
  
  scale_y_continuous(
    labels = percent_format(accuracy = 1)
  ) +
  
  labs(
    title = "DISTRIBUCIÓN DEL MARGEN OPERATIVO",
    subtitle = "Por producto, región y método de venta",
    x = NULL,
    y = "Margen operativo",
    fill = "Método de venta"
  ) +
  
  theme_minimal() +
  theme(
    plot.title = element_text(
      hjust = 0.5,
      size = 20,
      face = "bold"
    ),
    plot.subtitle = element_text(
      hjust = 0.5,
      size = 12,
      color = "#555555"
    ),
    axis.text.x = element_text(
      angle = 45,
      hjust = 1,
      size = 10
    ),
    panel.grid.major.x = element_blank(),
    panel.spacing = unit(1.3, "lines"),
    legend.position = "bottom"
  )

library(dplyr)
library(ggplot2)
library(scales)

# =========================================================
# 1. Preparar datos
# =========================================================

datos_box_precio <- Caso_1 %>%
  filter(
    !is.na(producto),
    !is.na(region),
    !is.na(metodo_venta),
    !is.na(precio_unidad)
  ) %>%
  mutate(
    producto = enc2utf8(as.character(producto)),
    region = enc2utf8(as.character(region)),
    metodo_venta = enc2utf8(as.character(metodo_venta)),
    precio_unidad = as.numeric(precio_unidad)
  )

# =========================================================
# 2. Orden global de productos por mediana de precio
# =========================================================

orden_productos <- datos_box_precio %>%
  group_by(producto) %>%
  summarise(
    mediana_global = median(precio_unidad, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  arrange(desc(mediana_global)) %>%
  pull(producto)

datos_box_precio <- datos_box_precio %>%
  mutate(producto = factor(producto, levels = orden_productos))

# =========================================================
# 3. Boxplot en estilo R Markdown / HTML
# =========================================================

grafico_precio_box <- ggplot(
  datos_box_precio,
  aes(
    x = producto,
    y = precio_unidad,
    fill = metodo_venta
  )
) +
  geom_boxplot(
    width = 0.72,
    outlier.alpha = 0.65,
    outlier.size = 1.8,
    position = position_dodge(width = 0.8)
  ) +
  
  # Punto de mediana
  stat_summary(
    fun = median,
    geom = "point",
    aes(group = metodo_venta),
    position = position_dodge(width = 0.8),
    color = "red",
    size = 2
  ) +
  
  facet_wrap(~ region, ncol = 3) +
  
  scale_y_continuous(
    labels = dollar_format(prefix = "$"),
    expand = expansion(mult = c(0.05, 0.08))
  ) +
  
  scale_fill_manual(
    values = c(
      "In-store" = "#F8766D",
      "Online"   = "#00BA38",
      "Outlet"   = "#619CFF"
    )
  ) +
  
  labs(
    title = "DISTRIBUCIÓN DEL PRECIO DE VENTA",
    subtitle = "Por producto, región y método de venta",
    x = NULL,
    y = "Precio unidad",
    fill = "Método de venta"
  ) +
  
  theme_minimal(base_size = 13) +
  theme(
    plot.title = element_text(
      size = 24,
      face = "bold",
      hjust = 0.5
    ),
    plot.subtitle = element_text(
      size = 14,
      hjust = 0.5,
      margin = margin(b = 15)
    ),
    axis.title.y = element_text(
      size = 14,
      face = "bold"
    ),
    axis.text.x = element_text(
      angle = 45,
      hjust = 1,
      vjust = 1,
      size = 11
    ),
    axis.text.y = element_text(size = 11),
    strip.text = element_text(
      size = 13,
      face = "bold"
    ),
    legend.position = "bottom",
    legend.title = element_text(
      size = 12,
      face = "bold"
    ),
    legend.text = element_text(size = 11),
    panel.grid.minor = element_blank(),
    panel.grid.major.x = element_blank(),
    plot.margin = margin(20, 20, 35, 20)
  )

grafico_precio_box