Análisis de la estructura comercial de la categoría Verduras en plazaVea

Enunciado del caso

Eres analista de datos en una startup de delivery de alimentos saludables que busca fortalecer su estrategia comercial en la categoría de verduras. El equipo necesita entender cómo está estructurada la oferta de plazaVea, uno de los supermercados más importantes del Perú, para identificar:

  • qué subcategorías concentran más productos,
  • qué marcas dominan la categoría,
  • qué nivel de presencia tiene la marca propia,
  • qué tan activa es la política promocional,
  • y cómo está distribuida la oferta entre plazaVea y marcas aliadas.

A partir de técnicas de web scraping en R, deberás recolectar información desde la página de la categoría Verduras y construir tablas que permitan responder preguntas de negocio útiles para la toma de decisiones comerciales.


Librerías necesarias

library(rvest)
library(httr)
library(dplyr)
library(stringr)
library(tidyr)
library(ggplot2)

Paso 1: Leer la página web

url <- "https://www.plazavea.com.pe/frutas-y-verduras/verduras"

pagina_web <- GET(
  url,
  user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0 Safari/537.36")
)

contenido <- content(pagina_web, "text", encoding = "UTF-8")
doc <- read_html(contenido)

texto_total <- doc %>% html_text2()

Pregunta 1: ¿Cuántos productos hay en total en la categoría Verduras?

total_productos <- texto_total %>%
  str_extract("Produtos encontrados:\\s*\\d+") %>%
  str_extract("\\d+") %>%
  as.numeric()

print(paste("Total de productos encontrados:", total_productos))
## [1] "Total de productos encontrados: 315"

Interpretación

Este indicador permite dimensionar el tamaño de la categoría y sirve como base para calcular participaciones relativas de subcategorías, marcas y promociones.

Pregunta 2: ¿Cuáles son las principales subcategorías de verduras?

subcategorias_df <- doc %>%
  html_elements("a") %>%
  html_text2() %>%
  str_trim() %>%
  .[str_detect(., "\\(\\d+\\)$")] %>%
  .[str_detect(., "Zapallo|Cebolla|Brócoli|Apio|Lechuga|Hierbas|Arveja|Tomate|Papa|Zanahoria|Verduras Congeladas|Hongos|Ensaladas|Limón|Choclo|Verduras Orientales")] %>%
  unique() %>%
  tibble(texto = .) %>%
  mutate(
    subcategoria = str_remove(texto, "\\s*\\(\\d+\\)$"),
    n_productos = as.numeric(str_extract(texto, "\\d+")),
    participacion = round(100 * n_productos / total_productos, 2)
  ) %>%
  select(subcategoria, n_productos, participacion) %>%
  arrange(desc(n_productos))

subcategorias_df
## # A tibble: 16 × 3
##    subcategoria                                  n_productos participacion
##    <chr>                                               <dbl>         <dbl>
##  1 Lechuga, Espinaca y Hojas Verdes                       59         18.7 
##  2 Verduras Congeladas                                    44         14.0 
##  3 Hongos, Setas y Germinados                             31          9.84
##  4 Ensaladas y Verduras Picadas                           31          9.84
##  5 Papa, Camote, Yuca y Otros Tubérculos                  25          7.94
##  6 Cebolla, Ajo, Rocoto y Ají                             20          6.35
##  7 Hierbas e Infusiones                                   17          5.4 
##  8 Tomate, Pepino y Pimiento                              17          5.4 
##  9 Apio, Espárrago y Otros Tallos                         13          4.13
## 10 Brócoli, Coliflor, Alcachofa y Col                     12          3.81
## 11 Choclo                                                 12          3.81
## 12 Zanahoria, Beterraga, Rabanito y Otras Raíces          11          3.49
## 13 Zapallo, Berenjena y Caigua                             7          2.22
## 14 Arveja, Vainita, Haba y Otras Legumbres                 7          2.22
## 15 Limón                                                   5          1.59
## 16 Verduras Orientales                                     5          1.59

Interpretación

Esta tabla permite identificar cuáles son las familias de productos con mayor amplitud de surtido dentro de la categoría.

Pregunta 3: ¿Qué subcategorías concentran la mayor oferta?

top_subcategorias <- subcategorias_df %>%
  slice_max(order_by = n_productos, n = 5)

