Adidas vende a través de distribuidores y minoristas de distintos tamaños, en varias regiones y por canales con lógicas muy diferentes (tienda física, outlet y online). En este informe asumimos el rol de analistas de datos de la compañía y analizamos 9.648 registros de ventas con técnicas de analítica descriptiva, exploratoria y diagnóstica; al final proponemos recomendaciones básicas (analítica prescriptiva) para el equipo directivo.
Pregunta guía:
¿Dónde, cómo y qué le conviene priorizar a Adidas para crecer en utilidad y no solo en ventas?
Alcance. La base no tiene fecha, así que no se calculan tasas de crecimiento ni estacionalidad: es una “foto” del desempeño. Tampoco trae costos ni descuentos, por lo que las causas de las diferencias de rentabilidad se presentan como hipótesis. Los valores se muestran en dólares (la base no especifica la moneda; se asume USD).
Cómo leer los gráficos. Todos los títulos dicen la conclusión principal. Como código de colores usamos siempre lo mismo: azul = por encima del promedio de la compañía (bueno) y naranja = por debajo del promedio (a revisar). Debajo de cada gráfico se explica por qué se eligió ese tipo de visualización.
PASO 1. Cargar librerías
library(readxl)
library(dplyr)
library(tidyr)
library(ggplot2)
library(forcats)
library(scales)
library(knitr)
library(kableExtra)
PASO 2. Cargar la base de datos
Nota: el archivo
DatosCaso1.xlsxy la imagenlogo_adidas.pngdeben estar en la misma carpeta que este.Rmd.
datos <- read_excel("DatosCaso1.xlsx", sheet = 1)
dim(datos)
## [1] 9648 11
glimpse(datos)
## Rows: 9,648
## Columns: 11
## $ distribuidor <chr> "Foot Locker", "Foot Locker", "Foot Locker", "Foot …
## $ region <chr> "Northeast", "Northeast", "Northeast", "Northeast",…
## $ estado <chr> "New York", "New York", "New York", "New York", "Ne…
## $ ciudad <chr> "New York", "New York", "New York", "New York", "Ne…
## $ producto <chr> "Men's Street Footwear", "Men's Athletic Footwear",…
## $ precio_unidad <dbl> 50, 50, 40, 45, 60, 50, 50, 50, 40, 45, 60, 50, 50,…
## $ unidades_vendidas <dbl> 1200, 1000, 1000, 850, 900, 1000, 1250, 900, 950, 8…
## $ ventas_total <dbl> 60000, 50000, 40000, 38250, 54000, 50000, 62500, 45…
## $ utilidad_operativa <dbl> 30000.00, 15000.00, 14000.00, 13387.50, 16200.00, 1…
## $ margen_operativo <dbl> 0.50, 0.30, 0.35, 0.35, 0.30, 0.25, 0.50, 0.30, 0.3…
## $ metodo_venta <chr> "In-store", "In-store", "In-store", "In-store", "In…
head(datos)
| 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 |
PASO 3. Auditoría de calidad de los datos
Antes de analizar verificamos que los datos sean confiables: valores faltantes, coherencia de las variables calculadas, duplicados y consistencia geográfica.
# 1. Valores faltantes
cat("Valores faltantes en toda la base:", sum(is.na(datos)), "\n")
## Valores faltantes en toda la base: 0
# 2. ¿ventas_total = precio x unidades en todos los registros?
cat("ventas_total coincide con precio x unidades:",
all(abs(datos$ventas_total - datos$precio_unidad * datos$unidades_vendidas) < 1e-6), "\n")
## ventas_total coincide con precio x unidades: TRUE
# 3. ¿margen_operativo = utilidad / ventas? (solo donde hay ventas)
con_ventas <- datos %>% filter(ventas_total > 0)
cat("margen_operativo coincide con utilidad / ventas:",
all(abs(con_ventas$utilidad_operativa / con_ventas$ventas_total - con_ventas$margen_operativo) < 0.01), "\n")
## margen_operativo coincide con utilidad / ventas: TRUE
# 4. Registros con cero unidades vendidas
datos %>% filter(unidades_vendidas == 0)
| distribuidor | region | estado | ciudad | producto | precio_unidad | unidades_vendidas | ventas_total | utilidad_operativa | margen_operativo | metodo_venta |
|---|---|---|---|---|---|---|---|---|---|---|
| Foot Locker | Midwest | Nebraska | Omaha | Women’s Athletic Footwear | 35 | 0 | 0 | 0 | 0.40 | Outlet |
| Foot Locker | Midwest | Nebraska | Omaha | Women’s Athletic Footwear | 30 | 0 | 0 | 0 | 0.40 | Outlet |
| Foot Locker | Midwest | Nebraska | Omaha | Women’s Athletic Footwear | 33 | 0 | 0 | 0 | 0.55 | Online |
| Foot Locker | Midwest | Nebraska | Omaha | Women’s Athletic Footwear | 27 | 0 | 0 | 0 | 0.53 | Online |
# 5. Duplicados exactos
n_dup_extra <- sum(duplicated(datos))
cat("Copias exactas repetidas:", n_dup_extra, "\n")
## Copias exactas repetidas: 262
# 6. Consistencia geográfica: ciudades que aparecen en más de un estado
datos %>% distinct(ciudad, estado) %>% count(ciudad) %>% filter(n > 1)
| ciudad | n |
|---|---|
| Charleston | 2 |
| Portland | 2 |
Hallazgos de la auditoría y decisiones tomadas:
ventas_total y margen_operativo) son
coherentes con sus componentes.ubicacion = ciudad, estado, así no se mezclan ciudades
distintas.datos <- datos %>%
mutate(
ubicacion = paste(ciudad, estado, sep = ", "),
distribuidor = as.factor(distribuidor),
region = as.factor(region),
producto = as.factor(producto),
metodo_venta = as.factor(metodo_venta)
)
# Totales globales que se usan como referencia en todo el informe
ventas_tot <- sum(datos$ventas_total)
util_tot <- sum(datos$utilidad_operativa)
margen_glob <- util_tot / ventas_tot
unid_tot <- sum(datos$unidades_vendidas)
# Paleta y tema visual (mismo código de colores en todo el informe)
col_alto <- "#0072B2" # por encima del promedio
col_bajo <- "#D55E00" # por debajo del promedio
col_gris <- "#BFBFBF"
col_negro <- "#111111"
pal_canal <- c("Online" = "#111111", "Outlet" = "#8A8A8A", "In-store" = "#C9C9C9")
theme_adidas <- function(base_size = 14) {
theme_minimal(base_size = base_size) +
theme(plot.title = element_text(face = "bold", size = base_size + 2, color = col_negro),
plot.subtitle = element_text(color = "grey35", size = base_size - 2),
plot.title.position = "plot",
panel.grid.minor = element_blank(),
panel.grid.major = element_line(color = "grey92"),
axis.title = element_text(color = "grey35", size = base_size - 2),
legend.position = "bottom",
legend.title = element_blank())
}
# Resumen por segmento
resumen_seg <- function(df, ...) {
df %>%
group_by(...) %>%
summarise(registros = n(),
ventas = sum(ventas_total),
utilidad = sum(utilidad_operativa),
unidades = sum(unidades_vendidas),
precio_prom = mean(precio_unidad),
margen_simple = mean(margen_operativo),
margen_pond = sum(utilidad_operativa) / sum(ventas_total),
.groups = "drop") %>%
mutate(part_ventas = ventas / sum(ventas),
part_utilidad = utilidad / sum(utilidad)) %>%
arrange(desc(ventas))
}
formato_seg <- function(df) {
df %>%
mutate(across(any_of(c("ventas", "utilidad")), ~ dollar(.x / 1e6, accuracy = 0.1, suffix = "M")),
across(any_of("unidades"), ~ comma(.x)),
across(any_of("precio_prom"), ~ dollar(.x, accuracy = 0.01)),
across(any_of(c("margen_simple", "margen_pond", "part_ventas", "part_utilidad")),
~ percent(.x, accuracy = 0.1)))
}
tabla <- function(df, ...) {
kable(df, format.args = list(big.mark = ","), ...) %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed"),
full_width = FALSE)
}
# Gráfico estándar por segmento: % de las ventas vs % de la utilidad.
# Si la barra de utilidad es azul, el segmento aporta más utilidad de lo que vende; si es naranja, menos.
graf_part <- function(df, var, titulo, subtitulo = NULL) {
d <- df %>%
mutate(cat = as.character(.data[[var]]),
etiqueta = paste0(cat, "\n(margen ", percent(margen_pond, accuracy = 0.1), ")"),
mas = part_utilidad >= part_ventas) %>%
arrange(ventas)
d$etiqueta <- factor(d$etiqueta, levels = d$etiqueta)
niveles <- c("% de la utilidad (aporta más de lo que vende)",
"% de la utilidad (aporta menos de lo que vende)",
"% de las ventas")
largo <- bind_rows(
d %>% transmute(etiqueta, tipo = "% de las ventas", valor = part_ventas),
d %>% transmute(etiqueta,
tipo = ifelse(mas, niveles[1], niveles[2]),
valor = part_utilidad)
) %>% mutate(tipo = factor(tipo, levels = niveles))
ggplot(largo, aes(x = etiqueta, y = valor, fill = tipo)) +
geom_col(position = position_dodge(width = 0.8), width = 0.75) +
geom_text(aes(label = percent(valor, accuracy = 0.1)),
position = position_dodge(width = 0.8), hjust = -0.12, size = 4) +
coord_flip() +
scale_y_continuous(labels = percent_format(accuracy = 1), expand = expansion(mult = c(0, 0.15))) +
scale_fill_manual(values = setNames(c(col_alto, col_bajo, col_gris), niveles), drop = FALSE) +
labs(title = titulo, subtitle = subtitulo, x = NULL, y = NULL) +
guides(fill = guide_legend(nrow = 3, reverse = FALSE)) +
theme_adidas() +
theme(panel.grid.major.y = element_blank())
}
Prueba de sensibilidad frente a los duplicados.
sin_dup <- distinct(datos)
sens <- bind_rows(
datos %>% summarise(Escenario = "Con duplicados (base completa)", Registros = n(),
Ventas = sum(ventas_total), Utilidad = sum(utilidad_operativa)),
sin_dup %>% summarise(Escenario = "Sin duplicados exactos", Registros = n(),
Ventas = sum(ventas_total), Utilidad = sum(utilidad_operativa))
) %>%
mutate(`Margen ponderado` = percent(Utilidad / Ventas, accuracy = 0.1),
Ventas = dollar(Ventas / 1e6, accuracy = 0.1, suffix = "M"),
Utilidad = dollar(Utilidad / 1e6, accuracy = 0.1, suffix = "M"))
tabla(sens)
| Escenario | Registros | Ventas | Utilidad | Margen ponderado |
|---|---|---|---|---|
| Con duplicados (base completa) | 9,648 | $120.2M | $47.2M | 39.3% |
| Sin duplicados exactos | 9,386 | $114.4M | $45.2M | 39.5% |
orden_con <- resumen_seg(datos, region) %>% arrange(desc(margen_pond)) %>% pull(region) %>% as.character()
orden_sin <- resumen_seg(sin_dup, region) %>% arrange(desc(margen_pond)) %>% pull(region) %>% as.character()
cat("Orden de regiones por margen (con duplicados):", orden_con, "\n")
## Orden de regiones por margen (con duplicados): South Midwest Southeast Northeast West
cat("Orden de regiones por margen (sin duplicados):", orden_sin, "\n")
## Orden de regiones por margen (sin duplicados): South Midwest Southeast Northeast West
Los totales y el ranking de regiones prácticamente no cambian: los duplicados no alteran las conclusiones y se mantiene la base completa.
Por cada dólar que vende Adidas en esta base, quedan $0.39 de utilidad operativa.
Una precisión importante: el margen promedio simple (42.3%) es mayor que el ponderado (39.3%) porque el promedio simple trata igual a una venta pequeña y a una grande. Para hablar de la rentabilidad de un segmento usamos el margen ponderado (utilidad total / ventas totales), que refleja cuánto se gana realmente por cada dólar vendido.
vars <- c(precio_unidad = "Precio por unidad", unidades_vendidas = "Unidades vendidas",
ventas_total = "Ventas totales", utilidad_operativa = "Utilidad operativa",
margen_operativo = "Margen operativo")
desc <- datos %>%
select(all_of(names(vars))) %>%
pivot_longer(everything(), names_to = "v", values_to = "x") %>%
group_by(v) %>%
summarise(Media = mean(x), Mediana = median(x), DE = sd(x), CV = sd(x) / mean(x),
Min = min(x), Q1 = quantile(x, 0.25), Q3 = quantile(x, 0.75), Max = max(x),
Asimetria = mean((x - mean(x))^3) / sd(x)^3,
Atipicos = sum(x < quantile(x, 0.25) - 1.5 * IQR(x) | x > quantile(x, 0.75) + 1.5 * IQR(x)),
.groups = "drop") %>%
mutate(`% atípicos` = percent(Atipicos / nrow(datos), accuracy = 0.1),
Variable = factor(vars[v], levels = vars)) %>%
arrange(Variable) %>%
select(Variable, Media, Mediana, DE, CV, Min, Q1, Q3, Max, Asimetria, Atipicos, `% atípicos`)
kable(desc, digits = 2, format.args = list(big.mark = ",")) %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = FALSE)
| Variable | Media | Mediana | DE | CV | Min | Q1 | Q3 | Max | Asimetria | Atipicos | % atípicos |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Precio por unidad | 45.22 | 45.00 | 14.71 | 0.33 | 7.0 | 35.00 | 55.00 | 110.0 | 0.36 | 104 | 1.1% |
| Unidades vendidas | 256.93 | 176.00 | 214.25 | 0.83 | 0.0 | 106.00 | 350.00 | 1,275.0 | 1.46 | 508 | 5.3% |
| Ventas totales | 12,455.08 | 7,803.50 | 12,716.39 | 1.02 | 0.0 | 4,065.25 | 15,864.50 | 82,500.0 | 1.96 | 799 | 8.3% |
| Utilidad operativa | 4,894.79 | 3,262.98 | 4,866.46 | 0.99 | 0.0 | 1,753.44 | 6,192.36 | 39,000.0 | 2.33 | 694 | 7.2% |
| Margen operativo | 0.42 | 0.41 | 0.10 | 0.23 | 0.1 | 0.35 | 0.49 | 0.8 | 0.23 | 63 | 0.7% |
Interpretación:
d1 <- datos %>%
select(ventas_total, utilidad_operativa) %>%
pivot_longer(everything(), names_to = "variable", values_to = "valor") %>%
mutate(variable = factor(variable, levels = c("ventas_total", "utilidad_operativa"),
labels = c("Ventas por registro (USD)", "Utilidad por registro (USD)")))
est <- d1 %>% group_by(variable) %>%
summarise(Mediana = median(valor), Promedio = mean(valor), .groups = "drop") %>%
pivot_longer(c(Mediana, Promedio), names_to = "medida", values_to = "v")
ggplot(d1, aes(x = valor)) +
geom_histogram(bins = 40, fill = col_gris, color = "white") +
geom_vline(data = est, aes(xintercept = v, color = medida), linewidth = 1.3) +
facet_wrap(~ variable, scales = "free") +
scale_color_manual(values = c("Mediana" = col_alto, "Promedio" = col_bajo)) +
scale_x_continuous(labels = dollar) +
labs(title = paste0("Una venta típica vale ", dollar(median(datos$ventas_total)),
", pero el promedio es ", dollar(mean(datos$ventas_total))),
subtitle = "Pocas ventas muy grandes elevan el promedio: por eso se usa la mediana como valor típico",
x = NULL, y = "Número de registros") +
theme_adidas()
q_m <- quantile(datos$margen_operativo, c(0.25, 0.5, 0.75))
ggplot(datos, aes(x = margen_operativo)) +
geom_histogram(binwidth = 0.025, fill = col_gris, color = "white") +
geom_vline(xintercept = margen_glob, color = col_bajo, linewidth = 1.3) +
geom_vline(xintercept = q_m[2], color = col_alto, linewidth = 1.3) +
scale_x_continuous(labels = percent_format(accuracy = 1)) +
labs(title = paste0("El margen típico es ", percent(q_m[2], accuracy = 1),
" y la mitad de los registros está entre ", percent(q_m[1], accuracy = 1),
" y ", percent(q_m[3], accuracy = 1)),
subtitle = paste0("Línea azul: mediana (", percent(q_m[2], accuracy = 0.1),
"). Línea naranja: margen ponderado de la compañía (",
percent(margen_glob, accuracy = 0.1), ")"),
x = "Margen operativo por registro", y = "Número de registros") +
theme_adidas()
ggplot(datos, aes(x = fct_reorder(metodo_venta, margen_operativo, .fun = median), y = margen_operativo,
fill = metodo_venta)) +
geom_boxplot(outlier.alpha = 0.25, outlier.size = 1, width = 0.6) +
geom_hline(yintercept = margen_glob, linetype = "dashed", color = col_bajo, linewidth = 1) +
coord_flip() +
scale_fill_manual(values = pal_canal, guide = "none") +
scale_y_continuous(labels = percent_format(accuracy = 1)) +
labs(title = "Online tiene los márgenes más altos; In-store, los más bajos",
subtitle = "La caja muestra el 50% central de los registros; la línea punteada es el margen promedio de la compañía",
x = NULL, y = "Margen operativo por registro") +
theme_adidas() +
theme(panel.grid.major.y = element_blank())
En estos gráficos, cada segmento tiene dos barras: qué porcentaje de las ventas totales representa (gris) y qué porcentaje de la utilidad total aporta. Si la barra de utilidad es azul, el segmento aporta más utilidad de lo que vende; si es naranja, aporta menos. Entre paréntesis está su margen ponderado.
seg_prod <- resumen_seg(datos, producto)
graf_part(seg_prod, "producto", "Men's Street Footwear lidera tanto en ventas como en rentabilidad",
paste0("Margen promedio de la compañía: ", percent(margen_glob, accuracy = 0.1)))
seg_prod %>%
select(producto, registros, ventas, part_ventas, utilidad, part_utilidad, precio_prom, margen_pond) %>%
formato_seg() %>%
tabla(col.names = c("Producto", "Registros", "Ventas", "% ventas", "Utilidad", "% utilidad",
"Precio prom.", "Margen ponderado"))
| Producto | Registros | Ventas | % ventas | Utilidad | % utilidad | Precio prom. | Margen ponderado |
|---|---|---|---|---|---|---|---|
| Men’s Street Footwear | 1,610 | $27.7M | 23.0% | $11.6M | 24.6% | $44.24 | 42.0% |
| Women’s Apparel | 1,608 | $23.9M | 19.9% | $9.7M | 20.5% | $51.60 | 40.6% |
| Men’s Athletic Footwear | 1,610 | $20.6M | 17.1% | $7.4M | 15.7% | $43.78 | 36.1% |
| Women’s Street Footwear | 1,608 | $17.2M | 14.3% | $6.5M | 13.8% | $40.25 | 37.8% |
| Men’s Apparel | 1,606 | $16.5M | 13.7% | $6.4M | 13.5% | $50.32 | 38.6% |
| Women’s Athletic Footwear | 1,606 | $14.3M | 11.9% | $5.6M | 11.9% | $41.11 | 39.1% |
Men’s Street Footwear aporta cerca del 23% de las ventas y el 25% de la utilidad, con el mejor margen (~42%). Women’s Apparel es el segundo en ventas (~20%) con un margen sólido (~41%). En el otro extremo, Men’s Athletic Footwear tiene el menor margen (~36%), seguido de Women’s Street Footwear (~38%): el calzado deportivo de hombre es el tercer producto en ventas (17%) pero deja proporcionalmente menos utilidad (15,8%).
seg_reg <- resumen_seg(datos, region)
graf_part(seg_reg, "region", "West vende más, pero aporta menos utilidad de lo que vende",
"South, en cambio, aporta más utilidad de la que representa en ventas")
seg_reg %>%
select(region, registros, ventas, part_ventas, utilidad, part_utilidad, precio_prom, margen_pond) %>%
formato_seg() %>%
tabla(col.names = c("Región", "Registros", "Ventas", "% ventas", "Utilidad", "% utilidad",
"Precio prom.", "Margen ponderado"))
| Región | Registros | Ventas | % ventas | Utilidad | % utilidad | Precio prom. | Margen ponderado |
|---|---|---|---|---|---|---|---|
| West | 2,448 | $36.4M | 30.3% | $13.0M | 27.6% | $49.94 | 35.7% |
| Northeast | 2,376 | $25.1M | 20.9% | $9.7M | 20.6% | $46.69 | 38.8% |
| Southeast | 1,224 | $21.4M | 17.8% | $8.4M | 17.8% | $50.18 | 39.3% |
| South | 1,728 | $20.6M | 17.1% | $9.2M | 19.5% | $38.24 | 44.8% |
| Midwest | 1,872 | $16.7M | 13.9% | $6.9M | 14.5% | $40.36 | 41.1% |
Es uno de los hallazgos más relevantes: West aporta el 30% de las ventas pero solo el 27,6% de la utilidad, con el margen más bajo de la compañía (~35,7%). South aporta el 17% de las ventas y casi el 19,5% de la utilidad, con el margen más alto (~44,8%). La región más grande no es la más rentable, y esa brecha de ~9 puntos de margen entre South y West es una oportunidad clara.
seg_dist <- resumen_seg(datos, distribuidor)
graf_part(seg_dist, "distribuidor", "Sports Direct deja el mayor margen por cada dólar vendido",
"West Gear y Foot Locker concentran más de la mitad de las ventas")
West Gear (27%) y Foot Locker (24%) concentran más de la mitad de las ventas. Sports Direct destaca por rentabilidad: con el 20,5% de las ventas logra el mayor margen (~43,2%). Walmart (~37,1%) y West Gear (~37,6%) tienen los márgenes más bajos; para West Gear esto pesa especialmente por su enorme volumen.
seg_met <- resumen_seg(datos, metodo_venta)
graf_part(seg_met, "metodo_venta", "Online: el canal más grande y el más rentable",
"In-store es el canal con menor margen")
Online genera el 37,4% de las ventas, tiene el mejor margen (~43,5%) y aporta el 41,4% de la utilidad. In-store tiene el menor margen (~35,8%) y el precio promedio más alto ($48,8), lo que sugiere que el canal físico tiene costos o descuentos que le restan rentabilidad. Outlet queda en un punto intermedio (~37,7%).
seg_ubi <- resumen_seg(datos, ubicacion)
tab_ubi <- function(df) {
df %>% select(ubicacion, ventas, part_ventas, margen_pond) %>%
formato_seg() %>%
tabla(col.names = c("Ubicación", "Ventas", "% ventas", "Margen ponderado"))
}
# Las 10 ubicaciones con más ventas
seg_ubi %>% slice_head(n = 10) %>% tab_ubi()
| Ubicación | Ventas | % ventas | Margen ponderado |
|---|---|---|---|
| New York, New York | $5.7M | 4.7% | 37.3% |
| San Francisco, California | $4.9M | 4.1% | 32.1% |
| Orlando, Florida | $3.9M | 3.3% | 34.0% |
| Miami, Florida | $3.9M | 3.2% | 40.8% |
| Los Angeles, California | $3.7M | 3.0% | 37.7% |
| Houston, Texas | $3.6M | 3.0% | 41.2% |
| Charleston, South Carolina | $3.6M | 3.0% | 40.9% |
| New Orleans, Louisiana | $3.4M | 2.8% | 42.2% |
| Seattle, Washington | $3.2M | 2.7% | 28.8% |
| Richmond, Virginia | $3.1M | 2.6% | 38.2% |
Ubicaciones con ventas superiores a $1M y menor margen:
seg_ubi %>% filter(ventas >= 1e6) %>% arrange(margen_pond) %>% slice_head(n = 5) %>% tab_ubi()
| Ubicación | Ventas | % ventas | Margen ponderado |
|---|---|---|---|
| Honolulu, Hawaii | $2.7M | 2.3% | 28.5% |
| Seattle, Washington | $3.2M | 2.7% | 28.8% |
| San Francisco, California | $4.9M | 4.1% | 32.1% |
| Anchorage, Alaska | $1.8M | 1.5% | 32.7% |
| Boston, Massachusetts | $1.6M | 1.3% | 32.8% |
Ubicaciones con ventas superiores a $1M y mayor margen:
seg_ubi %>% filter(ventas >= 1e6) %>% arrange(desc(margen_pond)) %>% slice_head(n = 5) %>% tab_ubi()
| Ubicación | Ventas | % ventas | Margen ponderado |
|---|---|---|---|
| Birmingham, Alabama | $2.5M | 2.1% | 54.4% |
| Knoxville, Tennessee | $2.6M | 2.1% | 49.5% |
| Detroit, Michigan | $2.3M | 1.9% | 45.9% |
| Dallas, Texas | $3.0M | 2.5% | 44.8% |
| Boise, Idaho | $2.7M | 2.3% | 44.6% |
Ninguna ubicación supera el 5% de las ventas (la mayor, Nueva York, aporta ~4,7%), así que la compañía no depende de una sola ciudad. Sí hay diferencias de rentabilidad: Honolulu, Seattle y San Francisco tienen márgenes de 28-32%, todas del oeste, mientras que Birmingham (Alabama) y Knoxville (Tennessee), en el sur, llegan a ~54% y ~50%. Es coherente con el patrón regional: el oeste vende con precios altos y menor margen; el sur, con precios más bajos y mayor margen. Se usan tablas y no gráficos porque son 52 ubicaciones: un gráfico sería ilegible.
etiquetas <- c("Precio", "Unidades", "Ventas", "Utilidad", "Margen")
mc <- datos %>%
select(precio_unidad, unidades_vendidas, ventas_total, utilidad_operativa, margen_operativo) %>%
cor()
dimnames(mc) <- list(etiquetas, etiquetas)
r_util <- data.frame(variable = c("Ventas totales", "Unidades vendidas", "Precio por unidad", "Margen operativo"),
r = mc["Utilidad", c("Ventas", "Unidades", "Precio", "Margen")])
ggplot(r_util, aes(x = fct_reorder(variable, r), y = r, fill = r > 0)) +
geom_col(width = 0.65) +
geom_text(aes(label = sprintf("%.2f", r), hjust = ifelse(r >= 0, -0.15, 1.15)), size = 5) +
coord_flip() +
scale_fill_manual(values = c("TRUE" = col_alto, "FALSE" = col_bajo), guide = "none") +
scale_y_continuous(limits = c(-0.2, 1.1)) +
labs(title = "La utilidad se explica sobre todo por cuánto se vende",
subtitle = "Correlación de Pearson (r) de cada variable con la utilidad operativa",
x = NULL, y = "Correlación con la utilidad") +
theme_adidas() +
theme(panel.grid.major.y = element_blank())
kable(round(mc, 2)) %>%
kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE)
| Precio | Unidades | Ventas | Utilidad | Margen | |
|---|---|---|---|---|---|
| Precio | 1.00 | 0.27 | 0.54 | 0.50 | -0.14 |
| Unidades | 0.27 | 1.00 | 0.92 | 0.87 | -0.31 |
| Ventas | 0.54 | 0.92 | 1.00 | 0.94 | -0.30 |
| Utilidad | 0.50 | 0.87 | 0.94 | 1.00 | -0.05 |
| Margen | -0.14 | -0.31 | -0.30 | -0.05 | 1.00 |
mod <- lm(utilidad_operativa ~ ventas_total, data = datos)
r2 <- summary(mod)$r.squared
ggplot(datos, aes(x = ventas_total, y = utilidad_operativa)) +
geom_point(alpha = 0.12, color = col_negro, size = 1.3) +
geom_smooth(method = "lm", color = col_alto, se = FALSE, linewidth = 1.6) +
scale_x_continuous(labels = dollar) + scale_y_continuous(labels = dollar) +
labs(title = paste0("A más ventas, más utilidad: casi en línea recta (r = ",
round(cor(datos$ventas_total, datos$utilidad_operativa), 2), ")"),
subtitle = "Cada punto es un registro de venta; la línea azul es la tendencia promedio",
x = "Ventas totales (USD)", y = "Utilidad operativa (USD)") +
theme_adidas()
El modelo lineal indica que cada dólar adicional de venta aporta ~0.36 dólares de utilidad, y que las ventas explican cerca del 87% de la variabilidad de la utilidad. La dispersión alrededor de la línea es donde está la oportunidad: a igual nivel de ventas, hay registros que ganan mucho más que otros, y eso se explica por el margen (segmento, canal, región).
Las preguntas de la guía son si precios más altos implican menor volumen y si el precio influye en la rentabilidad. Agrupamos los registros por banda de precio para responderlas de forma simple:
datos <- datos %>%
mutate(banda_precio = cut(precio_unidad, breaks = c(0, 30, 40, 50, 60, Inf),
labels = c("≤ $30", "$31-40", "$41-50", "$51-60", "> $60")))
bandas <- datos %>% group_by(banda_precio) %>%
summarise(registros = n(), unidades_prom = mean(unidades_vendidas),
ventas = sum(ventas_total), utilidad = sum(utilidad_operativa),
margen_pond = sum(utilidad_operativa) / sum(ventas_total), .groups = "drop")
b_long <- bandas %>%
transmute(banda_precio,
`Unidades vendidas por registro (promedio)` = unidades_prom,
`Margen operativo ponderado` = margen_pond) %>%
pivot_longer(-banda_precio, names_to = "indicador", values_to = "valor") %>%
mutate(etiqueta = ifelse(grepl("Margen", indicador), percent(valor, accuracy = 0.1), comma(valor, accuracy = 1)))
ggplot(b_long, aes(x = banda_precio, y = valor)) +
geom_col(fill = col_gris, width = 0.7) +
geom_col(data = b_long %>% filter(banda_precio == "> $60"), fill = col_bajo, width = 0.7) +
geom_text(aes(label = etiqueta), vjust = -0.4, size = 4.2) +
facet_wrap(~ indicador, scales = "free_y") +
scale_y_continuous(expand = expansion(mult = c(0, 0.18)), labels = NULL) +
labs(title = "Los precios altos venden más unidades, pero con menos margen",
subtitle = "Banda de precio > $60 en naranja: mayor volumen por registro y el menor margen",
x = "Precio por unidad", y = NULL) +
theme_adidas() +
theme(panel.grid = element_blank(), strip.text = element_text(face = "bold"))
bandas %>%
mutate(unidades_prom = round(unidades_prom, 1),
ventas = dollar(ventas / 1e6, accuracy = 0.1, suffix = "M"),
utilidad = dollar(utilidad / 1e6, accuracy = 0.1, suffix = "M"),
margen_pond = percent(margen_pond, accuracy = 0.1)) %>%
tabla(col.names = c("Banda de precio", "Registros", "Unidades prom.", "Ventas", "Utilidad", "Margen ponderado"))
| Banda de precio | Registros | Unidades prom. | Ventas | Utilidad | Margen ponderado |
|---|---|---|---|---|---|
| ≤ $30 | 1,445 | 188.4 | $6.5M | $2.7M | 41.9% |
| $31-40 | 2,391 | 225.8 | $19.6M | $7.9M | 40.6% |
| $41-50 | 2,656 | 249.6 | $30.9M | $12.4M | 40.2% |
| $51-60 | 1,796 | 280.0 | $28.1M | $11.3M | 40.1% |
| > $60 | 1,360 | 368.3 | $35.1M | $12.9M | 36.7% |
¿Precios más altos implican menor volumen? No: los datos muestran lo contrario (r = +0,27 entre precio y unidades). Pero esto no significa que subir precios aumente las ventas. Es una correlación entre registros distintos, no un experimento; lo más probable es que refleje el mix de productos y puntos de venta (los registros de precio alto son de puntos de venta de mayor tráfico o posicionamiento). Para verificar que no es un efecto de mezclar productos o canales, revisamos la relación dentro de cada uno:
cor_prod <- datos %>% group_by(Segmento = as.character(producto)) %>%
summarise(`r precio-unidades` = cor(precio_unidad, unidades_vendidas),
`r precio-margen` = cor(precio_unidad, margen_operativo), .groups = "drop")
cor_met <- datos %>% group_by(Segmento = as.character(metodo_venta)) %>%
summarise(`r precio-unidades` = cor(precio_unidad, unidades_vendidas),
`r precio-margen` = cor(precio_unidad, margen_operativo), .groups = "drop")
bind_rows(cor_prod, cor_met) %>%
kable(digits = 2) %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = FALSE) %>%
pack_rows("Por producto", 1, 6) %>%
pack_rows("Por método de venta", 7, 9)
| Segmento | r precio-unidades | r precio-margen |
|---|---|---|
| Por producto | ||
| Men’s Apparel | 0.31 | -0.08 |
| Men’s Athletic Footwear | 0.33 | -0.22 |
| Men’s Street Footwear | 0.25 | -0.03 |
| Women’s Apparel | 0.27 | -0.28 |
| Women’s Athletic Footwear | 0.34 | -0.18 |
| Women’s Street Footwear | 0.30 | -0.11 |
| Por método de venta | ||
| In-store | 0.36 | -0.10 |
| Online | 0.15 | -0.09 |
| Outlet | 0.39 | -0.29 |
La relación positiva se repite en todos los productos y canales (entre +0,15 y +0,39). Con esta base no es recomendable usar el precio como palanca de volumen; habría que probarlo con experimentos controlados.
¿El precio influye en la rentabilidad? Sí, pero en sentido contrario al que podría esperarse: la banda más cara (> $60) tiene el menor margen (~36,7%) frente a ~41,9% de la más barata (≤ $30), y la correlación entre precio y margen es negativa (r = -0,14). Esa banda vende bastante más unidades por registro (~368 frente a ~188), lo que sugiere que en los precios altos se está “comprando” volumen con margen (posibles descuentos, promociones o costos más altos). Aquí se ve el patrón general del negocio: vender más no implica ganar mejor (el margen se correlaciona negativamente con el volumen, r = -0,30).
Como apoyo al diagnóstico, comparamos la distribución del margen entre grupos con la prueba no paramétrica de Kruskal-Wallis (el margen no es normal ni simétrico).
kw <- lapply(c("metodo_venta", "region", "producto", "distribuidor"), function(v) {
t <- kruskal.test(datos$margen_operativo ~ datos[[v]])
data.frame(Factor = v, Chi2 = unname(t$statistic), gl = unname(t$parameter), p_valor = t$p.value)
}) %>% bind_rows() %>%
mutate(Factor = recode(Factor, metodo_venta = "Método de venta", region = "Región",
producto = "Producto", distribuidor = "Distribuidor"),
p_valor = ifelse(p_valor < 0.001, "< 0.001", as.character(round(p_valor, 3))))
kable(kw, digits = 1) %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = FALSE)
| Factor | Chi2 | gl | p_valor |
|---|---|---|---|
| Método de venta | 2112.0 | 2 | < 0.001 |
| Región | 520.0 | 4 | < 0.001 |
| Producto | 288.6 | 5 | < 0.001 |
| Distribuidor | 90.9 | 5 | < 0.001 |
En los cuatro casos el valor-p es menor a 0,001: las diferencias de margen entre métodos de venta, regiones, productos y distribuidores no son producto del azar; la más marcada es la del método de venta.
reg_met <- resumen_seg(datos, region, metodo_venta) %>%
mutate(metodo_venta = factor(metodo_venta, levels = c("Online", "Outlet", "In-store")),
region = factor(region, levels = c("Northeast", "Midwest", "Southeast", "South", "West")))
ggplot(reg_met, aes(x = region, y = margen_pond, fill = metodo_venta)) +
geom_col(position = position_dodge(width = 0.85), width = 0.8) +
geom_text(aes(label = percent(margen_pond, accuracy = 1)),
position = position_dodge(width = 0.85), vjust = -0.4, size = 3.8) +
geom_hline(yintercept = margen_glob, linetype = "dashed", color = col_bajo, linewidth = 1) +
scale_fill_manual(values = pal_canal) +
scale_y_continuous(labels = percent_format(accuracy = 1), expand = expansion(mult = c(0, 0.12))) +
labs(title = "En West, Outlet e In-store rinden poco; su canal Online sí rinde",
subtitle = paste0("Margen ponderado por región y canal. Línea punteada: promedio de la compañía (",
percent(margen_glob, accuracy = 0.1), ")"),
x = NULL, y = "Margen ponderado") +
theme_adidas() +
theme(panel.grid.major.x = element_blank())
reg_met %>%
group_by(region) %>% mutate(part_region = ventas / sum(ventas)) %>% ungroup() %>%
arrange(region, metodo_venta) %>%
select(region, metodo_venta, ventas, part_region, margen_pond) %>%
mutate(ventas = dollar(ventas / 1e6, accuracy = 0.1, suffix = "M"),
part_region = percent(part_region, accuracy = 0.1),
margen_pond = percent(margen_pond, accuracy = 0.1)) %>%
tabla(col.names = c("Región", "Método", "Ventas", "% de las ventas de la región", "Margen ponderado"))
| Región | Método | Ventas | % de las ventas de la región | Margen ponderado |
|---|---|---|---|---|
| Northeast | Online | $4.6M | 18.4% | 48.6% |
| Northeast | Outlet | $8.9M | 35.3% | 36.5% |
| Northeast | In-store | $11.6M | 46.2% | 36.7% |
| Midwest | Online | $7.1M | 42.7% | 44.0% |
| Midwest | Outlet | $3.6M | 21.6% | 39.2% |
| Midwest | In-store | $6.0M | 35.7% | 38.9% |
| Southeast | Online | $12.1M | 56.5% | 42.1% |
| Southeast | Outlet | $2.1M | 9.7% | 36.4% |
| Southeast | In-store | $7.2M | 33.9% | 35.4% |
| South | Online | $9.2M | 44.7% | 45.1% |
| South | Outlet | $11.1M | 53.7% | 44.6% |
| South | In-store | $0.3M | 1.6% | 39.7% |
| West | Online | $12.0M | 32.8% | 41.4% |
| West | Outlet | $13.9M | 38.3% | 32.8% |
| West | In-store | $10.5M | 28.9% | 33.2% |
Hipótesis que surgen:
celdas <- resumen_seg(datos, producto, region) %>%
mutate(celda = paste(producto, region, sep = " - "))
top10 <- celdas %>%
slice_max(ventas, n = 10) %>%
mutate(dif_pp = (margen_pond - margen_glob) * 100,
estado = ifelse(margen_pond >= margen_glob, "Por encima del promedio", "Por debajo del promedio"),
etiqueta = paste0(celda, " (", dollar(ventas / 1e6, accuracy = 0.1, suffix = "M"), ")"))
top10$etiqueta <- factor(top10$etiqueta, levels = rev(top10$etiqueta))
n_bajo <- sum(top10$margen_pond < margen_glob)
ggplot(top10, aes(x = etiqueta, y = dif_pp, fill = estado)) +
geom_col(width = 0.7) +
geom_hline(yintercept = 0, color = col_negro) +
geom_text(aes(label = percent(margen_pond, accuracy = 0.1),
hjust = ifelse(dif_pp >= 0, -0.15, 1.15)), size = 4.2) +
coord_flip() +
scale_fill_manual(values = c("Por encima del promedio" = col_alto, "Por debajo del promedio" = col_bajo)) +
scale_y_continuous(limits = c(-13, 9)) +
labs(title = paste0("De las 10 combinaciones que más venden, ", n_bajo, " rinden por debajo del promedio"),
subtitle = paste0("Barras: puntos porcentuales frente al margen promedio (", percent(margen_glob, accuracy = 0.1),
"). Entre paréntesis: ventas de la combinación"),
x = NULL, y = "Diferencia de margen frente al promedio de la compañía (puntos porcentuales)") +
theme_adidas() +
theme(panel.grid.major.y = element_blank())
Lectura estratégica:
prod_met <- resumen_seg(datos, producto, metodo_venta) %>%
mutate(metodo_venta = factor(metodo_venta, levels = c("Online", "Outlet", "In-store")))
ggplot(prod_met, aes(x = margen_pond, y = fct_reorder(producto, ventas, .fun = sum))) +
geom_line(aes(group = producto), color = "grey75", linewidth = 1.6) +
geom_vline(xintercept = margen_glob, linetype = "dashed", color = col_bajo, linewidth = 1) +
geom_point(aes(color = metodo_venta), size = 6) +
scale_color_manual(values = pal_canal) +
scale_x_continuous(labels = percent_format(accuracy = 1)) +
labs(title = "En los seis productos, Online es el canal más rentable",
subtitle = paste0("Cada punto es el margen ponderado del producto en un canal. Línea punteada: promedio (",
percent(margen_glob, accuracy = 0.1), ")"),
x = "Margen ponderado", y = NULL) +
theme_adidas() +
theme(panel.grid.major.y = element_blank())
En los seis productos, Online es el canal más rentable, y In-store es el menos rentable en cinco de ellos (en Men’s Athletic Footwear, Outlet e In-store quedan empatados en ~33%). Los mejores casos son Women’s Apparel y Men’s Street Footwear en Online (~45-46% de margen y entre $9M y $10,4M de ventas). Los peores son el calzado en tienda física: Women’s Street Footwear In-store (~32,5%), Men’s Athletic (~33%) y Women’s Athletic (~33,5%). El canal importa tanto como el producto.
resumen_seg(datos, distribuidor, metodo_venta) %>%
slice_head(n = 6) %>%
select(distribuidor, metodo_venta, ventas, part_ventas, margen_pond) %>%
formato_seg() %>%
tabla(col.names = c("Distribuidor", "Método", "Ventas", "% ventas", "Margen ponderado"))
| Distribuidor | Método | Ventas | % ventas | Margen ponderado |
|---|---|---|---|---|
| West Gear | In-store | $15.7M | 13.0% | 35.1% |
| Foot Locker | Online | $12.2M | 10.2% | 42.4% |
| Sports Direct | Online | $10.0M | 8.3% | 45.1% |
| West Gear | Online | $9.5M | 7.9% | 44.5% |
| Foot Locker | Outlet | $9.1M | 7.6% | 36.4% |
| Sports Direct | Outlet | $9.1M | 7.6% | 44.6% |
La combinación más grande es West Gear In-store (~$15,7M, 13% de las ventas totales) con solo ~35,1% de margen. En contraste, Sports Direct Online rinde ~45,1% y West Gear Online ~44,5%. Mover parte del volumen de West Gear de tienda física a online sería la palanca de rentabilidad más grande a nivel de un solo cliente.
w <- datos %>% filter(region == "West")
resto <- datos %>% filter(region != "West")
m_west <- sum(w$utilidad_operativa) / sum(w$ventas_total)
m_resto <- sum(resto$utilidad_operativa) / sum(resto$ventas_total)
brecha_west <- sum(w$ventas_total) * (m_resto - m_west)
onl <- seg_met %>% filter(metodo_venta == "Online")
Las recomendaciones se basan en la evidencia anterior. Como la base no incluye costos, descuentos ni fechas, cada una debe verse como una hipótesis a validar con información interna de Adidas antes de ejecutarse.
cierre <- 0.5
esc <- function(seg, ref, cierre = 0.5) {
m_seg <- sum(seg$utilidad_operativa) / sum(seg$ventas_total)
m_ref <- sum(ref$utilidad_operativa) / sum(ref$ventas_total)
list(ventas = sum(seg$ventas_total), m_seg = m_seg, m_ref = m_ref,
ganancia = sum(seg$ventas_total) * (m_ref - m_seg) * cierre)
}
# A: West - Outlet cierra la mitad de su brecha frente al Outlet del resto del país
A <- esc(datos %>% filter(region == "West", metodo_venta == "Outlet"),
datos %>% filter(region != "West", metodo_venta == "Outlet"), cierre)
# B: Women's Apparel en West cierra la mitad de su brecha frente al resto de regiones
B <- esc(datos %>% filter(region == "West", producto == "Women's Apparel"),
datos %>% filter(region != "West", producto == "Women's Apparel"), cierre)
# C: West Gear traslada el 10% de sus ventas In-store al canal Online
wg_ins <- datos %>% filter(distribuidor == "West Gear", metodo_venta == "In-store")
wg_onl <- datos %>% filter(distribuidor == "West Gear", metodo_venta == "Online")
traslado <- 0.10
C <- list(ventas = sum(wg_ins$ventas_total),
m_seg = sum(wg_ins$utilidad_operativa) / sum(wg_ins$ventas_total),
m_ref = sum(wg_onl$utilidad_operativa) / sum(wg_onl$ventas_total))
C$ganancia <- C$ventas * traslado * (C$m_ref - C$m_seg)
escenarios <- data.frame(
Escenario = c("A. West - Outlet: cierra 50% de la brecha frente al Outlet del resto del país",
"B. Women's Apparel en West: cierra 50% de la brecha frente al resto de regiones",
"C. West Gear: traslada 10% de su venta In-store a Online"),
`Ventas del segmento` = dollar(c(A$ventas, B$ventas, C$ventas) / 1e6, accuracy = 0.1, suffix = "M"),
`Margen actual` = percent(c(A$m_seg, B$m_seg, C$m_seg), accuracy = 0.1),
`Margen de referencia` = percent(c(A$m_ref, B$m_ref, C$m_ref), accuracy = 0.1),
`Utilidad adicional` = dollar(c(A$ganancia, B$ganancia, C$ganancia) / 1e6, accuracy = 0.01, suffix = "M"),
check.names = FALSE
)
total_esc <- A$ganancia + B$ganancia + C$ganancia
| # | Recomendación | Evidencia | Indicador de seguimiento |
|---|---|---|---|
| 1 | Revisar precios, descuentos y costos en los canales Outlet e In-store de West, empezando por Women’s Apparel y Men’s Athletic Footwear. | West: 30% de ventas y solo ~35,7% de margen; Outlet ~32,8%, In-store ~33,2%; Women’s Apparel West ~29,8% | Margen ponderado West Outlet e In-store |
| 2 | Impulsar el canal Online en Northeast y en las cuentas con mayor peso en tienda física (West Gear, Foot Locker). | Online es el canal más rentable en los 6 productos; Northeast Online ~48,6% de margen con solo ~18% del mix | % de ventas online por región y margen del canal |
| 3 | Replicar en otras regiones las prácticas de South (mix, precios y estructura de canal). | South: margen ~44,8% con el precio promedio más bajo ($38) | Margen ponderado por región |
| 4 | Priorizar la relación comercial con Sports Direct y renegociar condiciones con los clientes de menor rentabilidad (Walmart, West Gear en tienda). | Sports Direct ~43,2% de margen; Walmart ~37,1%; West Gear In-store ~35,1% con $15,7M de ventas | Margen por distribuidor y por distribuidor x canal |
| 5 | Proteger Men’s Street Footwear (la de mayor venta y margen) y revisar la estructura de costos y precios de Men’s Athletic Footwear. | Men’s Street 23% de ventas y ~42% de margen; Men’s Athletic ~36% (el menor) | Margen por producto |
| 6 | No usar el precio como palanca de volumen sin una prueba controlada. Diseñar experimentos por tienda o canal antes de mover precios. | Correlación positiva precio-unidades en todos los segmentos, que no permite inferir causalidad | Resultado del piloto (unidades y margen) |
| 7 | Mejorar la base de datos: incorporar fecha, costos, descuentos e inventario. | La base actual no permite medir crecimiento ni identificar causas | Existencia y calidad de las nuevas variables |
Impacto financiero ilustrativo. Para dimensionar el potencial de las recomendaciones 1, 2 y 4, calculamos cuánta utilidad adicional se generaría en tres escenarios conservadores, sin vender una sola unidad adicional: dos segmentos de West que cierran solo la mitad de su brecha de margen frente al resto de la compañía, y un cambio de mix en West Gear (el mayor cliente en tienda física) hacia el canal online.
esc_df <- data.frame(
escenario = c("A. West Outlet: mejora de margen", "B. Women's Apparel West: mejora de margen",
"C. West Gear: 10% de In-store a Online"),
ganancia = c(A$ganancia, B$ganancia, C$ganancia) / 1e6)
ggplot(esc_df, aes(x = fct_reorder(escenario, ganancia), y = ganancia)) +
geom_col(fill = col_alto, width = 0.6) +
geom_text(aes(label = dollar(ganancia, accuracy = 0.01, suffix = "M")), hjust = -0.15, size = 5) +
coord_flip() +
scale_y_continuous(expand = expansion(mult = c(0, 0.25))) +
labs(title = paste0("Tres ajustes conservadores sumarían ~", dollar(total_esc / 1e6, accuracy = 0.1, suffix = "M"),
" de utilidad"),
subtitle = "Utilidad operativa adicional por escenario, sin vender una unidad más (millones de USD)",
x = NULL, y = NULL) +
theme_adidas() +
theme(panel.grid.major.y = element_blank(), axis.text.x = element_blank(), panel.grid.major.x = element_blank())
tabla(escenarios)
| Escenario | Ventas del segmento | Margen actual | Margen de referencia | Utilidad adicional |
|---|---|---|---|---|
| A. West - Outlet: cierra 50% de la brecha frente al Outlet del resto del país | $13.9M | 32.8% | 40.4% | $0.53M |
| B. Women’s Apparel en West: cierra 50% de la brecha frente al resto de regiones | $7.0M | 29.8% | 45.1% | $0.54M |
| C. West Gear: traslada 10% de su venta In-store a Online | $15.7M | 35.1% | 44.5% | $0.15M |
Sumando los tres escenarios, la utilidad operativa aumentaría en aproximadamente $1.2M, cerca del 2.6% de la utilidad actual. (Los escenarios se solapan parcialmente porque comparten registros de West, por lo que la suma es un orden de magnitud y no una proyección exacta.) El resultado también deja una lección: mover el mix de canal es una palanca real pero gradual (cada 10% de traslado aporta cifras modestas), mientras que corregir el margen de las celdas grandes de West es la palanca de mayor impacto.
El análisis muestra que el desempeño de Adidas se explica por cómo y dónde se vende, más que por cuánto se vende. Las ventas y la utilidad se mueven casi en línea (r = 0,94), pero el margen por dólar vendido cambia mucho según la región, el canal, el producto y el distribuidor. Algunos de los segmentos que más venden (West, los canales físicos y Men’s Athletic Footwear) son los que menos rentabilidad dejan, mientras que Online, South, Sports Direct y Men’s Street Footwear combinan volumen y rentabilidad.
Para el equipo directivo, el mensaje es claro: la oportunidad no está en vender más en todas partes, sino en corregir el margen de las pocas combinaciones grandes que rinden por debajo del promedio (sobre todo en West) y en mover el mix hacia los canales y clientes más rentables. Con evidencia adicional (fechas, costos y descuentos) se podrá pasar de las hipótesis planteadas aquí a un análisis predictivo y prescriptivo más robusto.
DatosCaso1.xlsx (Data Sales Adidas),
suministrada por la profesora Julieth Stefens Cerón para el Caso 1 de
Analítica de Datos para la Toma de Decisiones, Pontificia Universidad
Javeriana Cali (2026).