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.
Extraer datos del sitio web de Tiendas MASS Perú
Analizar patrones de precios y categorías de productos
Realizar análisis de sentimiento en los comentarios de los clientes
Generar insights accionables para estrategias de marketing
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)
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 |
Generarás visualizaciones que permitan:
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/ "))
Aplicarás análisis de sentimiento a los comentarios de productos utilizando:
# 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)
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 |
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.
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.
arrange(-n)
: Ordenamiento crucial
para el análisis, ya que nos muestra inmediatamente las palabras
emocionales más frecuentes.
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.
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)
arrange(-total_palabras)
: Ordena
las emociones por importancia, mostrando primero las que tienen mayor
presencia en los comentarios.
PLUS [Gráficos]
# 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.
# 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.
# 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.
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.