top_subcategorias
## # A tibble: 5 × 3
##   subcategoria                          n_productos participacion
##   <chr>                                       <dbl>         <dbl>
## 1 Lechuga, Espinaca y Hojas Verdes               59         18.7 
## 2 Verduras Congeladas                            44         14.0 
## 3 Hongos, Setas y Germinados                     31          9.84
## 4 Ensaladas y Verduras Picadas                   31          9.84
## 5 Papa, Camote, Yuca y Otros Tubérculos          25          7.94
ggplot(top_subcategorias, aes(x = reorder(subcategoria, n_productos), y = n_productos)) +
  geom_col() +
  coord_flip() +
  labs(
    title = "Top 5 subcategorías con mayor número de productos",
    x = "Subcategoría",
    y = "Número de productos"
  )

Interpretación

Las subcategorías más grandes suelen ser las más relevantes comercialmente, ya sea por rotación, estacionalidad o amplitud del portafolio.

Pregunta 4: ¿Qué marcas dominan la categoría Verduras?

marcas_df <- doc %>%
  html_elements("a") %>%
  html_text2() %>%
  str_trim() %>%
  .[str_detect(., "\\(\\d+\\)$")] %>%
  .[str_detect(., "AGE|ARCOR|AVIKO|BELL'S|BELL'S FRESCOS|BEM BRASIL|EL HUERTO|EL SABOR|FEST|GENÉRICO|GREEN FOOD|GUVA|HUERCASA|LA CHIGUATENA|Lambweston|MANANTIALES|PACCU|PLAZA VEA|PRACTIFOOD|SAN SEN|SOFIT|TODO SANO|VERDE PURO|VIVA|WILLKA")] %>%
  unique() %>%
  tibble(texto = .) %>%
  mutate(
    marca = str_remove(texto, "\\s*\\(\\d+\\)$"),
    n_productos = as.numeric(str_extract(texto, "\\d+")),
    participacion = round(100 * n_productos / total_productos, 2)
  ) %>%
  select(marca, n_productos, participacion) %>%
  arrange(desc(n_productos))

marcas_df
## # A tibble: 25 × 3
##    marca          n_productos participacion
##    <chr>                <dbl>         <dbl>
##  1 PLAZA VEA              125         39.7 
##  2 BELL'S                  51         16.2 
##  3 BELL'S FRESCOS          33         10.5 
##  4 GENÉRICO                22          6.98
##  5 VIVA                    17          5.4 
##  6 PRACTIFOOD              14          4.44
##  7 VERDE PURO              10          3.17
##  8 MANANTIALES              6          1.9 
##  9 AGE                      5          1.59
## 10 BEM BRASIL               5          1.59
## # ℹ 15 more rows

Interpretación

Este análisis permite detectar concentración de portafolio por marca y evaluar el peso relativo de la marca propia frente a terceros.

Pregunta 5: ¿Qué porcentaje de productos corresponde a la marca propia plazaVea?

marca_propia_df <- marcas_df %>%
  filter(marca == "PLAZA VEA") %>%
  mutate(
    porcentaje_marca_propia = round(100 * n_productos / total_productos, 2)
  )

marca_propia_df
## # A tibble: 1 × 4
##   marca     n_productos participacion porcentaje_marca_propia
##   <chr>           <dbl>         <dbl>                   <dbl>
## 1 PLAZA VEA         125          39.7                    39.7

Interpretación

Una mayor presencia de marca propia puede reflejar estrategia de margen, control de surtido y posicionamiento de valor.

Pregunta 6: ¿Qué marcas tienen mayor presencia dentro de la categoría?

top_marcas <- marcas_df %>%
  slice_max(order_by = n_productos, n = 10)

top_marcas
## # A tibble: 11 × 3
##    marca          n_productos participacion
##    <chr>                <dbl>         <dbl>
##  1 PLAZA VEA              125         39.7 
##  2 BELL'S                  51         16.2 
##  3 BELL'S FRESCOS          33         10.5 
##  4 GENÉRICO                22          6.98
##  5 VIVA                    17          5.4 
##  6 PRACTIFOOD              14          4.44
##  7 VERDE PURO              10          3.17
##  8 MANANTIALES              6          1.9 
##  9 AGE                      5          1.59
## 10 BEM BRASIL               5          1.59
## 11 PACCU                    5          1.59

