ElectroTienda S.A. necesita entender el posicionamiento competitivo de precios en el mercado de smartphones para su próxima estrategia comercial.
Realizar benchmarking de precios en smartphones
Identificar patrones de pricing por marca
Evaluar estrategias de descuentos
#Carga de librerias
library(rvest) # Web scraping
library(dplyr) # Manipulación de datos
library(ggplot2) # Visualización
library(knitr) # Reportes
library(scales) # Formato de números
library(stringr) # Manipulación de texto
library(tidyr) # Datos ordenados
Parte 1: Extracción de Datos
# Establecemos la ruta al archivo HTML que debe estar en el mismo directorio que este script
ruta_html <- "electrotienda.html"
# Verificamos si el archivo existe antes de continuar
if(!file.exists(ruta_html)) {
stop("❌ Error: No se encontró el archivo HTML. Verifica que:")
cat("\n1. El archivo 'electrotienda.html' esté en la misma carpeta que este Rmd")
cat("\n2. El nombre del archivo coincida exactamente (incluyendo mayúsculas)")
}
# Creamos una función robusta para extraer datos que maneje posibles errores
extraer_dato <- function(nodo, selector) {
resultado <- tryCatch({
# Intenta extraer el texto del nodo con el selector CSS
texto <- nodo %>% html_nodes(selector) %>% html_text() %>% trimws()
# Si no encuentra nada, devuelve NA en lugar de error
if(length(texto) == 0) NA_character_ else texto
},
# Manejo de errores (si el selector no existe)
error = function(e) NA_character_)
return(resultado)
}
# Leemos el archivo HTML
pagina <- read_html(ruta_html)
# Extraemos todos los nodos con clase "producto"
productos <- html_nodes(pagina, ".producto")
# Verificación crítica
if(length(productos) == 0) {
stop("❌ Error: No se encontraron productos. Revisa:")
cat("\n1. Que el HTML use la clase 'producto' para cada item")
cat("\n2. La estructura del archivo HTML")
} else {
cat("✅ Éxito: Se encontraron", length(productos), "productos para analizar")
}
## ✅ Éxito: Se encontraron 51 productos para analizar
# Creamos un dataframe con los datos extraídos
datos <- data.frame(
# Nombre del producto (extraído de la clase CSS ".nombre")
Producto = sapply(productos, extraer_dato, ".nombre"),
# Marca del producto (extraído de la clase CSS ".marca")
Marca = sapply(productos, extraer_dato, ".marca"),
# Precio (con limpieza de formato $ y conversión a numérico)
Precio = sapply(productos, function(p) {
precio_texto <- extraer_dato(p, ".precio")
if(is.na(precio_texto)) return(NA_real_)
# Expresión regular para extraer números con decimales
precio_limpio <- str_extract(precio_texto, "[0-9,]+\\.[0-9]{2}") %>%
str_replace(",", "") %>% # Elimina comas de miles
as.numeric() # Convierte a número
return(precio_limpio)
}),
# Descuento (extrae el % y maneja casos sin descuento)
Descuento = sapply(productos, function(p) {
desc_texto <- extraer_dato(p, ".descuento")
if(is.na(desc_texto)) return(0) # Si no hay descuento, devuelve 0
# Extrae solo los dígitos del descuento
desc_num <- str_extract(desc_texto, "\\d+") %>% as.numeric()
return(desc_num)
}),
stringsAsFactors = FALSE
) %>%
# Calculamos el precio original antes del descuento
mutate(
Precio_Original = ifelse(Descuento > 0, Precio / (1 - Descuento/100), Precio),
# Clasificación por gama de precio
Gama = case_when(
Precio >= 800 ~ "Premium",
Precio >= 400 ~ "Media",
TRUE ~ "Básica"
)
)
# Mostramos las primeras filas para verificación
head(datos) %>% kable(caption = "Muestra de Datos Extraídos")
Producto | Marca | Precio | Descuento | Precio_Original | Gama |
---|---|---|---|---|---|
Galaxy S23 Ultra | Samsung | 1199.99 | 10 | 1333.322 | Premium |
Galaxy S23+ | Samsung | 999.99 | 0 | 999.990 | Premium |
Galaxy S23 | Samsung | 799.99 | 0 | 799.990 | Media |
Galaxy Z Flip5 | Samsung | 999.00 | 15 | 1175.294 | Premium |
Galaxy Z Fold5 | Samsung | 1799.99 | 0 | 1799.990 | Premium |
Galaxy A54 5G | Samsung | 449.99 | 0 | 449.990 | Media |
# Guardamos los datos en CSV para análisis posteriores
write.csv(datos, "datos_smartphones.csv", row.names = FALSE, fileEncoding = "UTF-8")
cat("📊 Datos exportados correctamente a 'datos_smartphones.csv'")
## 📊 Datos exportados correctamente a 'datos_smartphones.csv'
Parte 2: Análisis Exploratorio
resumen_general <- datos %>%
summarise(
Total_Productos = n(),
Marcas_Unicas = n_distinct(Marca),
Precio_Promedio = mean(Precio, na.rm = TRUE),
Precio_Mediano = median(Precio, na.rm = TRUE),
Rango_Precios = paste("$", min(Precio, na.rm = TRUE), "-", "$", max(Precio, na.rm = TRUE)),
Productos_Descuento = sum(Descuento > 0),
Porcentaje_Descuento = mean(Descuento[Descuento > 0])
)
kable(resumen_general, caption = "Métricas Clave del Mercado")
Total_Productos | Marcas_Unicas | Precio_Promedio | Precio_Mediano | Rango_Precios | Productos_Descuento | Porcentaje_Descuento |
---|---|---|---|---|---|---|
51 | 16 | 710.8986 | 699 | $ 149.99 - $ 1799.99 | 11 | 10.63636 |
ggplot(datos, aes(x = Precio)) +
geom_histogram(fill = "steelblue", bins = 15, color = "white") +
scale_x_continuous(labels = scales::dollar) +
labs(title = "Distribución de Precios en el Mercado",
x = "Precio (USD)", y = "Número de Productos") +
theme_minimal()
Parte 3: Análisis Competitivo
top_marcas <- datos %>%
group_by(Marca) %>%
summarise(
Productos = n(),
Precio_Promedio = mean(Precio),
Descuento_Promedio = mean(Descuento),
.groups = 'drop'
) %>%
arrange(desc(Precio_Promedio))
ggplot(top_marcas, aes(x = reorder(Marca, Precio_Promedio), y = Precio_Promedio)) +
geom_col(fill = "steelblue") +
coord_flip() +
labs(title = "Posicionamiento de Precio por Marca",
x = "", y = "Precio Promedio (USD)") +
scale_y_continuous(labels = scales::dollar) +
theme_minimal()
datos %>%
filter(Descuento > 0) %>%
group_by(Marca) %>%
summarise(
Productos = n(),
Descuento_Promedio = mean(Descuento),
.groups = 'drop'
) %>%
ggplot(aes(x = Productos, y = Descuento_Promedio, size = Productos, color = Marca)) +
geom_point() +
labs(title = "Estrategias de Descuento por Marca",
x = "Número de Productos con Descuento",
y = "Descuento Promedio (%)") +
theme_minimal() +
theme(legend.position = "none")
Conclusiones y Recomendaciones
Hallazgos Clave
# 1. Calcular marca con precio promedio más alto
marca_premium <- datos %>%
group_by(Marca) %>%
summarise(Precio_Promedio = mean(Precio)) %>%
arrange(desc(Precio_Promedio)) %>%
slice(1) %>%
pull(Marca)
# 2. Identificar marca con más descuentos
marca_mas_descuentos <- datos %>%
filter(Descuento > 0) %>%
count(Marca, sort = TRUE) %>%
slice(1) %>%
pull(Marca)
# 3. Calcular porcentaje de productos con descuento
porcentaje_descuentos <- mean(datos$Descuento > 0) * 100
# 4. Obtener rango de precios
precio_min <- min(datos$Precio)
precio_max <- max(datos$Precio)
# 5. Contar productos por gama
gama_media_count <- sum(datos$Gama == "Media")
Resultados : 1. La marca premium es:
marca_premium
## [1] "Asus"
2.Rango de precios:
precio_max - precio_min
## [1] 1650
round(porcentaje_descuentos,1)
## [1] 21.6