Análisis de Mercado de Tiendas MASS

Contexto

Eres un analista de marketing en una empresa consultora y has sido asignado para realizar un estudio de mercado sobre Tiendas MASS, una importante cadena de retail peruana. Tu tarea es analizar al menos 40 productos de su catálogo online, examinando precios, características y valoraciones de los clientes para identificar insights relevantes que puedan guiar estrategias de marketing.

Objetivos

  1. Extraer datos del sitio web de Tiendas MASS Perú

  2. Analizar patrones de precios y categorías de productos

  3. Realizar análisis de sentimiento en los comentarios de los clientes

  4. Generar insights accionables para estrategias de marketing

Tareas a desarrollar

library(rvest)
library(dplyr)
## 
## Adjuntando el paquete: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(ggplot2)
library(tidyr)
library(stringr)
library(tidytext)
library(syuzhet)
library(kableExtra)
## 
## Adjuntando el paquete: 'kableExtra'
## The following object is masked from 'package:dplyr':
## 
##     group_rows
library(DT)

1. Realizar web scraping para obtener un dataframe

Extraerás los datos de la página web creada y los estructurarás en un dataframe con las siguientes variables:

  • ID del producto

  • Nombre del producto

  • Precio

  • Categoría

  • Descripción

  • Comentarios de clientes

# Leer el HTML
html_content <- read_html("tienda_mass_peru_completa.html")

# Extraer información de productos
productos <- html_content %>% html_elements(".product-card")

# Crear vectores vacíos para almacenar la información
ids <- c()
nombres <- c()
precios <- c()
categorias <- c()
descripciones <- c()
comentarios <- c()

# Iterar sobre cada producto para extraer la información
for (i in 1:length(productos)) {
  producto <- productos[i]
  
  # Extraer información básica
  categoria <- producto %>% html_element(".product-category") %>% html_text()
  nombre <- producto %>% html_element(".product-title") %>% html_text()
  precio <- producto %>% html_element(".product-price") %>% html_text()
  descripcion <- producto %>% html_element(".product-description") %>% html_text()
  
  # Extraer comentarios
  comentarios_producto <- producto %>% html_elements(".review-text") %>% html_text()
  
  # Si hay comentarios, agregarlos individualmente
  if (length(comentarios_producto) > 0) {
    for (comentario in comentarios_producto) {
      ids <- c(ids, i)
      nombres <- c(nombres, nombre)
      precios <- c(precios, precio)
      categorias <- c(categorias, categoria)
      descripciones <- c(descripciones, descripcion)
      comentarios <- c(comentarios, comentario)
    }
  } else {
    # Si no hay comentarios, agregar una fila con NA
    ids <- c(ids, i)
    nombres <- c(nombres, nombre)
    precios <- c(precios, precio)
    categorias <- c(categorias, categoria)
    descripciones <- c(descripciones, descripcion)
    comentarios <- c(comentarios, NA)
  }
}

# Crear el dataframe
productos_df <- data.frame(
  id = ids,
  nombre = nombres,
  precio = precios,
  categoria = categorias,
  descripcion = descripciones,
  comentario = comentarios,
  stringsAsFactors = FALSE
)

# Limpiar la columna de precios (eliminar "S/" y convertir a numérico)
productos_df <- productos_df %>%
  mutate(
    precio_limpio = as.numeric(gsub("[^0-9.]", "", gsub("S/ ", "", precio)))
  )

# Mostrar las primeras filas del dataframe
head(productos_df) %>% 
  kable() %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
id nombre precio categoria descripcion comentario precio_limpio
1 Smart TV LED 55” 4K UHD S/ 1899.00 Electrohogar Televisor inteligente con resolución 4K, Android TV y conexión Wi-Fi integrada. Perfecto para disfrutar de tus series favoritas. Increíble relación calidad-precio, lo volvería a comprar. 1899
1 Smart TV LED 55” 4K UHD S/ 1899.00 Electrohogar Televisor inteligente con resolución 4K, Android TV y conexión Wi-Fi integrada. Perfecto para disfrutar de tus series favoritas. Producto correcto, sin grandes sorpresas pero hace su trabajo. 1899
2 Lavadora Automática 18 kg S/ 2299.00 Electrohogar Lavadora de carga frontal con capacidad de 18 kg, múltiples programas de lavado y tecnología inverter que ahorra energía. Funciona de maravilla, muy intuitivo y bien diseñado. 2299
2 Lavadora Automática 18 kg S/ 2299.00 Electrohogar Lavadora de carga frontal con capacidad de 18 kg, múltiples programas de lavado y tecnología inverter que ahorra energía. Funciona como debe, instalación un poco complicada. 2299
3 Refrigeradora No Frost 480L S/ 2599.00 Electrohogar Refrigeradora con tecnología No Frost, dispensador de agua y hielo, y compartimentos especializados para frutas y verduras. Muy buena calidad, fácil de usar y el precio está bien. 2599
3 Refrigeradora No Frost 480L S/ 2599.00 Electrohogar Refrigeradora con tecnología No Frost, dispensador de agua y hielo, y compartimentos especializados para frutas y verduras. Cumple con lo prometido, nada extraordinario pero está bien. 2599

