En este apartado se analizan las ventas de Adidas en Estados Unidos por distribuidor, región, producto y método de venta. El propósito es identificar dónde se genera el dinero, dónde se gana más por cada dólar vendido y dónde hay oportunidades de mejora, para apoyar la toma de decisiones estratégicas de la Junta Directiva.
# Opción A: el archivo está en la misma carpeta del proyecto
datos <- read_excel("C:/Users/jesusd.perdomo/Downloads/DatosCaso1 (2).xlsx")
# Opción B (si no lo encuentra): datos <- read_excel(file.choose())
# La columna ventas_total es una fórmula en Excel; la recalculamos por seguridad
datos <- datos %>%
mutate(ventas_total = precio_unidad * unidades_vendidas,
across(c(distribuidor, region, estado, ciudad, producto, metodo_venta), as.factor))
dim(datos)
## [1] 9648 11
glimpse(datos)
## Rows: 9,648
## Columns: 11
## $ distribuidor <fct> Foot Locker, Foot Locker, Foot Locker, Foot Locker,…
## $ region <fct> Northeast, Northeast, Northeast, Northeast, Northea…
## $ estado <fct> New York, New York, New York, New York, New York, N…
## $ ciudad <fct> New York, New York, New York, New York, New York, N…
## $ producto <fct> Men's Street Footwear, Men's Athletic Footwear, Wom…
## $ 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 <fct> In-store, In-store, In-store, In-store, In-store, I…
# Calidad de datos
colSums(is.na(datos)) # valores faltantes
## distribuidor region estado ciudad
## 0 0 0 0
## producto precio_unidad unidades_vendidas ventas_total
## 0 0 0 0
## utilidad_operativa margen_operativo metodo_venta
## 0 0 0
sum(duplicated(datos)) # filas idénticas (pueden ser transacciones repetidas legítimas)
## [1] 262
summary(datos %>% select(where(is.numeric)))
## precio_unidad unidades_vendidas ventas_total utilidad_operativa
## Min. : 7.00 Min. : 0.0 Min. : 0 Min. : 0
## 1st Qu.: 35.00 1st Qu.: 106.0 1st Qu.: 4065 1st Qu.: 1753
## Median : 45.00 Median : 176.0 Median : 7804 Median : 3263
## Mean : 45.22 Mean : 256.9 Mean :12455 Mean : 4895
## 3rd Qu.: 55.00 3rd Qu.: 350.0 3rd Qu.:15864 3rd Qu.: 6192
## Max. :110.00 Max. :1275.0 Max. :82500 Max. :39000
## margen_operativo
## Min. :0.100
## 1st Qu.:0.350
## Median :0.410
## Mean :0.423
## 3rd Qu.:0.490
## Max. :0.800
Nota: la base no tiene variable de fecha, por lo que no se analiza evolución en el tiempo.
kpis <- datos %>%
summarise(
`Ventas totales (USD)` = sum(ventas_total),
`Utilidad operativa (USD)` = sum(utilidad_operativa),
`Margen operativo global` = sum(utilidad_operativa) / sum(ventas_total),
`Unidades vendidas` = sum(unidades_vendidas),
`Precio promedio (USD)` = mean(precio_unidad)
)
kable(kpis, digits = 2, format.args = list(big.mark = ","))
| Ventas totales (USD) | Utilidad operativa (USD) | Margen operativo global | Unidades vendidas | Precio promedio (USD) |
|---|---|---|---|---|
| 120,166,650 | 47,224,968 | 0.39 | 2,478,861 | 45.22 |
Para lo siguiente, definimos una función que resume cualquier
dimensión. Importante: el margen se calcula como utilidad total
/ ventas totales (ponderado), no como promedio de la columna
margen_operativo.
resumen_por <- function(df, variable) {
df %>%
group_by({{ variable }}) %>%
summarise(ventas = sum(ventas_total),
utilidad = sum(utilidad_operativa),
unidades = sum(unidades_vendidas),
margen = utilidad / ventas,
.groups = "drop") %>%
mutate(part_ventas = ventas / sum(ventas)) %>%
arrange(desc(ventas))
}
r_dist <- resumen_por(datos, distribuidor)
kable(r_dist, digits = 3, format.args = list(big.mark = ","))
| distribuidor | ventas | utilidad | unidades | margen | part_ventas |
|---|---|---|---|---|---|
| West Gear | 32,409,558 | 12,196,891 | 625,262 | 0.376 | 0.270 |
| Foot Locker | 29,024,945 | 11,317,027 | 604,369 | 0.390 | 0.242 |
| Sports Direct | 24,616,622 | 10,641,609 | 557,640 | 0.432 | 0.205 |
| Kohl’s | 13,512,453 | 5,182,260 | 287,375 | 0.384 | 0.112 |
| Walmart | 10,506,085 | 3,902,749 | 206,225 | 0.371 | 0.087 |
| Amazon | 10,096,987 | 3,984,432 | 197,990 | 0.395 | 0.084 |
# 1. Creamos la etiqueta flotante para cada distribuidor
r_dist_interactivo <- r_dist %>%
mutate(
etiqueta = paste0(
"<b>Distribuidor:</b> ", distribuidor, "<br>",
"<b>Ventas totales:</b> ", dollar(ventas), "<br>",
"<b>Margen operativo:</b> ", percent(margen, accuracy = 0.1)
)
)
# 2. Construimos el gráfico conservando tus etiquetas de texto y colores
grafico_dist <- ggplot(r_dist_interactivo, aes(x = reorder(distribuidor, ventas), y = ventas, text = etiqueta)) +
geom_col(aes(fill = margen)) +
geom_text(aes(label = percent(margen, accuracy = 0.1)), hjust = -0.1) +
coord_flip() +
scale_y_continuous(labels = label_dollar(scale = 1e-6, suffix = "M"),
expand = expansion(mult = c(0, 0.15))) +
scale_fill_gradient(low = "#9ecae1", high = "#08519c", labels = percent) +
labs(title = "Ventas por distribuidor (etiqueta = margen operativo)",
x = NULL, y = "Ventas (millones USD)", fill = "Margen")
# 3. Convertimos a interactivo
ggplotly(grafico_dist, tooltip = "text")
r_reg <- resumen_por(datos, region)
kable(r_reg, digits = 3, format.args = list(big.mark = ","))
| region | ventas | utilidad | unidades | margen | part_ventas |
|---|---|---|---|---|---|
| West | 36,436,157 | 13,017,584 | 686,985 | 0.357 | 0.303 |
| Northeast | 25,078,267 | 9,732,774 | 501,279 | 0.388 | 0.209 |
| Southeast | 21,374,436 | 8,393,059 | 407,000 | 0.393 | 0.178 |
| South | 20,603,356 | 9,221,605 | 492,260 | 0.448 | 0.171 |
| Midwest | 16,674,434 | 6,859,945 | 391,337 | 0.411 | 0.139 |
# 1. Creamos la etiqueta flotante únicamente con ventas y porcentaje de margen
r_reg_interactivo <- r_reg %>%
mutate(
etiqueta = paste0(
"<b>Ventas:</b> ", dollar(ventas), "<br>",
"<b>Margen:</b> ", percent(margen, accuracy = 0.1)
)
)
# 2. Construimos el gráfico sin el eje secundario innecesario
grafico_reg <- ggplot(r_reg_interactivo, aes(x = reorder(region, ventas))) +
geom_col(aes(y = ventas, text = etiqueta), fill = "#2c7fb8") +
geom_point(aes(y = margen * max(ventas) / 0.5, text = etiqueta), color = "red", size = 4) +
scale_y_continuous(labels = label_dollar(scale = 1e-6, suffix = "M")) +
coord_flip() +
labs(title = "Ventas y margen por región", x = NULL, y = "Ventas (millones USD)")
# 3. Convertimos a interactivo y centramos el título
ggplotly(grafico_reg, tooltip = "text") %>%
layout(title = list(text = "Ventas y margen por región", x = 0.5, xanchor = "center"))
# 1. Preparamos los datos y creamos el texto personalizado para el cursor
mapa_datos <- datos %>%
group_by(estado) %>%
summarise(ventas = sum(ventas_total, na.rm = TRUE), .groups = "drop") %>%
mutate(
state = as.character(estado),
etiqueta = paste0(
"<b>Estado:</b> ", state, "<br>",
"<b>Ventas totales:</b> ", dollar(ventas)
)
)
# 2. Creamos el mapa base asignando la etiqueta al parámetro 'text'
mapa_base <- plot_usmap(data = mapa_datos, values = "ventas", color = "white", linewidth = 0.3) +
aes(text = etiqueta) +
scale_fill_continuous(
low = "#deebf7", # Azul claro para menos ventas
high = "#08306b", # Azul oscuro para mayores ventas
name = "Ventas totales",
labels = label_dollar(scale = 1e-6, suffix = "M")
) +
labs(title = "Distribución geográfica de ventas en Estados Unidos") +
theme(
legend.position = "right",
plot.title = element_text(face = "bold", size = 14)
)
# 3. Convertimos el gráfico a interactivo indicando que muestre solo 'text'
ggplotly(mapa_base, tooltip = "text")
r_est <- resumen_por(datos, estado)
# Top 10 estados por ventas (versión interactiva)
grafico_top10 <- r_est %>%
slice_head(n = 10) %>%
mutate(
etiqueta = paste0(
"<b>Estado:</b> ", estado, "<br>",
"<b>Ventas totales:</b> ", dollar(ventas), "<br>",
"<b>Margen:</b> ", percent(margen, accuracy = 0.1)
)
) %>%
ggplot(aes(x = reorder(estado, ventas), y = ventas, fill = margen, text = etiqueta)) +
geom_col() +
coord_flip() +
scale_y_continuous(labels = label_dollar(scale = 1e-6, suffix = "M")) +
scale_fill_gradient(low = "#fee0d2", high = "#de2d26", labels = percent) +
labs(title = "Top 10 estados por ventas", x = NULL,
y = "Ventas (millones USD)", fill = "Margen")
ggplotly(grafico_top10, tooltip = "text")
# Estados con mayor margen (con ventas relevantes)
r_est %>% filter(ventas > median(ventas)) %>% arrange(desc(margen)) %>%
slice_head(n = 5) %>% kable(digits = 3, format.args = list(big.mark = ","))
| estado | ventas | utilidad | unidades | margen | part_ventas |
|---|---|---|---|---|---|
| Alabama | 2,513,424 | 1,368,206.4 | 63,327 | 0.544 | 0.021 |
| Tennessee | 2,567,190 | 1,269,585.1 | 66,077 | 0.495 | 0.021 |
| Michigan | 2,287,283 | 1,050,351.5 | 50,095 | 0.459 | 0.019 |
| Idaho | 2,742,753 | 1,222,558.9 | 63,827 | 0.446 | 0.023 |
| Mississippi | 2,218,609 | 959,892.2 | 56,814 | 0.433 | 0.018 |
r_prod <- resumen_por(datos, producto) %>%
mutate(
etiqueta = paste0(
"<b>Producto:</b> ", producto, "<br>",
"<b>Ventas:</b> ", dollar(ventas), "<br>",
"<b>Margen:</b> ", percent(margen, accuracy = 0.1), "<br>",
"<b>Unidades:</b> ", comma(unidades)
)
)
g_prod <- ggplot(r_prod, aes(x = ventas, y = margen, size = unidades, text = etiqueta)) +
geom_point(color = "#08519c", alpha = 0.8) +
geom_text(aes(label = producto), vjust = -1.2, size = 3.5, check_overlap = TRUE) +
scale_x_continuous(labels = label_dollar(scale = 1e-6, suffix = "M"),
expand = expansion(mult = 0.18)) + # Evita cortes a los lados
scale_y_continuous(labels = percent_format(accuracy = 1),
expand = expansion(mult = 0.15)) + # Evita cortes arriba y abajo
scale_size_continuous(range = c(4, 15), labels = comma) + # Agranda puntos pequeños y quita '4e+05'
labs(
title = "Portafolio de Productos: Ventas vs. Margen Operativo",
x = "Ventas (millones USD)",
y = "Margen operativo",
size = "Unidades"
) +
theme_minimal()
ggplotly(g_prod, tooltip = "text")
r_met <- resumen_por(datos, metodo_venta)
kable(r_met, digits = 3, format.args = list(big.mark = ","))
| metodo_venta | ventas | utilidad | unidades | margen | part_ventas |
|---|---|---|---|---|---|
| Online | 44,965,657 | 19,552,538 | 939,093 | 0.435 | 0.374 |
| Outlet | 39,536,618 | 14,913,301 | 849,778 | 0.377 | 0.329 |
| In-store | 35,664,375 | 12,759,129 | 689,990 | 0.358 | 0.297 |
ggplot(r_met, aes(x = "", y = ventas, fill = metodo_venta)) +
geom_col(width = 1) +
coord_polar("y") +
geom_text(aes(label = percent(part_ventas, accuracy = 0.1)),
position = position_stack(vjust = 0.5), color = "white") +
theme_void() +
labs(title = "Participación en ventas por método de venta", fill = NULL)
datos %>%
group_by(distribuidor, metodo_venta) %>%
summarise(margen = sum(utilidad_operativa) / sum(ventas_total), .groups = "drop") %>%
ggplot(aes(x = metodo_venta, y = distribuidor, fill = margen)) +
geom_tile(color = "white") +
geom_text(aes(label = percent(margen, accuracy = 0.1))) +
scale_fill_gradient(low = "#fff7bc", high = "#d95f0e", labels = percent) +
labs(title = "Margen operativo: distribuidor x método de venta",
x = NULL, y = NULL, fill = "Margen")
# 1. Agrupamos los datos y creamos la etiqueta únicamente con las ventas
datos_cruce2 <- datos %>%
group_by(region, producto) %>%
summarise(ventas = sum(ventas_total, na.rm = TRUE), .groups = "drop") %>%
mutate(
etiqueta = paste0("<b>Ventas:</b> ", dollar(ventas))
)
# 2. Construimos el gráfico agrupado conectando la etiqueta
grafico_cruce2 <- ggplot(datos_cruce2, aes(x = region, y = ventas, fill = producto, text = etiqueta)) +
geom_col(position = "dodge") +
scale_y_continuous(labels = label_dollar(scale = 1e-6, suffix = "M")) +
labs(title = "Ventas por región y producto", x = NULL,
y = "Ventas (millones USD)", fill = "Producto")
# 3. Convertimos a interactivo
ggplotly(grafico_cruce2, tooltip = "text")
# 1. Creamos el boxplot comparando el margen operativo por método de venta
grafico_box <- ggplot(datos, aes(x = metodo_venta, y = margen_operativo, fill = metodo_venta)) +
geom_boxplot(alpha = 0.85, outlier.color = "#de2d26", outlier.alpha = 0.6) +
scale_y_continuous(labels = percent_format(accuracy = 1)) +
scale_fill_manual(values = c("#08519c", "#3182bd", "#6baed6")) +
labs(
title = "Distribución del margen operativo por método de venta",
x = NULL,
y = "Margen operativo"
) +
theme_minimal() +
theme(
legend.position = "none",
plot.title = element_text(face = "bold", size = 14)
)
# 2. Lo convertimos a interactivo (Plotly calcula automáticamente mediana y cuartiles)
ggplotly(grafico_box)
```
Resumen Ejecutivo: El negocio de Adidas en Estados Unidos muestra una sólida generación de ingresos liderada por mercados clave y categorías urbanas, pero presenta una oportunidad inmediata de maximizar la utilidad neta migrando volumen hacia el canal digital y ajustando la política de descuentos en segmentos de alta rotación.
West Gear (27,0 %), Foot Locker (24,2 %) y **Sports Direct* (20,5 %) suman cerca del 72 % de las ventas. underline(Sports Direct) es el distribuidor más eficiente, con un margen de 43,2 % frente al 37,6 % de West Gear, que es el que más vende. Walmart y Amazon aportan cada uno cerca del 8 % de las ventas.
Genera el 37 % de las ventas y el 41 % de la utilidad, con un margen de 43,5 %.Convirtiendolo en el formato más rentable de la operación, alcanzando una mediana de margen operativo del 47% (con transacciones pico de hasta el 80%) aportando utilidad limpia sin exigir grandes inventarios. Es el único método cuya participación en la utilidad es menor que su participación en ventas, junto con una brecha menor en Outlet. La tienda física (In-store) aporta el 30 % de las ventas pero solo el 27 % de la utilidad, con el margen más bajo (35,8 %). Es el único método cuya participación en la utilidad es menor que su participación en ventas, junto con una brecha menor en Outlet producto de los descuentos agresivos.
Los estados grandes no son los más rentables. New York, California, Florida y Texas suman el 26 % de las ventas. California tiene un margen de 34,5 %, mientras que Texas alcanza 42,8 %. Estados pequeños como Alabama (54,4 %) y Tennessee (49,5 %) superan ampliamente el margen promedio. En el extremo opuesto están Hawái (28,5 %) y Washington (28,8 %).
La combinación importa más que cada variable por separado. - Los mejores márgenes aparecen en Sports Direct (Online 45,1 % y Outlet 44,6 %) y en West Gear Online (44,5 %). - Los peores aparecen en Walmart In-store (30,0 %) y West Gear Outlet (34,2 %). - En la región West, Women’s Apparel tiene un margen de solo 29,8 %, mientras que en South el mismo producto llega a 52,1 %.
Men’s Street Footwear es el producto estrella. Aporta el 23,0 % de las ventas y el 24,6 % de la utilidad, con un margen de 42 %. Men’s Athletic Footwear tiene el margen más bajo (36,1 %). El calzado en conjunto representa cerca del 66 % de las ventas.