Bellabeat Case Study

About Bellabeat

Bellabeat is a high-tech company that manufactures health-focused smart products. Found by Urška Sršen and Sando Mur in 2013, with a line of products that collects data on activity, sleep, stress, and reproductive health to empower women with knowledge about their own health and habits.

Business Task

Provide stakeholders (Urška Sršen, Sando Mur & Bellabeat marketing analytics team) with analysis on how non-Bellabeat smart devices are used to be able to better market Bellabeat products.

Loaded Packages

library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## âś” dplyr     1.1.4     âś” readr     2.1.5
## âś” forcats   1.0.0     âś” stringr   1.5.1
## âś” ggplot2   3.5.1     âś” tibble    3.2.1
## âś” lubridate 1.9.3     âś” tidyr     1.3.1
## âś” purrr     1.0.2     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## âś– dplyr::filter() masks stats::filter()
## âś– dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(lubridate)
library(dplyr)
library(ggplot2)
library(tidyr)

Uploaded & Renamed Data

Data is from a Kaggle dataset that contains personal fitness tracker from thirty fitbit users. These users consented to the submission of personal tracker data, including minute-level output for physical activity, heart rate, and sleep monitoring. It includes information about daily activity, steps, and heart rate that can be used to explore users’ habits.

daily_activity_3 <- read.csv("dailyActivity_merged_3-4.csv")
daily_activity_4 <- read.csv("dailyActivity_merged_4-5.csv")
weight_log_info_3 <- read.csv("weightLogInfo_merged_3-4.csv")
weight_log_info_4 <- read.csv("weightLogInfo_merged_4-5.csv")
hourly_calories_3 <- read_csv("hourlyCalories_merged_3-4.csv")
## Rows: 24084 Columns: 3
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): ActivityHour
## dbl (2): Id, Calories
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
hourly_calories_4 <- read_csv("hourlyCalories_merged_4-5.csv")
## Rows: 22099 Columns: 3
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): ActivityHour
## dbl (2): Id, Calories
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
hourly_intensities_3 <- read_csv("hourlyIntensities_merged_3-4.csv")
## Rows: 24084 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): ActivityHour
## dbl (3): Id, TotalIntensity, AverageIntensity
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
hourly_intensities_4 <- read_csv("hourlyIntensities_merged_4-5.csv")
## Rows: 22099 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): ActivityHour
## dbl (3): Id, TotalIntensity, AverageIntensity
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
minute_sleep_3 <- read_csv("minuteSleep_merged_3-4.csv")
## Rows: 198559 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): date
## dbl (3): Id, value, logId
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
minute_sleep_4 <- read_csv("minuteSleep_merged_4-5.csv")
## Rows: 188521 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): date
## dbl (3): Id, value, logId
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

Explored a few of the key tables and made sure column names were same for data from 03.12.2016-04.11.2016 tables and 04.12.2016-05.12.2016. Also to help see names to merge data later for further analysis.