3. Análisis gráfico y estadístico

Generarás visualizaciones que permitan:

  • Distribución de precios por categoría
ggplot(productos_df %>% distinct(nombre, .keep_all = TRUE), 
       aes(x = categoria, y = precio_limpio, fill = categoria)) +
  geom_boxplot(alpha = 0.7) +
  labs(title = "Distribución de Precios por Categoría",
       x = "Categoría",
       y = "Precio (S/)") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1),
        legend.position = "none") +
  scale_y_continuous(labels = scales::dollar_format(prefix = "S/ "))

4. Análisis de sentimiento con diccionario NRC

Aplicarás análisis de sentimiento a los comentarios de productos utilizando:

  • Diccionario NRC para identificar emociones
# Primero: Crear comentarios_tokens a partir del dataframe de productos
comentarios_tokens <- productos_df %>%
  # Filtrar solo comentarios no vacíos
  filter(!is.na(comentario)) %>%
  # Tokenizar: convertir cada palabra en una fila
  unnest_tokens(word, comentario) %>%
  # Contar frecuencia de cada palabra
  count(word, sort = TRUE)  # sort = TRUE para ordenar de mayor a menor frecuencia

# Mostrar las palabras más frecuentes
head(comentarios_tokens, 10) %>% 
  kable() %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
word n
bien 35
funciona 29
muy 29
producto 27
de 23
y 23
pero 19
el 18
calidad 17
está 17
# Ahora sí podemos continuar con el análisis NRC...

# Obtener sentimientos con NRC
# El diccionario NRC contiene palabras asociadas a 10 categorías emocionales:
# positive, negative, anger, anticipation, disgust, fear, joy, sadness, surprise, trust
nrc <- get_sentiments("nrc")

# Unir con nuestras palabras para clasificarlas según concordancia
# inner_join() fusiona los dos dataframes manteniendo solo las palabras que existen en ambos
# Esto significa que solo las palabras que están en el diccionario NRC serán analizadas
# by = "word" indica que la unión se hace mediante la columna "word" que existe en ambos dataframes
Base_nrc <- comentarios_tokens %>% 
  inner_join(nrc, by = "word")  # Unimos el data de sentimientos con nuestras palabras

# Ordenamos de forma descendente por frecuencia
# arrange(-n) ordena el dataframe por la columna "n" (frecuencia) en orden descendente
# El signo negativo (-) antes de "n" indica orden descendente
# Esto nos permite ver primero las palabras emocionales más frecuentes
Base_nrc <- arrange(Base_nrc, -n)

# Mostrar palabras más frecuentes por sentimiento
# head(15) muestra las primeras 15 filas del dataframe ordenado
# kable() y kable_styling() formatean la tabla para una visualización más profesional
# Esto nos ayuda a identificar rápidamente las palabras emocionales más relevantes
head(Base_nrc, 15) %>% 
  kable() %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
word n sentiment
sin 8 anger
sin 8 disgust
sin 8 fear
sin 8 negative
sin 8 sadness
general 4 positive
general 4 trust
# Resumen por tipo de sentimiento/emoción
# group_by(sentiment) agrupa los datos por cada categoría emocional del diccionario NRC
# Esto nos permite analizar cada emoción por separado
resumen_nrc <- Base_nrc %>%
  group_by(sentiment) %>%
  
  # summarise() crea un resumen estadístico para cada grupo (emoción)
  # total_palabras = sum(n): suma la frecuencia de todas las palabras en cada emoción
  # palabras_unicas = n_distinct(word): cuenta cuántas palabras únicas hay en cada emoción
  # .groups = "drop": elimina la agrupación después del resumen
  summarise(
    total_palabras = sum(n),          # Total de apariciones de palabras en cada emoción
    palabras_unicas = n_distinct(word), # Cantidad de palabras diferentes en cada emoción
    .groups = "drop"
  ) %>%
  
  # arrange(-total_palabras) ordena las emociones de mayor a menor frecuencia total
  # Esto nos muestra qué emociones son las más predominantes en los comentarios
  arrange(-total_palabras)

# Mostrar el resumen de sentimientos en formato de tabla profesional
# kable(caption = "...") añade un título descriptivo a la tabla
# El formato ayuda a visualizar qué emociones son más frecuentes en el análisis
resumen_nrc %>% 
  kable(caption = "Resumen de sentimientos NRC - Frecuencia de palabras por emoción") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Resumen de sentimientos NRC - Frecuencia de palabras por emoción
sentiment total_palabras palabras_unicas
anger 8 1
disgust 8 1
fear 8 1
negative 8 1
sadness 8 1
positive 4 1
trust 4 1

