BellaBeat Analysis Project

Background BellaBeat is a tech manufacturer focused around health products for women. They are looking to potentially become a big player in the global smart device market. Our cofounder, Urška Sršen, wants to analyze smart device fitness data to gain insight on how to grow the BellaBeat company. We will focus on improving the BellaBeat Leaf which can be worn as a bracelet, necklace or clip on the consumer. This leaf tracks the users’ activity, sleep, and stress levels.

Our business task is: Identify the current trends of smart device usage and connect this to the marketing strategy for the company of Bellabeat

We will start with loading the necessary packages for our project

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.0     ✔ 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(readr)
library(ggplot2)
library(dbplyr)
## 
## Attaching package: 'dbplyr'
## 
## The following objects are masked from 'package:dplyr':
## 
##     ident, sql
library(lubridate)
library(janitor)
## 
## Attaching package: 'janitor'
## 
## The following objects are masked from 'package:stats':
## 
##     chisq.test, fisher.test

Importing Datasets

Next we will look to import our datasets using read_csv This will look different depending on where your files are located in the directory

dailyactivity <- read_csv("/Users/joebeadles/Desktop/BellaBeat Case Study/FitBit Fitness Tracker Data 04:16/files used/dailyActivity_merged.csv")
## Rows: 940 Columns: 15
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr  (1): ActivityDate
## dbl (14): Id, TotalSteps, TotalDistance, TrackerDistance, LoggedActivitiesDi...
## 
## ℹ 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.
dailycalories <- read_csv("/Users/joebeadles/Desktop/BellaBeat Case Study/FitBit Fitness Tracker Data 04:16/files used/dailyCalories_merged.csv")
## Rows: 940 Columns: 3
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): ActivityDay
## 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.
dailyintensities <- read_csv("/Users/joebeadles/Desktop/BellaBeat Case Study/FitBit Fitness Tracker Data 04:16/files used/dailyIntensities_merged.csv")
## Rows: 940 Columns: 10
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): ActivityDay
## dbl (9): Id, SedentaryMinutes, LightlyActiveMinutes, FairlyActiveMinutes, Ve...
## 
## ℹ 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.
dailysteps <- read_csv("/Users/joebeadles/Desktop/BellaBeat Case Study/FitBit Fitness Tracker Data 04:16/files used/dailySteps_merged.csv")
## Rows: 940 Columns: 3
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): ActivityDay
## dbl (2): Id, StepTotal
## 
## ℹ 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.
minutesleep <- read.csv("/Users/joebeadles/Desktop/BellaBeat Case Study/FitBit Fitness Tracker Data 04:16/files used/minuteSleep_merged.csv")
sleepDay <- read.csv("/Users/joebeadles/Desktop/BellaBeat Case Study/FitBit Fitness Tracker Data 04:16/files used/sleepDay_merged.csv")
weightlog <- read.csv("/Users/joebeadles/Desktop/BellaBeat Case Study/FitBit Fitness Tracker Data 04:16/files used/weightLoginfo_merged.csv")

First impressions

After analyzing the data, we want to get a good look at what our data looks like. You can use head(), glimpse(), or str(), but I chose to use head().