head(daily_activity_3)
##           Id ActivityDate TotalSteps TotalDistance TrackerDistance
## 1 1503960366    3/25/2016      11004          7.11            7.11
## 2 1503960366    3/26/2016      17609         11.55           11.55
## 3 1503960366    3/27/2016      12736          8.53            8.53
## 4 1503960366    3/28/2016      13231          8.93            8.93
## 5 1503960366    3/29/2016      12041          7.85            7.85
## 6 1503960366    3/30/2016      10970          7.16            7.16
##   LoggedActivitiesDistance VeryActiveDistance ModeratelyActiveDistance
## 1                        0               2.57                     0.46
## 2                        0               6.92                     0.73
## 3                        0               4.66                     0.16
## 4                        0               3.19                     0.79
## 5                        0               2.16                     1.09
## 6                        0               2.36                     0.51
##   LightActiveDistance SedentaryActiveDistance VeryActiveMinutes
## 1                4.07                       0                33
## 2                3.91                       0                89
## 3                3.71                       0                56
## 4                4.95                       0                39
## 5                4.61                       0                28
## 6                4.29                       0                30
##   FairlyActiveMinutes LightlyActiveMinutes SedentaryMinutes Calories
## 1                  12                  205              804     1819
## 2                  17                  274              588     2154
## 3                   5                  268              605     1944
## 4                  20                  224             1080     1932
## 5                  28                  243              763     1886
## 6                  13                  223             1174     1820
head(daily_activity_4)
##           Id ActivityDate TotalSteps TotalDistance TrackerDistance
## 1 1503960366    4/12/2016      13162          8.50            8.50
## 2 1503960366    4/13/2016      10735          6.97            6.97
## 3 1503960366    4/14/2016      10460          6.74            6.74
## 4 1503960366    4/15/2016       9762          6.28            6.28
## 5 1503960366    4/16/2016      12669          8.16            8.16
## 6 1503960366    4/17/2016       9705          6.48            6.48
##   LoggedActivitiesDistance VeryActiveDistance ModeratelyActiveDistance
## 1                        0               1.88                     0.55
## 2                        0               1.57                     0.69
## 3                        0               2.44                     0.40
## 4                        0               2.14                     1.26
## 5                        0               2.71                     0.41
## 6                        0               3.19                     0.78
##   LightActiveDistance SedentaryActiveDistance VeryActiveMinutes
## 1                6.06                       0                25
## 2                4.71                       0                21
## 3                3.91                       0                30
## 4                2.83                       0                29
## 5                5.04                       0                36
## 6                2.51                       0                38
##   FairlyActiveMinutes LightlyActiveMinutes SedentaryMinutes Calories
## 1                  13                  328              728     1985
## 2                  19                  217              776     1797
## 3                  11                  181             1218     1776
## 4                  34                  209              726     1745
## 5                  10                  221              773     1863
## 6                  20                  164              539     1728
colnames(daily_activity_3)
##  [1] "Id"                       "ActivityDate"            
##  [3] "TotalSteps"               "TotalDistance"           
##  [5] "TrackerDistance"          "LoggedActivitiesDistance"
##  [7] "VeryActiveDistance"       "ModeratelyActiveDistance"
##  [9] "LightActiveDistance"      "SedentaryActiveDistance" 
## [11] "VeryActiveMinutes"        "FairlyActiveMinutes"     
## [13] "LightlyActiveMinutes"     "SedentaryMinutes"        
## [15] "Calories"
colnames(daily_activity_4)
##  [1] "Id"                       "ActivityDate"            
##  [3] "TotalSteps"               "TotalDistance"           
##  [5] "TrackerDistance"          "LoggedActivitiesDistance"
##  [7] "VeryActiveDistance"       "ModeratelyActiveDistance"
##  [9] "LightActiveDistance"      "SedentaryActiveDistance" 
## [11] "VeryActiveMinutes"        "FairlyActiveMinutes"     
## [13] "LightlyActiveMinutes"     "SedentaryMinutes"        
## [15] "Calories"
head(minute_sleep_3)
## # A tibble: 6 Ă— 4
##           Id date                 value       logId
##        <dbl> <chr>                <dbl>       <dbl>
## 1 1503960366 3/13/2016 2:39:30 AM     1 11114919637
## 2 1503960366 3/13/2016 2:40:30 AM     1 11114919637
## 3 1503960366 3/13/2016 2:41:30 AM     1 11114919637
## 4 1503960366 3/13/2016 2:42:30 AM     1 11114919637
## 5 1503960366 3/13/2016 2:43:30 AM     1 11114919637
## 6 1503960366 3/13/2016 2:44:30 AM     1 11114919637
head(minute_sleep_4)
## # A tibble: 6 Ă— 4
##           Id date                 value       logId
##        <dbl> <chr>                <dbl>       <dbl>
## 1 1503960366 4/12/2016 2:47:30 AM     3 11380564589
## 2 1503960366 4/12/2016 2:48:30 AM     2 11380564589
## 3 1503960366 4/12/2016 2:49:30 AM     1 11380564589
## 4 1503960366 4/12/2016 2:50:30 AM     1 11380564589
## 5 1503960366 4/12/2016 2:51:30 AM     1 11380564589
## 6 1503960366 4/12/2016 2:52:30 AM     1 11380564589
colnames(minute_sleep_3)
## [1] "Id"    "date"  "value" "logId"
colnames(minute_sleep_4)
## [1] "Id"    "date"  "value" "logId"

Exploring Data & Summary Statistics

How many unique participants are there in each dataframe?

n_distinct(daily_activity_3$Id)
## [1] 35
n_distinct(daily_activity_4$Id)
## [1] 33
n_distinct(weight_log_info_3$Id)
## [1] 11
n_distinct(weight_log_info_4$Id)
## [1] 8
n_distinct(hourly_calories_3$Id)
## [1] 34
n_distinct(hourly_calories_4$Id)
## [1] 33
n_distinct(hourly_intensities_3$Id)
## [1] 34
n_distinct(hourly_intensities_4$Id)
## [1] 33
n_distinct(minute_sleep_3$Id)
## [1] 23
n_distinct(minute_sleep_4$Id)
## [1] 24