Pregunta 7: ¿Qué tipos de descuento aparecen en la categoría?

descuentos_df <- doc %>%
  html_elements("a") %>%
  html_text2() %>%
  str_trim() %>%
  .[str_detect(., "Tarjeta Oh!|sin descuentos")] %>%
  unique() %>%
  tibble(texto = .) %>%
  mutate(
    tipo_descuento = str_remove(texto, "\\s*\\(\\d+\\)$"),
    n_productos = as.numeric(str_extract(texto, "\\d+")),
    participacion = round(100 * n_productos / total_productos, 2)
  ) %>%
  select(tipo_descuento, n_productos, participacion) %>%
  arrange(desc(n_productos))

descuentos_df
## # A tibble: 2 × 3
##   tipo_descuento  n_productos participacion
##   <chr>                 <dbl>         <dbl>
## 1 sin descuentos          287         91.1 
## 2 con Tarjeta Oh!           5          1.59

Pregunta 8: ¿Qué proporción del surtido está sin descuentos?

sin_descuento_df <- descuentos_df %>%
  filter(str_detect(tipo_descuento, regex("sin descuentos", ignore_case = TRUE)))

sin_descuento_df
## # A tibble: 1 × 3
##   tipo_descuento n_productos participacion
##   <chr>                <dbl>         <dbl>
## 1 sin descuentos         287          91.1

Interpretación

Una alta proporción sin descuentos puede indicar estabilidad de precios, menor presión promocional o una categoría menos agresiva comercialmente.

Pregunta 9: ¿Cómo se distribuye la oferta entre plazaVea y Marcas Aliadas?

vendedores_df <- doc %>%
  html_elements("a") %>%
  html_text2() %>%
  str_trim() %>%
  .[str_detect(., "plazaVea \\(|Marcas Aliadas \\(")] %>%
  unique() %>%
  tibble(texto = .) %>%
  mutate(
    vendedor = str_remove(texto, "\\s*\\(\\d+\\)$"),
    n_productos = as.numeric(str_extract(texto, "\\d+")),
    participacion = round(100 * n_productos / total_productos, 2)
  ) %>%
  select(vendedor, n_productos, participacion) %>%
  arrange(desc(n_productos))

vendedores_df
## # A tibble: 2 × 3
##   vendedor       n_productos participacion
##   <chr>                <dbl>         <dbl>
## 1 plazaVea               292          92.7
## 2 Marcas Aliadas          23           7.3

Pregunta 10: Construya una base consolidada de estructura de la categoría

estructura_verduras_df <- bind_rows(
  subcategorias_df %>%
    mutate(tipo = "subcategoria", nombre = subcategoria) %>%
    select(tipo, nombre, n_productos, participacion),
  
  marcas_df %>%
    mutate(tipo = "marca", nombre = marca) %>%
    select(tipo, nombre, n_productos, participacion),
  
  descuentos_df %>%
    mutate(tipo = "descuento", nombre = tipo_descuento) %>%
    select(tipo, nombre, n_productos, participacion),
  
  vendedores_df %>%
    mutate(tipo = "vendedor", nombre = vendedor) %>%
    select(tipo, nombre, n_productos, participacion)
)

estructura_verduras_df
## # A tibble: 45 × 4
##    tipo         nombre                                n_productos participacion
##    <chr>        <chr>                                       <dbl>         <dbl>
##  1 subcategoria Lechuga, Espinaca y Hojas Verdes               59         18.7 
##  2 subcategoria Verduras Congeladas                            44         14.0 
##  3 subcategoria Hongos, Setas y Germinados                     31          9.84
##  4 subcategoria Ensaladas y Verduras Picadas                   31          9.84
##  5 subcategoria Papa, Camote, Yuca y Otros Tubérculos          25          7.94
##  6 subcategoria Cebolla, Ajo, Rocoto y Ají                     20          6.35
##  7 subcategoria Hierbas e Infusiones                           17          5.4 
##  8 subcategoria Tomate, Pepino y Pimiento                      17          5.4 
##  9 subcategoria Apio, Espárrago y Otros Tallos                 13          4.13
## 10 subcategoria Brócoli, Coliflor, Alcachofa y Col             12          3.81
## # ℹ 35 more rows