head(dailyactivity)
## # A tibble: 6 × 15
##           Id ActivityDate TotalSteps TotalDistance TrackerDistance
##        <dbl> <chr>             <dbl>         <dbl>           <dbl>
## 1 1503960366 4/12/2016         13162          8.5             8.5 
## 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
## # ℹ 10 more variables: LoggedActivitiesDistance <dbl>,
## #   VeryActiveDistance <dbl>, ModeratelyActiveDistance <dbl>,
## #   LightActiveDistance <dbl>, SedentaryActiveDistance <dbl>,
## #   VeryActiveMinutes <dbl>, FairlyActiveMinutes <dbl>,
## #   LightlyActiveMinutes <dbl>, SedentaryMinutes <dbl>, Calories <dbl>
head(dailycalories)
## # A tibble: 6 × 3
##           Id ActivityDay Calories
##        <dbl> <chr>          <dbl>
## 1 1503960366 4/12/2016       1985
## 2 1503960366 4/13/2016       1797
## 3 1503960366 4/14/2016       1776
## 4 1503960366 4/15/2016       1745
## 5 1503960366 4/16/2016       1863
## 6 1503960366 4/17/2016       1728
head(dailyintensities)
## # A tibble: 6 × 10
##         Id ActivityDay SedentaryMinutes LightlyActiveMinutes FairlyActiveMinutes
##      <dbl> <chr>                  <dbl>                <dbl>               <dbl>
## 1   1.50e9 4/12/2016                728                  328                  13
## 2   1.50e9 4/13/2016                776                  217                  19
## 3   1.50e9 4/14/2016               1218                  181                  11
## 4   1.50e9 4/15/2016                726                  209                  34
## 5   1.50e9 4/16/2016                773                  221                  10
## 6   1.50e9 4/17/2016                539                  164                  20
## # ℹ 5 more variables: VeryActiveMinutes <dbl>, SedentaryActiveDistance <dbl>,
## #   LightActiveDistance <dbl>, ModeratelyActiveDistance <dbl>,
## #   VeryActiveDistance <dbl>
head(dailysteps)
## # A tibble: 6 × 3
##           Id ActivityDay StepTotal
##        <dbl> <chr>           <dbl>
## 1 1503960366 4/12/2016       13162
## 2 1503960366 4/13/2016       10735
## 3 1503960366 4/14/2016       10460
## 4 1503960366 4/15/2016        9762
## 5 1503960366 4/16/2016       12669
## 6 1503960366 4/17/2016        9705
head(minutesleep)
##           Id                 date value       logId
## 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
head(sleepDay)
##           Id              SleepDay TotalSleepRecords TotalMinutesAsleep
## 1 1503960366 4/12/2016 12:00:00 AM                 1                327
## 2 1503960366 4/13/2016 12:00:00 AM                 2                384
## 3 1503960366 4/15/2016 12:00:00 AM                 1                412
## 4 1503960366 4/16/2016 12:00:00 AM                 2                340
## 5 1503960366 4/17/2016 12:00:00 AM                 1                700
## 6 1503960366 4/19/2016 12:00:00 AM                 1                304
##   TotalTimeInBed
## 1            346
## 2            407
## 3            442
## 4            367
## 5            712
## 6            320
head(weightlog)
##           Id                  Date WeightKg WeightPounds Fat   BMI
## 1 1503960366  5/2/2016 11:59:59 PM     52.6     115.9631  22 22.65
## 2 1503960366  5/3/2016 11:59:59 PM     52.6     115.9631  NA 22.65
## 3 1927972279  4/13/2016 1:08:52 AM    133.5     294.3171  NA 47.54
## 4 2873212765 4/21/2016 11:59:59 PM     56.7     125.0021  NA 21.45
## 5 2873212765 5/12/2016 11:59:59 PM     57.3     126.3249  NA 21.69
## 6 4319703577 4/17/2016 11:59:59 PM     72.4     159.6147  25 27.45
##   IsManualReport        LogId
## 1           True 1.462234e+12
## 2           True 1.462320e+12
## 3          False 1.460510e+12
## 4           True 1.461283e+12
## 5           True 1.463098e+12
## 6           True 1.460938e+12

First stage of cleaning

Before doing anything else, we want to make sure our data is clean for analysis

n_distinct(dailyactivity$Id)
## [1] 33
n_distinct(dailycalories$Id)
## [1] 33
n_distinct(dailyintensities$Id)
## [1] 33
n_distinct(dailysteps$Id)
## [1] 33
n_distinct(minutesleep$Id)
## [1] 24
n_distinct(sleepDay$Id)
## [1] 24
n_distinct(weightlog$Id)
## [1] 8

Removing data

Here we decided to remove three datasets that would not help with answering our business task

remove("minutesleep")
remove("weightlog")
remove("dailyintensities")

Locating and removing duplicates

Next, it’s essential to remove any duplicates that could interfere with our analysis

sum(duplicated(dailyactivity))
## [1] 0
sum(duplicated(dailycalories))
## [1] 0
sum(duplicated(dailysteps))
## [1] 0
sum(duplicated(sleepDay))
## [1] 3
dailyactivity <- dailyactivity %>%
  distinct() %>%
  drop_na()