For 03.12.2016-04.11.2016 the number of participants for daily activity is 35, weight log is 11, hourly calories is 34, hourly intensities is 34 and minutes sleep is 23. For 04.12.2016-05.12.2016 the number of participants for daily activity is 33, weight log is 8, hourly calories is 33, hourly intensities is 33 and minutes sleep is 24. No conclusions will be able to be drawn from the weight log data due to the small sample size. Also not all participants participated in the minutes sleep data.

How many observations are there in each dataframe?

nrow(daily_activity_3)
## [1] 457
nrow(daily_activity_4)
## [1] 940
nrow(weight_log_info_3)
## [1] 33
nrow(weight_log_info_4)
## [1] 67
nrow(hourly_calories_3)
## [1] 24084
nrow(hourly_calories_4)
## [1] 22099
nrow(hourly_intensities_3)
## [1] 24084
nrow(hourly_intensities_4)
## [1] 22099
nrow(minute_sleep_3)
## [1] 198559
nrow(minute_sleep_4)
## [1] 188521

457, 940, 33, 67, 24084, 22099, 24084, 22099, 198559, 188521

What are some quick summary statistics we’d want to know about each data frame?

daily_activity_3%>%
  select(TotalSteps,
    TotalDistance,
    SedentaryMinutes) %>%
  summary()
##    TotalSteps    TotalDistance    SedentaryMinutes
##  Min.   :    0   Min.   : 0.000   Min.   :  32.0  
##  1st Qu.: 1988   1st Qu.: 1.410   1st Qu.: 728.0  
##  Median : 5986   Median : 4.090   Median :1057.0  
##  Mean   : 6547   Mean   : 4.664   Mean   : 995.3  
##  3rd Qu.:10198   3rd Qu.: 7.160   3rd Qu.:1285.0  
##  Max.   :28497   Max.   :27.530   Max.   :1440.0
daily_activity_4%>%
  select(TotalSteps,
    TotalDistance,
    SedentaryMinutes) %>%
  summary()
##    TotalSteps    TotalDistance    SedentaryMinutes
##  Min.   :    0   Min.   : 0.000   Min.   :   0.0  
##  1st Qu.: 3790   1st Qu.: 2.620   1st Qu.: 729.8  
##  Median : 7406   Median : 5.245   Median :1057.5  
##  Mean   : 7638   Mean   : 5.490   Mean   : 991.2  
##  3rd Qu.:10727   3rd Qu.: 7.713   3rd Qu.:1229.5  
##  Max.   :36019   Max.   :28.030   Max.   :1440.0
hourly_calories_3%>%
  select(Calories) %>%
  summary()
##     Calories     
##  Min.   : 42.00  
##  1st Qu.: 61.00  
##  Median : 77.00  
##  Mean   : 94.27  
##  3rd Qu.:104.00  
##  Max.   :933.00
hourly_calories_4%>%
  select(Calories) %>%
  summary()
##     Calories     
##  Min.   : 42.00  
##  1st Qu.: 63.00  
##  Median : 83.00  
##  Mean   : 97.39  
##  3rd Qu.:108.00  
##  Max.   :948.00
hourly_intensities_3%>%
  select(TotalIntensity,
      AverageIntensity) %>%
  summary()
##  TotalIntensity   AverageIntensity 
##  Min.   :  0.00   Min.   :0.00000  
##  1st Qu.:  0.00   1st Qu.:0.00000  
##  Median :  1.00   Median :0.01667  
##  Mean   : 10.83   Mean   :0.18044  
##  3rd Qu.: 14.00   3rd Qu.:0.23333  
##  Max.   :180.00   Max.   :3.00000
hourly_intensities_4%>%
  select(TotalIntensity,
      AverageIntensity) %>%
  summary()
##  TotalIntensity   AverageIntensity
##  Min.   :  0.00   Min.   :0.0000  
##  1st Qu.:  0.00   1st Qu.:0.0000  
##  Median :  3.00   Median :0.0500  
##  Mean   : 12.04   Mean   :0.2006  
##  3rd Qu.: 16.00   3rd Qu.:0.2667  
##  Max.   :180.00   Max.   :3.0000
minute_sleep_3 %>%
  select(value) %>%
  summary()
##      value      
##  Min.   :1.000  
##  1st Qu.:1.000  
##  Median :1.000  
##  Mean   :1.086  
##  3rd Qu.:1.000  
##  Max.   :3.000
minute_sleep_4 %>%
  select(value) %>%
  summary()