Explicación adicional de lo que hace cada parte:

  1. get_sentiments("nrc"): Carga el léxico NRC que asigna palabras a categorías emocionales. Cada palabra puede pertenecer a una o más categorías.

  2. inner_join(nrc, by = "word"): Es el corazón del análisis. Solo mantiene las palabras de los comentarios que tienen significado emocional según el diccionario NRC.

  3. arrange(-n): Ordenamiento crucial para el análisis, ya que nos muestra inmediatamente las palabras emocionales más frecuentes.

  4. group_by(sentiment): La agrupación por emoción nos permite cambiar de un análisis por palabra individual a un análisis por categoría emocional.

  5. summarise(): Calcula dos métricas importantes:

    • total_palabras: Intensidad de la emoción (cuántas veces aparece)

    • palabras_unicas: Riqueza vocabular de la emoción (cuántas palabras diferentes)

  6. arrange(-total_palabras): Ordena las emociones por importancia, mostrando primero las que tienen mayor presencia en los comentarios.

PLUS [Gráficos]

1. Gráfico de barras de emociones principales

# Gráfico de barras horizontales de emociones
ggplot(resumen_nrc, aes(x = reorder(sentiment, total_palabras), 
                       y = total_palabras, 
                       fill = sentiment)) +
  geom_col(alpha = 0.8) +  # Barras con transparencia
  coord_flip() +  # Barras horizontales para mejor lectura
  labs(title = "Frecuencia de Emociones en Comentarios",
       subtitle = "Análisis con diccionario NRC",
       x = "Tipo de Emoción",
       y = "Total de Palabras",
       caption = "Fuente: Comentarios de productos Tiendas MASS") +
  theme_minimal() +  # Tema limpio y moderno
  theme(legend.position = "none")  # Oculta la leyenda de colores

# Explicación: Este gráfico muestra qué emociones son las más frecuentes
# en los comentarios. Las barras horizontales facilitan la lectura de
# las categorías. El orden descendente ayuda a identificar rápidamente
# las emociones predominantes.

2. Gráfico de proporción de emociones

# Calcular porcentajes
resumen_nrc <- resumen_nrc %>%
  mutate(porcentaje = round(total_palabras/sum(total_palabras)*100, 1))

# Gráfico de torta (pie chart)
ggplot(resumen_nrc, aes(x = "", y = porcentaje, fill = sentiment)) +
  geom_bar(stat = "identity", width = 1) +
  coord_polar("y", start = 0) +
  geom_text(aes(label = paste0(porcentaje, "%")), 
            position = position_stack(vjust = 0.5)) +
  labs(title = "Distribución Porcentual de Emociones",
       fill = "Emoción") +
  theme_void()  # Tema minimalista para gráficos de torta

# Explicación: Este gráfico circular muestra la proporción de cada emoción
# en relación al total. Es útil para entender la distribución relativa
# de las emociones en los comentarios.

3. Comparación entre emociones positivas y negativas

# Clasificar emociones en positivas, negativas y neutrales
emociones_clasificadas <- resumen_nrc %>%
  mutate(tipo = case_when(
    sentiment %in% c("positive", "joy", "trust", "surprise", "anticipation") ~ "Positivas",
    sentiment %in% c("negative", "anger", "disgust", "fear", "sadness") ~ "Negativas",
    TRUE ~ "Neutrales"
  )) %>%
  group_by(tipo) %>%
  summarise(total = sum(total_palabras), .groups = "drop")

ggplot(emociones_clasificadas, aes(x = tipo, y = total, fill = tipo)) +
  geom_col(alpha = 0.8) +
  labs(title = "Comparación: Emociones Positivas vs Negativas",
       x = "Tipo de Emoción",
       y = "Total de Palabras") +
  theme_minimal() +
  theme(legend.position = "none")

# Explicación: Este gráfico simplifica el análisis agrupando las emociones
# en tres categorías principales. Ayuda a tener una visión general del
# balance emocional de los comentarios.

4. Gráfico de burbujas: frecuencia vs riqueza vocabular

ggplot(resumen_nrc, aes(x = total_palabras, y = palabras_unicas, 
                       size = total_palabras, color = sentiment)) +
  geom_point(alpha = 0.7) +  # Puntos con transparencia
  geom_text(aes(label = sentiment), vjust = -1, size = 3) +  # Etiquetas
  scale_size(range = c(3, 10)) +  # Rango de tamaños de burbujas
  labs(title = "Relación: Frecuencia vs Riqueza Vocabular por Emoción",
       x = "Total de Palabras (Frecuencia)",
       y = "Palabras Únicas (Riqueza Vocabular)",
       size = "Frecuencia",
       color = "Emoción") +
  theme_minimal()

# Explicación: Este gráfico de dispersión muestra la relación entre
# la frecuencia total de palabras y la diversidad vocabular de cada emoción.
# Las burbujas más grandes indican emociones más frecuentes.