dailycalories <- dailycalories %>%
  distinct() %>%
  drop_na()
dailysteps <- dailysteps %>%
  distinct() %>%
  drop_na()
sleepDay <- sleepDay %>%
  distinct() %>%
  drop_na()
sum(duplicated(dailyactivity))
## [1] 0
sum(duplicated(dailycalories))
## [1] 0
sum(duplicated(dailysteps))
## [1] 0
sum(duplicated(sleepDay))
## [1] 0

Cleaning names and column names

dailyactivity <- rename_with(dailyactivity, tolower)
dailycalories <- rename_with(dailycalories, tolower)
dailysteps <- rename_with(dailysteps, tolower)
sleepDay <- rename_with(sleepDay, tolower)
clean_names(dailyactivity)
## # A tibble: 940 × 15
##            id activitydate totalsteps totaldistance trackerdistance
##         <dbl> <chr>             <dbl>         <dbl>           <dbl>
##  1 1503960366 4/12/2016         13162          8.5             8.5 
##  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
##  7 1503960366 4/18/2016         13019          8.59            8.59
##  8 1503960366 4/19/2016         15506          9.88            9.88
##  9 1503960366 4/20/2016         10544          6.68            6.68
## 10 1503960366 4/21/2016          9819          6.34            6.34
## # ℹ 930 more rows
## # ℹ 10 more variables: loggedactivitiesdistance <dbl>,
## #   veryactivedistance <dbl>, moderatelyactivedistance <dbl>,
## #   lightactivedistance <dbl>, sedentaryactivedistance <dbl>,
## #   veryactiveminutes <dbl>, fairlyactiveminutes <dbl>,
## #   lightlyactiveminutes <dbl>, sedentaryminutes <dbl>, calories <dbl>
clean_names(dailycalories)
## # A tibble: 940 × 3
##            id activityday calories
##         <dbl> <chr>          <dbl>
##  1 1503960366 4/12/2016       1985
##  2 1503960366 4/13/2016       1797
##  3 1503960366 4/14/2016       1776
##  4 1503960366 4/15/2016       1745
##  5 1503960366 4/16/2016       1863
##  6 1503960366 4/17/2016       1728
##  7 1503960366 4/18/2016       1921
##  8 1503960366 4/19/2016       2035
##  9 1503960366 4/20/2016       1786
## 10 1503960366 4/21/2016       1775
## # ℹ 930 more rows
clean_names(dailysteps)
## # A tibble: 940 × 3
##            id activityday steptotal
##         <dbl> <chr>           <dbl>
##  1 1503960366 4/12/2016       13162
##  2 1503960366 4/13/2016       10735
##  3 1503960366 4/14/2016       10460
##  4 1503960366 4/15/2016        9762
##  5 1503960366 4/16/2016       12669
##  6 1503960366 4/17/2016        9705
##  7 1503960366 4/18/2016       13019
##  8 1503960366 4/19/2016       15506
##  9 1503960366 4/20/2016       10544
## 10 1503960366 4/21/2016        9819
## # ℹ 930 more rows
head(clean_names(sleepDay))
##           id              sleepday totalsleeprecords totalminutesasleep
## 1 1503960366 4/12/2016 12:00:00 AM                 1                327
## 2 1503960366 4/13/2016 12:00:00 AM                 2                384
## 3 1503960366 4/15/2016 12:00:00 AM                 1                412
## 4 1503960366 4/16/2016 12:00:00 AM                 2                340
## 5 1503960366 4/17/2016 12:00:00 AM                 1                700
## 6 1503960366 4/19/2016 12:00:00 AM                 1                304
##   totaltimeinbed
## 1            346
## 2            407
## 3            442
## 4            367
## 5            712
## 6            320

Utilizing lubridate to change all our dates from character types to date types

Capital “Y” for year is important our else your dates will change

dailyactivity <- dailyactivity %>%
  rename(date = activitydate) %>%
  mutate(date = as_date(date, format = "%m/%d/%Y"))