##      value      
##  Min.   :1.000  
##  1st Qu.:1.000  
##  Median :1.000  
##  Mean   :1.096  
##  3rd Qu.:1.000  
##  Max.   :3.000

Counting number of times Sedentary Minutes = 1440 minutes which is a whole day

count(daily_activity_3, SedentaryMinutes == 1440)
##   SedentaryMinutes == 1440   n
## 1                    FALSE 394
## 2                     TRUE  63
count(daily_activity_4, SedentaryMinutes == 1440)
##   SedentaryMinutes == 1440   n
## 1                    FALSE 861
## 2                     TRUE  79

For March-April daily activity data there are 63 days that sedentary minutes is equal to 1440 so the whole day and 79 days for April-May. This would lead me to believe the person didn’t wear their fitness tracker for the day which could skew some of the data and results.

Getting idea of how often fitness trackers were worn excluding data points where sedentary minutes was equal to 1440

minutes_3 <- select(daily_activity_3,"VeryActiveMinutes", "FairlyActiveMinutes", "LightlyActiveMinutes","SedentaryMinutes")
minutes_3$total_minutes <- rowSums(minutes_3,na.rm =TRUE)
count(minutes_3, total_minutes == 1440)
##   total_minutes == 1440   n
## 1                 FALSE 224
## 2                  TRUE 233
count(minutes_3, total_minutes > 1080)
##   total_minutes > 1080   n
## 1                FALSE 183
## 2                 TRUE 274
minutes_4 <- select(daily_activity_4,"VeryActiveMinutes", "FairlyActiveMinutes", "LightlyActiveMinutes","SedentaryMinutes")
minutes_4$total_minutes <- rowSums(minutes_4,na.rm =TRUE)
count(minutes_4, total_minutes == 1440)
##   total_minutes == 1440   n
## 1                 FALSE 462
## 2                  TRUE 478
count(minutes_4, total_minutes > 1080)
##   total_minutes > 1080   n
## 1                FALSE 372
## 2                 TRUE 568

For March-April the whole day was tracked 170 times(43.1%) and for April-May 394 times(46.3%). More than 2/3rds of the day or 1080 minutes was tracked 211 times(53.6%) for March-April and 489 times(56.8%) for April-May.

Some interesting finds

  • The mean of the total steps for the first month is 6547 and the second is 7638. Evne though the total steps increased in the second month it is still below the daily recommended number of steps per day of 10,000.

  • The max for sedentary minutes in both months is 1440 minutes which equals 24 hours so going to look into this more since unlikely person didn’t move once in whole day.

  • The body averages to burn about 1,300 to more than 2,000 calories without any activity.The mean of the average calories times 24 (to equal a full day) for the first month is 2262.48 and second month is 2337.36.

  • The hourly intensities for both months have a wide range from 0 to 180 while the mean for the first month is only 10.83 and the second is 12.04.

  • The min, max, median and mean for the minute sleep were very similar in both months.

Plotting a few explorations

Relationship between total steps and calories

ggplot(data=daily_activity_3, aes(x=TotalSteps, y= Calories))+
  geom_point(color ="green")+
  geom_smooth(method = "loess") + 
  ggtitle("Total Steps vs Calories (March-April)") +
  theme(plot.title = element_text(hjust = 0.5))
## `geom_smooth()` using formula = 'y ~ x'

ggplot(data=daily_activity_4, aes(x=TotalSteps, y= Calories))+
  geom_point(color="red")+
  geom_smooth(method = "loess") + 
  ggtitle("Total Steps vs Calories (April-May)") +
  theme(plot.title = element_text(hjust = 0.5))
## `geom_smooth()` using formula = 'y ~ x'

As would be expected in both graphs the more steps taken equals more calories burned.

Relationship between time and calories First need to separate the time and date for visualization purposes

hourly_calories_3$ActivityHour=as.POSIXct(hourly_calories_3$ActivityHour, format="%m/%d/%Y %I:%M:%S %p", tz=Sys.timezone())
hourly_calories_3$time <- format(hourly_calories_3$ActivityHour, format = "%H:%M:%S")
hourly_calories_3$date <- format(hourly_calories_3$ActivityHour, format = "%m/%d/%y")

hourly_calories_4$ActivityHour=as.POSIXct(hourly_calories_4$ActivityHour, format="%m/%d/%Y %I:%M:%S %p", tz=Sys.timezone())
hourly_calories_4$time <- format(hourly_calories_4$ActivityHour, format = "%H:%M:%S")
hourly_calories_4$date <- format(hourly_calories_4$ActivityHour, format = "%m/%d/%y")

