Bellabeat es una empresa de alta tecnología que fabrica productos de bienestar enfocados en mujeres. Fundada en 2013 por Urška Sršen y Sando Mur, la compañía ha crecido rápidamente posicionándose como líder en wellness tech para mujeres.
Objetivo Principal: Analizar datos de uso de dispositivos inteligentes para identificar oportunidades de crecimiento y desarrollar estrategias de marketing efectivas para los productos de Bellabeat.
Dataset: FitBit Fitness Tracker Data
Fuente: Kaggle (CC0: Public Domain)
Descripción: Datos personales de trackers de fitness de
30 usuarios de Fitbit que consintieron compartir sus datos,
incluyendo:
# Manipulación de datos
library(tidyverse)
library(lubridate)
library(janitor)
# Visualización
library(ggplot2)
library(scales)
library(gridExtra)
library(viridis)
library(RColorBrewer)
library(ggpubr)
library(plotly)
# Análisis estadístico
library(corrplot)
# Tablas
library(knitr)
library(kableExtra)
# Establecer tema de gráficos
theme_set(theme_minimal(base_size = 12))
# Paleta de colores Bellabeat (tonos profesionales y wellness)
bellabeat_colors <- c("#2C5F7C", "#7BA8C4", "#B8D4E3", "#E8B4D4", "#D67BA8")# Cargar datasets
daily_activity <- read_csv("Dataset/dailyActivity_merged.csv")
daily_sleep <- read_csv("Dataset/sleepDay_merged.csv")
daily_calories <- read_csv("Dataset/dailyCalories_merged.csv")
daily_steps <- read_csv("Dataset/dailySteps_merged.csv")
daily_intensities <- read_csv("Dataset/dailyIntensities_merged.csv")
# Vista previa de la actividad diaria
head(daily_activity)## === ESTRUCTURA DE LOS DATASETS ===
## 1. Daily Activity:
## Rows: 940
## Columns: 15
## $ Id <dbl> 1503960366, 1503960366, 1503960366, 150396036…
## $ ActivityDate <chr> "4/12/2016", "4/13/2016", "4/14/2016", "4/15/…
## $ TotalSteps <dbl> 13162, 10735, 10460, 9762, 12669, 9705, 13019…
## $ TotalDistance <dbl> 8.50, 6.97, 6.74, 6.28, 8.16, 6.48, 8.59, 9.8…
## $ TrackerDistance <dbl> 8.50, 6.97, 6.74, 6.28, 8.16, 6.48, 8.59, 9.8…
## $ LoggedActivitiesDistance <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
## $ VeryActiveDistance <dbl> 1.88, 1.57, 2.44, 2.14, 2.71, 3.19, 3.25, 3.5…
## $ ModeratelyActiveDistance <dbl> 0.55, 0.69, 0.40, 1.26, 0.41, 0.78, 0.64, 1.3…
## $ LightActiveDistance <dbl> 6.06, 4.71, 3.91, 2.83, 5.04, 2.51, 4.71, 5.0…
## $ SedentaryActiveDistance <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
## $ VeryActiveMinutes <dbl> 25, 21, 30, 29, 36, 38, 42, 50, 28, 19, 66, 4…
## $ FairlyActiveMinutes <dbl> 13, 19, 11, 34, 10, 20, 16, 31, 12, 8, 27, 21…
## $ LightlyActiveMinutes <dbl> 328, 217, 181, 209, 221, 164, 233, 264, 205, …
## $ SedentaryMinutes <dbl> 728, 776, 1218, 726, 773, 539, 1149, 775, 818…
## $ Calories <dbl> 1985, 1797, 1776, 1745, 1863, 1728, 1921, 203…
##
## 2. Daily Sleep:
## Rows: 413
## Columns: 5
## $ Id <dbl> 1503960366, 1503960366, 1503960366, 1503960366, 150…
## $ SleepDay <chr> "4/12/2016 12:00:00 AM", "4/13/2016 12:00:00 AM", "…
## $ TotalSleepRecords <dbl> 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …
## $ TotalMinutesAsleep <dbl> 327, 384, 412, 340, 700, 304, 360, 325, 361, 430, 2…
## $ TotalTimeInBed <dbl> 346, 407, 442, 367, 712, 320, 377, 364, 384, 449, 3…
##
## 3. Daily Calories:
## Rows: 940
## Columns: 3
## $ Id <dbl> 1503960366, 1503960366, 1503960366, 1503960366, 1503960366…
## $ ActivityDay <chr> "4/12/2016", "4/13/2016", "4/14/2016", "4/15/2016", "4/16/…
## $ Calories <dbl> 1985, 1797, 1776, 1745, 1863, 1728, 1921, 2035, 1786, 1775…
# Número de usuarios únicos por dataset
usuarios_info <- tibble(
Dataset = c("Daily Activity", "Daily Sleep", "Daily Calories",
"Daily Steps", "Daily Intensities"),
Usuarios_Unicos = c(
n_distinct(daily_activity$Id),
n_distinct(daily_sleep$Id),
n_distinct(daily_calories$Id),
n_distinct(daily_steps$Id),
n_distinct(daily_intensities$Id)
),
Total_Registros = c(
nrow(daily_activity),
nrow(daily_sleep),
nrow(daily_calories),
nrow(daily_steps),
nrow(daily_intensities)
)
)
usuarios_info %>%
kable(caption = "Resumen de Usuarios y Registros por Dataset") %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed"))| Dataset | Usuarios_Unicos | Total_Registros |
|---|---|---|
| Daily Activity | 33 | 940 |
| Daily Sleep | 24 | 413 |
| Daily Calories | 33 | 940 |
| Daily Steps | 33 | 940 |
| Daily Intensities | 33 | 940 |
# Limpiar nombres de columnas
daily_activity <- daily_activity %>% clean_names()
daily_sleep <- daily_sleep %>% clean_names()
# Convertir fechas a formato Date
daily_activity <- daily_activity %>%
mutate(
activity_date = mdy(activity_date),
weekday = wday(activity_date, label = TRUE, abbr = FALSE),
month = month(activity_date, label = TRUE)
)
daily_sleep <- daily_sleep %>%
mutate(
sleep_day = mdy_hms(sleep_day),
sleep_date = as.Date(sleep_day),
weekday = wday(sleep_date, label = TRUE, abbr = FALSE)
)
# Verificar valores faltantes
cat("=== VALORES FALTANTES ===\n\n")## === VALORES FALTANTES ===
## Daily Activity:
## id activity_date
## 0 0
## total_steps total_distance
## 0 0
## tracker_distance logged_activities_distance
## 0 0
## very_active_distance moderately_active_distance
## 0 0
## light_active_distance sedentary_active_distance
## 0 0
## very_active_minutes fairly_active_minutes
## 0 0
## lightly_active_minutes sedentary_minutes
## 0 0
## calories weekday
## 0 0
## month
## 0
##
##
## Daily Sleep:
## id sleep_day total_sleep_records
## 0 0 0
## total_minutes_asleep total_time_in_bed sleep_date
## 0 0 0
## weekday
## 0
# Estadísticas descriptivas de actividad
summary_activity <- daily_activity %>%
select(total_steps, total_distance, calories,
very_active_minutes, fairly_active_minutes,
lightly_active_minutes, sedentary_minutes) %>%
summary()
print(summary_activity)## total_steps total_distance calories very_active_minutes
## Min. : 0 Min. : 0.000 Min. : 0 Min. : 0.00
## 1st Qu.: 3790 1st Qu.: 2.620 1st Qu.:1828 1st Qu.: 0.00
## Median : 7406 Median : 5.245 Median :2134 Median : 4.00
## Mean : 7638 Mean : 5.490 Mean :2304 Mean : 21.16
## 3rd Qu.:10727 3rd Qu.: 7.713 3rd Qu.:2793 3rd Qu.: 32.00
## Max. :36019 Max. :28.030 Max. :4900 Max. :210.00
## fairly_active_minutes lightly_active_minutes sedentary_minutes
## Min. : 0.00 Min. : 0.0 Min. : 0.0
## 1st Qu.: 0.00 1st Qu.:127.0 1st Qu.: 729.8
## Median : 6.00 Median :199.0 Median :1057.5
## Mean : 13.56 Mean :192.8 Mean : 991.2
## 3rd Qu.: 19.00 3rd Qu.:264.0 3rd Qu.:1229.5
## Max. :143.00 Max. :518.0 Max. :1440.0
# Identificar registros con 0 pasos (días sin uso)
dias_sin_uso <- daily_activity %>%
filter(total_steps == 0) %>%
nrow()
cat("\n\nDías registrados sin actividad (0 pasos):", dias_sin_uso,
"de", nrow(daily_activity), "registros\n")##
##
## Días registrados sin actividad (0 pasos): 77 de 940 registros
## Porcentaje: 8.19 %
# Crear dataset integrado con métricas calculadas
daily_activity <- daily_activity %>%
mutate(
# Categorización de usuarios por nivel de actividad (pasos)
activity_level = case_when(
total_steps < 5000 ~ "Sedentario",
total_steps >= 5000 & total_steps < 7500 ~ "Poco Activo",
total_steps >= 7500 & total_steps < 10000 ~ "Moderadamente Activo",
total_steps >= 10000 ~ "Muy Activo"
),
# Total de minutos activos
total_active_minutes = very_active_minutes + fairly_active_minutes + lightly_active_minutes,
# Porcentaje del día activo
pct_active_time = (total_active_minutes / 1440) * 100,
# Categoría de día de la semana
day_type = ifelse(weekday %in% c("sábado", "domingo"), "Fin de semana", "Entre semana")
)
# Agregar información de sueño
daily_sleep <- daily_sleep %>%
mutate(
# Eficiencia de sueño
sleep_efficiency = (total_minutes_asleep / total_time_in_bed) * 100,
# Categoría de calidad de sueño
sleep_quality = case_when(
total_minutes_asleep < 360 ~ "Insuficiente (<6h)",
total_minutes_asleep >= 360 & total_minutes_asleep < 420 ~ "Bajo (6-7h)",
total_minutes_asleep >= 420 & total_minutes_asleep < 540 ~ "Bueno (7-9h)",
total_minutes_asleep >= 540 ~ "Excesivo (>9h)"
)
)
head(daily_activity %>% select(id, activity_date, total_steps, activity_level,
total_active_minutes, pct_active_time))# Combinar actividad con sueño
activity_sleep <- daily_activity %>%
inner_join(daily_sleep, by = c("id" = "id", "activity_date" = "sleep_date"))
cat("Registros después del merge:", nrow(activity_sleep), "\n")## Registros después del merge: 413
## Usuarios con datos de actividad y sueño: 24
# Días de uso por usuario
dias_uso <- daily_activity %>%
group_by(id) %>%
summarise(
dias_registrados = n(),
promedio_pasos = mean(total_steps),
promedio_calorias = mean(calories)
) %>%
arrange(desc(dias_registrados))
# Visualización de distribución de días de uso
ggplot(dias_uso, aes(x = dias_registrados)) +
geom_histogram(binwidth = 1, fill = bellabeat_colors[1], alpha = 0.8, color = "white") +
geom_vline(aes(xintercept = median(dias_registrados)),
color = bellabeat_colors[4], linetype = "dashed", size = 1) +
labs(
title = "Distribución de Días de Uso del Dispositivo",
subtitle = paste("Mediana:", median(dias_uso$dias_registrados), "días"),
x = "Número de Días Registrados",
y = "Número de Usuarios"
) +
theme_minimal(base_size = 14) +
annotate("text", x = median(dias_uso$dias_registrados) + 3, y = 5,
label = paste("Mediana:", median(dias_uso$dias_registrados), "días"),
color = bellabeat_colors[4], size = 4)📊 Insight de Participación: - El análisis muestra la consistencia de uso del dispositivo - Usuarios con menos de 20 días pueden indicar abandono temprano - Oportunidad: Estrategias de retención en las primeras semanas
# Estadísticas de pasos
pasos_stats <- daily_activity %>%
summarise(
Promedio = mean(total_steps),
Mediana = median(total_steps),
Desv_Estandar = sd(total_steps),
Minimo = min(total_steps),
Maximo = max(total_steps)
)
pasos_stats %>%
kable(caption = "Estadísticas Descriptivas de Pasos Diarios", digits = 0) %>%
kable_styling(bootstrap_options = c("striped", "hover"))| Promedio | Mediana | Desv_Estandar | Minimo | Maximo |
|---|---|---|---|---|
| 7638 | 7406 | 5087 | 0 | 36019 |
# Distribución de pasos diarios
p1 <- ggplot(daily_activity, aes(x = total_steps)) +
geom_histogram(bins = 30, fill = bellabeat_colors[2], alpha = 0.8, color = "white") +
geom_vline(aes(xintercept = mean(total_steps)),
color = bellabeat_colors[4], linetype = "dashed", size = 1) +
geom_vline(aes(xintercept = 10000),
color = "red", linetype = "dashed", size = 1) +
labs(
title = "Distribución de Pasos Diarios",
subtitle = "Línea roja: Meta recomendada de 10,000 pasos",
x = "Total de Pasos",
y = "Frecuencia"
) +
annotate("text", x = 10000, y = 50, label = "Meta: 10K",
color = "red", angle = 90, vjust = -0.5)
# Niveles de actividad
p2 <- daily_activity %>%
count(activity_level) %>%
mutate(
activity_level = factor(activity_level,
levels = c("Sedentario", "Poco Activo",
"Moderadamente Activo", "Muy Activo"))
) %>%
ggplot(aes(x = activity_level, y = n, fill = activity_level)) +
geom_col(alpha = 0.8) +
scale_fill_manual(values = bellabeat_colors) +
labs(
title = "Distribución de Niveles de Actividad",
x = "Nivel de Actividad",
y = "Número de Días"
) +
theme(legend.position = "none") +
geom_text(aes(label = n), vjust = -0.5, size = 5)
grid.arrange(p1, p2, ncol = 2)# Actividad por día de la semana
actividad_semanal <- daily_activity %>%
group_by(weekday) %>%
summarise(
promedio_pasos = mean(total_steps),
promedio_calorias = mean(calories),
promedio_distancia = mean(total_distance),
n_registros = n()
) %>%
mutate(weekday = factor(weekday, levels = c("lunes", "martes", "miércoles",
"jueves", "viernes", "sábado", "domingo")))
ggplot(actividad_semanal, aes(x = weekday, y = promedio_pasos, fill = weekday)) +
geom_col(alpha = 0.8, show.legend = FALSE) +
scale_fill_brewer(palette = "Blues") +
labs(
title = "Pasos Promedio por Día de la Semana",
subtitle = "¿Los usuarios son más activos en ciertos días?",
x = "Día de la Semana",
y = "Pasos Promedio"
) +
geom_hline(yintercept = mean(daily_activity$total_steps),
linetype = "dashed", color = "red", size = 1) +
geom_text(aes(label = round(promedio_pasos, 0)), vjust = -0.5) +
theme(axis.text.x = element_text(angle = 45, hjust = 1))# Comparación entre semana y fin de semana
comparacion_dias <- daily_activity %>%
group_by(day_type) %>%
summarise(
pasos_promedio = mean(total_steps),
calorias_promedio = mean(calories),
minutos_activos = mean(total_active_minutes),
minutos_sedentarios = mean(sedentary_minutes)
)
comparacion_dias %>%
kable(caption = "Comparación: Entre Semana vs Fin de Semana", digits = 1) %>%
kable_styling(bootstrap_options = c("striped", "hover"))| day_type | pasos_promedio | calorias_promedio | minutos_activos | minutos_sedentarios |
|---|---|---|---|---|
| Entre semana | 7637.9 | 2303.6 | 227.5 | 991.2 |
# Visualización
comparacion_dias_long <- comparacion_dias %>%
pivot_longer(cols = -day_type, names_to = "metrica", values_to = "valor")
ggplot(comparacion_dias_long, aes(x = day_type, y = valor, fill = day_type)) +
geom_col(alpha = 0.8) +
facet_wrap(~metrica, scales = "free_y", ncol = 2) +
scale_fill_manual(values = bellabeat_colors[c(1,4)]) +
labs(
title = "Comparación de Actividad: Entre Semana vs Fin de Semana",
x = "",
y = "Valor Promedio"
) +
theme(legend.position = "bottom")📊 Insight de Patrones Semanales: Los datos muestran variaciones en la actividad según el día de la semana. Esto puede informar cuándo enviar notificaciones y recordatorios de actividad.
# Distribución de minutos por intensidad
intensidad_promedio <- daily_activity %>%
summarise(
Muy_Activo = mean(very_active_minutes),
Bastante_Activo = mean(fairly_active_minutes),
Ligeramente_Activo = mean(lightly_active_minutes),
Sedentario = mean(sedentary_minutes)
) %>%
pivot_longer(everything(), names_to = "Intensidad", values_to = "Minutos")
ggplot(intensidad_promedio, aes(x = reorder(Intensidad, -Minutos),
y = Minutos, fill = Intensidad)) +
geom_col(alpha = 0.8) +
scale_fill_manual(values = c("#E74C3C", "#F39C12", "#3498DB", "#95A5A6")) +
labs(
title = "Distribución Promedio de Tiempo por Intensidad de Actividad",
subtitle = "Minutos promedio por día",
x = "Nivel de Intensidad",
y = "Minutos Promedio"
) +
geom_text(aes(label = round(Minutos, 0)), vjust = -0.5, size = 5) +
theme(legend.position = "none")# Porcentaje del día por tipo de actividad
pct_tiempo <- intensidad_promedio %>%
mutate(
Porcentaje = (Minutos / 1440) * 100,
Horas = Minutos / 60
)
pct_tiempo %>%
kable(caption = "Distribución del Tiempo Diario", digits = 1) %>%
kable_styling(bootstrap_options = c("striped", "hover"))| Intensidad | Minutos | Porcentaje | Horas |
|---|---|---|---|
| Muy_Activo | 21.2 | 1.5 | 0.4 |
| Bastante_Activo | 13.6 | 0.9 | 0.2 |
| Ligeramente_Activo | 192.8 | 13.4 | 3.2 |
| Sedentario | 991.2 | 68.8 | 16.5 |
# Gráfico de pie
ggplot(pct_tiempo, aes(x = "", y = Porcentaje, fill = Intensidad)) +
geom_bar(stat = "identity", width = 1) +
coord_polar("y", start = 0) +
scale_fill_manual(values = c("#E74C3C", "#F39C12", "#3498DB", "#95A5A6")) +
labs(title = "Distribución Porcentual del Tiempo Diario por Intensidad") +
theme_void() +
theme(legend.position = "right") +
geom_text(aes(label = paste0(round(Porcentaje, 1), "%")),
position = position_stack(vjust = 0.5), size = 4)⚠️ Hallazgo Crítico: Los usuarios pasan la mayor parte del día sedentarios. Esto representa una oportunidad clave para Bellabeat en desarrollar funciones que promuevan movimiento regular y rompan períodos sedentarios prolongados.
# Relación entre pasos y calorías
cor_pasos_calorias <- cor(daily_activity$total_steps, daily_activity$calories)
ggplot(daily_activity, aes(x = total_steps, y = calories)) +
geom_point(alpha = 0.4, color = bellabeat_colors[1]) +
geom_smooth(method = "lm", color = bellabeat_colors[4], size = 1.5) +
labs(
title = "Relación entre Pasos Diarios y Calorías Quemadas",
subtitle = paste("Correlación:", round(cor_pasos_calorias, 3)),
x = "Total de Pasos",
y = "Calorías Quemadas"
) +
annotate("text", x = 25000, y = 1000,
label = paste("r =", round(cor_pasos_calorias, 3)),
size = 6, color = bellabeat_colors[4])# Calorías por nivel de actividad
daily_activity %>%
mutate(activity_level = factor(activity_level,
levels = c("Sedentario", "Poco Activo",
"Moderadamente Activo", "Muy Activo"))) %>%
ggplot(aes(x = activity_level, y = calories, fill = activity_level)) +
geom_boxplot(alpha = 0.7) +
scale_fill_manual(values = bellabeat_colors) +
labs(
title = "Distribución de Calorías Quemadas por Nivel de Actividad",
x = "Nivel de Actividad",
y = "Calorías Quemadas"
) +
theme(legend.position = "none") +
stat_summary(fun = mean, geom = "point", shape = 23, size = 3, fill = "red")# Estadísticas de sueño
sleep_stats <- daily_sleep %>%
summarise(
Promedio_Horas = mean(total_minutes_asleep) / 60,
Mediana_Horas = median(total_minutes_asleep) / 60,
Desv_Estandar = sd(total_minutes_asleep) / 60,
Min_Horas = min(total_minutes_asleep) / 60,
Max_Horas = max(total_minutes_asleep) / 60
)
sleep_stats %>%
kable(caption = "Estadísticas de Horas de Sueño", digits = 2) %>%
kable_styling(bootstrap_options = c("striped", "hover"))| Promedio_Horas | Mediana_Horas | Desv_Estandar | Min_Horas | Max_Horas |
|---|---|---|---|---|
| 6.99 | 7.22 | 1.97 | 0.97 | 13.27 |
# Distribución de horas de sueño
ggplot(daily_sleep, aes(x = total_minutes_asleep / 60)) +
geom_histogram(bins = 30, fill = bellabeat_colors[3], alpha = 0.8, color = "white") +
geom_vline(aes(xintercept = 7), color = "green", linetype = "dashed", size = 1) +
geom_vline(aes(xintercept = 9), color = "green", linetype = "dashed", size = 1) +
annotate("rect", xmin = 7, xmax = 9, ymin = 0, ymax = Inf,
alpha = 0.1, fill = "green") +
labs(
title = "Distribución de Horas de Sueño",
subtitle = "Zona verde: Rango recomendado de 7-9 horas",
x = "Horas de Sueño",
y = "Frecuencia"
)# Distribución de calidad de sueño
sleep_quality_dist <- daily_sleep %>%
count(sleep_quality) %>%
mutate(
sleep_quality = factor(sleep_quality,
levels = c("Insuficiente (<6h)", "Bajo (6-7h)",
"Bueno (7-9h)", "Excesivo (>9h)")),
porcentaje = (n / sum(n)) * 100
)
ggplot(sleep_quality_dist, aes(x = sleep_quality, y = n, fill = sleep_quality)) +
geom_col(alpha = 0.8) +
scale_fill_manual(values = c("#E74C3C", "#F39C12", "#27AE60", "#3498DB")) +
labs(
title = "Distribución de Calidad de Sueño",
x = "Categoría de Sueño",
y = "Número de Registros"
) +
geom_text(aes(label = paste0(n, "\n(", round(porcentaje, 1), "%)")),
vjust = -0.5, size = 4) +
theme(legend.position = "none")# Análisis de eficiencia de sueño
ggplot(daily_sleep, aes(x = sleep_efficiency)) +
geom_histogram(bins = 30, fill = bellabeat_colors[5], alpha = 0.8, color = "white") +
geom_vline(aes(xintercept = mean(sleep_efficiency)),
color = "red", linetype = "dashed", size = 1) +
labs(
title = "Distribución de Eficiencia del Sueño",
subtitle = paste("Eficiencia promedio:",
round(mean(daily_sleep$sleep_efficiency), 1), "%"),
x = "Eficiencia del Sueño (%)",
y = "Frecuencia"
)# Sueño por día de la semana
sleep_weekday <- daily_sleep %>%
group_by(weekday) %>%
summarise(
horas_promedio = mean(total_minutes_asleep) / 60,
eficiencia_promedio = mean(sleep_efficiency)
) %>%
mutate(weekday = factor(weekday, levels = c("lunes", "martes", "miércoles",
"jueves", "viernes", "sábado", "domingo")))
ggplot(sleep_weekday, aes(x = weekday, y = horas_promedio, fill = weekday)) +
geom_col(alpha = 0.8, show.legend = FALSE) +
scale_fill_brewer(palette = "Purples") +
geom_hline(yintercept = 7, linetype = "dashed", color = "green", size = 1) +
geom_hline(yintercept = 9, linetype = "dashed", color = "green", size = 1) +
labs(
title = "Horas de Sueño Promedio por Día de la Semana",
subtitle = "Líneas verdes: Rango recomendado (7-9 horas)",
x = "Día de la Semana",
y = "Horas Promedio de Sueño"
) +
geom_text(aes(label = round(horas_promedio, 1)), vjust = -0.5) +
theme(axis.text.x = element_text(angle = 45, hjust = 1))📊 Insight de Sueño: - Muchos usuarios no duermen las 7-9 horas recomendadas - La eficiencia de sueño varía significativamente - Oportunidad para features de coaching de sueño en la app Bellabeat
# ¿Más actividad = mejor sueño?
cor_actividad_sueno <- cor(activity_sleep$total_steps,
activity_sleep$total_minutes_asleep)
ggplot(activity_sleep, aes(x = total_steps, y = total_minutes_asleep / 60)) +
geom_point(alpha = 0.5, color = bellabeat_colors[2]) +
geom_smooth(method = "lm", color = bellabeat_colors[4], size = 1.5) +
labs(
title = "Relación entre Actividad Física y Horas de Sueño",
subtitle = paste("Correlación:", round(cor_actividad_sueno, 3)),
x = "Total de Pasos",
y = "Horas de Sueño"
) +
annotate("text", x = 25000, y = 4,
label = paste("r =", round(cor_actividad_sueno, 3)),
size = 6, color = bellabeat_colors[4])# Comparación de sueño por nivel de actividad
activity_sleep %>%
mutate(activity_level = factor(activity_level,
levels = c("Sedentario", "Poco Activo",
"Moderadamente Activo", "Muy Activo"))) %>%
ggplot(aes(x = activity_level, y = total_minutes_asleep / 60, fill = activity_level)) +
geom_boxplot(alpha = 0.7) +
scale_fill_manual(values = bellabeat_colors) +
labs(
title = "Distribución de Horas de Sueño por Nivel de Actividad",
x = "Nivel de Actividad",
y = "Horas de Sueño"
) +
theme(legend.position = "none") +
geom_hline(yintercept = 7, linetype = "dashed", color = "green", alpha = 0.5) +
geom_hline(yintercept = 9, linetype = "dashed", color = "green", alpha = 0.5)# Matriz de correlación
cor_data <- daily_activity %>%
select(total_steps, total_distance, calories,
very_active_minutes, fairly_active_minutes,
lightly_active_minutes, sedentary_minutes) %>%
cor()
# Visualizar matriz de correlación
corrplot(cor_data, method = "color", type = "upper",
tl.col = "black", tl.srt = 45,
addCoef.col = "black", number.cex = 0.7,
col = colorRampPalette(c("#E74C3C", "white", "#27AE60"))(200),
title = "Matriz de Correlación - Métricas de Actividad",
mar = c(0,0,2,0))🔍 Hallazgo: Existe una fuerte correlación positiva entre pasos, distancia y calorías, lo que valida las métricas de actividad. Los minutos sedentarios están negativamente correlacionados con la actividad.
# Crear perfiles de usuario basados en comportamiento
user_profiles <- daily_activity %>%
group_by(id) %>%
summarise(
avg_steps = mean(total_steps),
avg_active_mins = mean(total_active_minutes),
avg_sedentary_mins = mean(sedentary_minutes),
avg_calories = mean(calories),
days_logged = n()
) %>%
mutate(
user_segment = case_when(
avg_steps >= 10000 & avg_active_mins >= 60 ~ "Usuario Altamente Activo",
avg_steps >= 7500 & avg_active_mins >= 30 ~ "Usuario Moderadamente Activo",
avg_steps >= 5000 ~ "Usuario Ligeramente Activo",
TRUE ~ "Usuario Sedentario"
)
)
# Distribución de segmentos
segment_dist <- user_profiles %>%
count(user_segment) %>%
mutate(porcentaje = (n / sum(n)) * 100)
ggplot(segment_dist, aes(x = reorder(user_segment, n), y = n, fill = user_segment)) +
geom_col(alpha = 0.8) +
coord_flip() +
scale_fill_manual(values = bellabeat_colors) +
labs(
title = "Segmentación de Usuarios por Nivel de Actividad",
x = "Segmento de Usuario",
y = "Número de Usuarios"
) +
geom_text(aes(label = paste0(n, " (", round(porcentaje, 1), "%)")),
hjust = -0.2, size = 4) +
theme(legend.position = "none")# Perfil detallado de cada segmento
segment_profiles <- daily_activity %>%
left_join(user_profiles %>% select(id, user_segment), by = "id") %>%
group_by(user_segment) %>%
summarise(
Usuarios = n_distinct(id),
Pasos_Promedio = round(mean(total_steps), 0),
Minutos_Activos = round(mean(total_active_minutes), 0),
Calorias_Promedio = round(mean(calories), 0),
Pct_Dia_Activo = round(mean(pct_active_time), 1)
)
segment_profiles %>%
kable(caption = "Perfil Detallado por Segmento de Usuario") %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed"))| user_segment | Usuarios | Pasos_Promedio | Minutos_Activos | Calorias_Promedio | Pct_Dia_Activo |
|---|---|---|---|---|---|
| Usuario Altamente Activo | 7 | 12522 | 285 | 2554 | 19.8 |
| Usuario Ligeramente Activo | 9 | 6557 | 228 | 2149 | 15.8 |
| Usuario Moderadamente Activo | 9 | 8642 | 269 | 2482 | 18.6 |
| Usuario Sedentario | 8 | 2842 | 119 | 2017 | 8.3 |
Campaña enfocada en romper el sedentarismo
Estrategia: - Feature Clave: Alertas inteligentes de movimiento cada 60-90 minutos durante el día - Mensaje de Marketing: “Tu cuerpo está diseñado para moverse - nosotros te recordamos hacerlo” - Contenido: Mini-rutinas de 2-5 minutos que se pueden hacer en la oficina/casa - Canal: Notificaciones push personalizadas según el patrón de actividad del usuario
Por qué: El 81% del tiempo diario es sedentario. Esta es la mayor oportunidad de diferenciación.
KPI de Éxito: Reducir tiempo sedentario promedio en 10% en 3 meses.
Programa integral de mejora del sueño
Estrategia: - Feature Clave: Coach de sueño con rutinas personalizadas de wind-down - Mensaje de Marketing: “El 50% de las usuarias no duermen lo suficiente. Cambiemos eso juntas” - Contenido: - Recordatorios inteligentes de hora de dormir - Meditaciones guiadas pre-sueño - Tracking de factores que afectan el sueño (cafeína, ejercicio tarde, etc.) - Canal: Email marketing con “Sleep Score” semanal personalizado
Por qué: Solo 50% de los registros muestran sueño en rango óptimo (7-9h).
KPI de Éxito: Incrementar usuarios con 7+ horas de sueño en 20% en 4 meses.
Programa de gamificación para adherencia
Estrategia: - Feature Clave: Sistema de streaks, badges y challenges community - Mensaje de Marketing: “Tu salud es un maratón, no un sprint. Celebremos cada día” - Contenido: - Challenges semanales adaptativos al nivel del usuario - Tabla de líderes social (opcional) - Rewards desbloqueables (contenido premium, descuentos tienda) - Canal: In-app engagement + social media campaigns con UGC
Por qué: Variación significativa en días de uso indica problemas de retención.
KPI de Éxito: Aumentar usuarios con 25+ días activos/mes en 30% en 6 meses.
| Fase | Accion |
|---|---|
| Inmediato (0-30 días) |
|
| Corto Plazo (1-3 meses) |
|
| Mediano Plazo (3-6 meses) |
|
| Largo Plazo (6-12 meses) |
|
Para medir el éxito de las estrategias propuestas:
Métricas de Engagement: - Daily Active Users (DAU) / Monthly Active Users (MAU) - Session frequency y duration - Retention rate (D1, D7, D30)
Métricas de Salud/Wellness: - % usuarios alcanzando metas diarias - Reducción en tiempo sedentario promedio - Mejora en horas de sueño promedio - Sleep efficiency score
Métricas de Negocio: - Conversión a membresía premium - Customer Lifetime Value (CLV) - Net Promoter Score (NPS) - Churn rate
Este análisis de datos de dispositivos inteligentes de fitness ha revelado insights clave sobre los patrones de uso y comportamiento de los usuarios que pueden informar directamente la estrategia de marketing de Bellabeat:
Las tres recomendaciones principales - Movimiento Consciente, Sleep Better, y Consistency Rewards - abordan directamente estos hallazgos y posicionan a Bellabeat como líder en wellness holístico para mujeres.
Al enfocarse en pequeñas acciones diarias en lugar de transformaciones masivas, y al personalizar la experiencia según los patrones individuales, Bellabeat puede diferenciarse en un mercado saturado.
## === INFORMACIÓN DE LA SESIÓN ===
## R version 4.5.2 (2025-10-31)
## Platform: x86_64-pc-linux-gnu
## Running under: Ubuntu 24.04.3 LTS
##
## Matrix products: default
## BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
## LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.26.so; LAPACK version 3.12.0
##
## locale:
## [1] LC_CTYPE=C.UTF-8 LC_NUMERIC=C LC_TIME=C.UTF-8
## [4] LC_COLLATE=C.UTF-8 LC_MONETARY=C.UTF-8 LC_MESSAGES=C.UTF-8
## [7] LC_PAPER=C.UTF-8 LC_NAME=C LC_ADDRESS=C
## [10] LC_TELEPHONE=C LC_MEASUREMENT=C.UTF-8 LC_IDENTIFICATION=C
##
## time zone: UTC
## tzcode source: system (glibc)
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] kableExtra_1.4.0 knitr_1.51 corrplot_0.95 plotly_4.11.0
## [5] ggpubr_0.6.2 RColorBrewer_1.1-3 viridis_0.6.5 viridisLite_0.4.2
## [9] gridExtra_2.3 scales_1.4.0 janitor_2.2.1 lubridate_1.9.4
## [13] forcats_1.0.1 stringr_1.6.0 dplyr_1.1.4 purrr_1.2.0
## [17] readr_2.1.6 tidyr_1.3.2 tibble_3.3.0 ggplot2_4.0.1
## [21] tidyverse_2.0.0
##
## loaded via a namespace (and not attached):
## [1] gtable_0.3.6 xfun_0.55 bslib_0.9.0 htmlwidgets_1.6.4
## [5] rstatix_0.7.3 lattice_0.22-7 tzdb_0.5.0 vctrs_0.6.5
## [9] tools_4.5.2 generics_0.1.4 parallel_4.5.2 pkgconfig_2.0.3
## [13] Matrix_1.7-4 data.table_1.18.0 S7_0.2.1 lifecycle_1.0.4
## [17] compiler_4.5.2 farver_2.1.2 textshaping_1.0.4 carData_3.0-5
## [21] snakecase_0.11.1 htmltools_0.5.9 sass_0.4.10 yaml_2.3.12
## [25] lazyeval_0.2.2 Formula_1.2-5 crayon_1.5.3 pillar_1.11.1
## [29] car_3.1-3 jquerylib_0.1.4 cachem_1.1.0 abind_1.4-8
## [33] nlme_3.1-168 tidyselect_1.2.1 digest_0.6.39 stringi_1.8.7
## [37] splines_4.5.2 labeling_0.4.3 fastmap_1.2.0 grid_4.5.2
## [41] cli_3.6.5 magrittr_2.0.4 broom_1.0.11 withr_3.0.2
## [45] backports_1.5.0 bit64_4.6.0-1 timechange_0.3.0 rmarkdown_2.30
## [49] httr_1.4.7 bit_4.6.0 otel_0.2.0 ggsignif_0.6.4
## [53] hms_1.1.4 evaluate_1.0.5 mgcv_1.9-3 rlang_1.1.6
## [57] glue_1.8.0 xml2_1.5.1 vroom_1.6.7 svglite_2.2.2
## [61] rstudioapi_0.17.1 jsonlite_2.0.0 R6_2.6.1 systemfonts_1.3.1