dailycalories <- dailycalories %>%
  rename(date = activityday) %>%
  mutate(date = as_date(date, format = "%m/%d/%Y"))

dailysteps <- dailysteps %>%
  rename(date = activityday) %>%
  mutate(date = as_date(date, format = "%m/%d/%Y"))

Separating the date and time in our sleepDay data set

sleepDay[c('Date', 'Time')] <- str_split_fixed(sleepDay$sleepday, ' ', 2)

sleepDay <- sleepDay %>%
  rename(date = Date) %>%
  mutate(date = as_date(date, format = "%m/%d/%Y"))

sleepDay <- subset(sleepDay, select = -c(Time))

Taking a look at our clean datasets

head(dailyactivity)
## # A tibble: 6 × 15
##           id date       totalsteps totaldistance trackerdistance
##        <dbl> <date>          <dbl>         <dbl>           <dbl>
## 1 1503960366 2016-04-12      13162          8.5             8.5 
## 2 1503960366 2016-04-13      10735          6.97            6.97
## 3 1503960366 2016-04-14      10460          6.74            6.74
## 4 1503960366 2016-04-15       9762          6.28            6.28
## 5 1503960366 2016-04-16      12669          8.16            8.16
## 6 1503960366 2016-04-17       9705          6.48            6.48
## # ℹ 10 more variables: loggedactivitiesdistance <dbl>,
## #   veryactivedistance <dbl>, moderatelyactivedistance <dbl>,
## #   lightactivedistance <dbl>, sedentaryactivedistance <dbl>,
## #   veryactiveminutes <dbl>, fairlyactiveminutes <dbl>,
## #   lightlyactiveminutes <dbl>, sedentaryminutes <dbl>, calories <dbl>
head(dailycalories)
## # A tibble: 6 × 3
##           id date       calories
##        <dbl> <date>        <dbl>
## 1 1503960366 2016-04-12     1985
## 2 1503960366 2016-04-13     1797
## 3 1503960366 2016-04-14     1776
## 4 1503960366 2016-04-15     1745
## 5 1503960366 2016-04-16     1863
## 6 1503960366 2016-04-17     1728
head(dailysteps)
## # A tibble: 6 × 3
##           id date       steptotal
##        <dbl> <date>         <dbl>
## 1 1503960366 2016-04-12     13162
## 2 1503960366 2016-04-13     10735
## 3 1503960366 2016-04-14     10460
## 4 1503960366 2016-04-15      9762
## 5 1503960366 2016-04-16     12669
## 6 1503960366 2016-04-17      9705
head(sleepDay)
##           id              sleepday totalsleeprecords totalminutesasleep
## 1 1503960366 4/12/2016 12:00:00 AM                 1                327
## 2 1503960366 4/13/2016 12:00:00 AM                 2                384
## 3 1503960366 4/15/2016 12:00:00 AM                 1                412
## 4 1503960366 4/16/2016 12:00:00 AM                 2                340
## 5 1503960366 4/17/2016 12:00:00 AM                 1                700
## 6 1503960366 4/19/2016 12:00:00 AM                 1                304
##   totaltimeinbed       date
## 1            346 2016-04-12
## 2            407 2016-04-13
## 3            442 2016-04-15
## 4            367 2016-04-16
## 5            712 2016-04-17
## 6            320 2016-04-19

Summaries

dailyactivity %>%
  select(totalsteps,
         totaldistance,
         calories,
         sedentaryminutes) %>%
  summary()
##    totalsteps    totaldistance       calories    sedentaryminutes
##  Min.   :    0   Min.   : 0.000   Min.   :   0   Min.   :   0.0  
##  1st Qu.: 3790   1st Qu.: 2.620   1st Qu.:1828   1st Qu.: 729.8  
##  Median : 7406   Median : 5.245   Median :2134   Median :1057.5  
##  Mean   : 7638   Mean   : 5.490   Mean   :2304   Mean   : 991.2  
##  3rd Qu.:10727   3rd Qu.: 7.713   3rd Qu.:2793   3rd Qu.:1229.5  
##  Max.   :36019   Max.   :28.030   Max.   :4900   Max.   :1440.0
dailycalories %>%
  select(calories) %>%
  summary()