calories_3 <- hourly_calories_3 %>%
  group_by(time) %>%
  drop_na() %>%
  summarise(mean_calories_3 = mean(Calories))
ggplot(data=calories_3) + 
  geom_histogram(aes(x=time, y=mean_calories_3), fill="green", stat="identity")+
  theme(axis.text.x = element_text(angle=90))+
  ggtitle("Calories vs Time (March-April)") +
  theme(plot.title = element_text(hjust = 0.5))
## Warning in geom_histogram(aes(x = time, y = mean_calories_3), fill = "green", :
## Ignoring unknown parameters: `binwidth`, `bins`, and `pad`

calories_4 <- hourly_calories_4 %>%
  group_by(time) %>%
  drop_na() %>%
  summarise(mean_calories_4 = mean(Calories))
ggplot(data=calories_4) + 
  geom_histogram(aes(x=time, y=mean_calories_4), fill="red", stat="identity")+
  theme(axis.text.x = element_text(angle=90))+
  ggtitle("Calories vs Time (April-May)") +
  theme(plot.title = element_text(hjust = 0.5))
## Warning in geom_histogram(aes(x = time, y = mean_calories_4), fill = "red", :
## Ignoring unknown parameters: `binwidth`, `bins`, and `pad`

Both graphs are very similar in shape. We can see that most calories are burned from 7 am to 9 pm which makes sense since this is when people are normally awake. The most calories are burned at 7 pm in March to April and at 6 pm in April to May so similar times.

Merging Data

Merging calories and intensities First we must make the data formats match for the datasets

hourly_intensities_3$ActivityHour=as.POSIXct(hourly_intensities_3$ActivityHour, format="%m/%d/%Y %I:%M:%S %p", tz=Sys.timezone())
hourly_intensities_3$time <- format(hourly_intensities_3$ActivityHour, format = "%H:%M:%S")
hourly_intensities_3$date <- format(hourly_intensities_3$ActivityHour, format = "%m/%d/%y")

hourly_intensities_4$ActivityHour=as.POSIXct(hourly_intensities_4$ActivityHour, format="%m/%d/%Y %I:%M:%S %p", tz=Sys.timezone())
hourly_intensities_4$time <- format(hourly_intensities_4$ActivityHour, format = "%H:%M:%S")
hourly_intensities_4$date <- format(hourly_intensities_4$ActivityHour, format = "%m/%d/%y")

calories_intensities_3 <- merge(hourly_calories_3, hourly_intensities_3, by=c('Id','ActivityHour'))

calories_intensities_4 <- merge(hourly_calories_4, hourly_intensities_4, by=c('Id','ActivityHour'))

Graphing the merged data

ggplot(data=calories_intensities_3, aes(x=TotalIntensity, y= Calories))+
  geom_point(color ="green")+
  geom_smooth(method = "loess") + 
  ggtitle("Calories vs Intensities (March-April)") +
  theme(plot.title = element_text(hjust = 0.5))
## `geom_smooth()` using formula = 'y ~ x'

ggplot(data=calories_intensities_4, aes(x=TotalIntensity, y= Calories))+
  geom_point(color="red")+
  geom_smooth(method = "loess") + 
  ggtitle("Calories vs Intensities (April-May)") +
  theme(plot.title = element_text(hjust = 0.5))
## `geom_smooth()` using formula = 'y ~ x'

As expected in both graphs the higher the intensity, the more calories burned. The graph seems to be starting to take more of an exponential curve than a linear one.

Summary

After going through the data we come back to the initial question of how non-Bellabeat smart devices are used to be able to better market Bellabeat products. We used the [Fitbit data] (https://www.kaggle.com/datasets/arashnic/fitbit/data) to draw some conclusions.

  • The percentage for the tracker being used for the whole day and 2/3rds of the day went up from the first month of tracking data to the second month. So did the total number of steps. So one recommendation I would make is for the Bellabeat app to have a notification in the morning to remember your tracker. By remembering your tracker you’re going to be more cautious about meeting your daily goal of steps so more likely to meet that goal.

  • More steps means more calories burned so having products or app giving users a reminder about number of steps could help users reach their end goal. When giving reminder could also give suggestions on how to higher intensity to help burn more calories.

  • In the steps, distance, calorie, total intensity and average intensity we saw an increase in the mean from the first month to the second month while seeing a decrease in the mean of sedentary minutes from the first month to the second. So by ads, products and app helping to get users into a routine with tips and info on a healthy lifestyle will help them to continue to grow.