##     calories   
##  Min.   :   0  
##  1st Qu.:1828  
##  Median :2134  
##  Mean   :2304  
##  3rd Qu.:2793  
##  Max.   :4900
dailysteps %>%
  select(steptotal) %>%
  summary()
##    steptotal    
##  Min.   :    0  
##  1st Qu.: 3790  
##  Median : 7406  
##  Mean   : 7638  
##  3rd Qu.:10727  
##  Max.   :36019

Finding the difference between time spent in bed vs. time spent asleep

sleepDay %>%
  select(totalminutesasleep,
         totaltimeinbed) %>%
  summary()
##  totalminutesasleep totaltimeinbed 
##  Min.   : 58.0      Min.   : 61.0  
##  1st Qu.:361.0      1st Qu.:403.8  
##  Median :432.5      Median :463.0  
##  Mean   :419.2      Mean   :458.5  
##  3rd Qu.:490.0      3rd Qu.:526.0  
##  Max.   :796.0      Max.   :961.0
minutesasleep = sleepDay$totalminutesasleep

timeinbed = sleepDay$totaltimeinbed

difference = mean(timeinbed-minutesasleep)

print(difference)
## [1] 39.30976

Plots

Now that we finished our summary analysis, lets dive into relationships between the variables

combined_sleep_activity <- merge(dailyactivity, 
                                 sleepDay,
                                 by = "id",
                                 all = TRUE)
ggplot(combined_sleep_activity)+
  geom_point(mapping = aes(x = totalsteps, y = totalminutesasleep))
## Warning: Removed 227 rows containing missing values or values outside the scale range
## (`geom_point()`).

No meaningful connections or relationships can be made from this graph, lets try another one

ggplot(combined_sleep_activity) + 
  geom_point(mapping = aes(x = totalsteps, y = sedentaryminutes)) + 
  geom_smooth(stat = 'smooth', method = 'gam', formula = y~x, aes(totalsteps, sedentaryminutes), color = 'blue') +
  labs(title = "Total Steps vs. Sedentary Minutes", 
       subtitle = "Sleep Activity Data", 
       caption = "Results from 2016", 
       x = "Total Steps", 
       y = "Sedentary Minutes")

Negative correlation represented through more steps correlates to less sedentary minutes and vice versa

Health statistics by time spent in bed

ggplot(combined_sleep_activity)+
  geom_point(mapping = aes(x = totalminutesasleep, y = totaltimeinbed), colour = 'skyblue2')+
  labs(title = "Time spent in bed vs. Time spent asleep", caption = "Data from 2016", x = "Minutes asleep", y = "Time in Bed")+
  geom_smooth(stat = 'smooth', method = 'gam', formula = y~x, aes(totalminutesasleep, totaltimeinbed), color = 'skyblue4')
## Warning: Removed 227 rows containing non-finite outside the scale range
## (`stat_smooth()`).
## Warning: Removed 227 rows containing missing values or values outside the scale range
## (`geom_point()`).

No surprise we see a positive correlation between time spent in bed and time asleep

Daily steps and daily calories

Next, let’s look at some correlations between daily steps and daily calories

combined_steps_calories <- merge(dailyactivity, 
                                 dailysteps, 
                                 by = "id", 
                                 all = TRUE)

ggplot(data=combined_steps_calories)+
  geom_point(mapping = aes(x = totalsteps, y = calories))+
  geom_smooth(stat = 'smooth', method = 'gam', formula = y~x, aes(totalsteps, calories))+
  labs(title = "Total Steps vs. Calories", caption = "Data from 2016")

The more steps our participants took, the more calories they burned throughout their daily life

Distribution of average sleep

Finally, we will look at a distribution of sleep from our participants

sleepDay$avghours <- (sleepDay$totalminutesasleep)/60

ggplot(data = sleepDay)+
  geom_histogram(aes(x = avghours), fill = 'skyblue3', color = 'white')+
  labs(title = "Average hours of sleep", 
       subtitle = "Each individual had a month of their sleep data recorded", 
       caption = "Data from 2016", x = "Average hours of sleep", y = "Number of days")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Normal distribution