Introduction

Bellabeat makes high-tech health products specifically for women. By tracking things like activity levels, sleep, stress, and reproductive health, Bellabeat gives women valuable insights into their own bodies and habits. They’re currently a successful smaller company, but they have the potential to grow significantly in the global smart device market. One of the founders, Urška Sršen, thinks that studying the fitness data from these devices could lead to new ways for the company to expand.The case study follows the six step data analysis process:

Ask

Business Task

The goal is to find ways for Bellabeat to grow and suggest improvements to their marketing by looking at current trends in how people use smart devices.

-Business Questions

  • What are some trends in smart device usage?

  • How could these trends apply to Bellabeat customers?

  • How could these trends help influence Bellabeat marketing strategy?

-Stakeholders Involved

  • Urka Sren - The cofounder and Chief Creative Officer of Bellabeat.
  • Sando Mur - Bellabeat cofounder and key member of Bellabeat executive team
  • The Marketing Analytics team at Bellabeat

Prepare Data

-Validity of the Data

For this analysis, we’re using publicly available fitness tracker data from Fitbit users who agreed to share it. This data will help us understand user habits. While the dataset includes information from 30 consenting users (meeting the minimum sample size), was collected over a month in 2016 (April 12th to May 12th), and includes a range of data points like activity levels, sleep, and steps, its origin is a third-party source (Amazon Mechanical Turk surveys), making it difficult to verify its accuracy and limiting our ability to cite its source.

-Install Packages and Import Datasets

After reviewing the dataset, the following points were noted:

  • Dataset Selection: The dataset consists of 18 tables.
  • Redundancy: The ‘dailyCalories_merged’, ‘dailyIntensities_merged’, and ‘dailyIntensities_merged’ tables contain data already present in the ‘dailyActivity_merged’ table.
  • Insufficient Data: The ‘weightLogInfo_merged’ table is excluded due to data from only 8 users.
  • Inconsistent User IDs: The ‘sleepDay_merged’ table has 24 unique user IDs, while the other tables have 33.
  • User Categorization: Users will be categorized for later analysis.
  • Unique Identifier: A new column combining ID and Activity Date will be created for unique identification.
  • Time Format: Time columns will be reformatted.
# Load necessary libraries
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.4     ✔ 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(ggplot2)
library(dplyr)
library(tidyr)
library(lubridate)
library(readr)
library(gridExtra)
## 
## Attaching package: 'gridExtra'
## 
## The following object is masked from 'package:dplyr':
## 
##     combine
#Load datasets
daily_activity <- read_csv("Fitabase Data 4.12.16-5.12.16/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.
daily_calories <- read_csv("Fitabase Data 4.12.16-5.12.16/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.
daily_intensities <- read_csv("Fitabase Data 4.12.16-5.12.16/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.
daily_steps <- read_csv("Fitabase Data 4.12.16-5.12.16/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.
daily_sleep <- read_csv("Fitabase Data 4.12.16-5.12.16/sleepDay_merged.csv")
## Rows: 413 Columns: 5
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): SleepDay
## dbl (4): Id, TotalSleepRecords, TotalMinutesAsleep, TotalTimeInBed
## 
## ℹ 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.
weight_info <- read_csv("Fitabase Data 4.12.16-5.12.16/weightLogInfo_merged.csv")
## Rows: 67 Columns: 8
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): Date
## dbl (6): Id, WeightKg, WeightPounds, Fat, BMI, LogId
## lgl (1): IsManualReport
## 
## ℹ 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.
# data preview
head(daily_activity)
## # 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>
colnames(daily_activity)
##  [1] "Id"                       "ActivityDate"            
##  [3] "TotalSteps"               "TotalDistance"           
##  [5] "TrackerDistance"          "LoggedActivitiesDistance"
##  [7] "VeryActiveDistance"       "ModeratelyActiveDistance"
##  [9] "LightActiveDistance"      "SedentaryActiveDistance" 
## [11] "VeryActiveMinutes"        "FairlyActiveMinutes"     
## [13] "LightlyActiveMinutes"     "SedentaryMinutes"        
## [15] "Calories"
n_distinct(daily_activity$Id)
## [1] 33
head(daily_calories)
## # 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
colnames(daily_calories)
## [1] "Id"          "ActivityDay" "Calories"
n_distinct(daily_calories$Id)
## [1] 33
head(daily_intensities)
## # 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>
colnames(daily_intensities)
##  [1] "Id"                       "ActivityDay"             
##  [3] "SedentaryMinutes"         "LightlyActiveMinutes"    
##  [5] "FairlyActiveMinutes"      "VeryActiveMinutes"       
##  [7] "SedentaryActiveDistance"  "LightActiveDistance"     
##  [9] "ModeratelyActiveDistance" "VeryActiveDistance"
n_distinct(daily_intensities$Id)
## [1] 33
head(daily_steps)
## # 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
colnames(daily_steps)
## [1] "Id"          "ActivityDay" "StepTotal"
n_distinct(daily_steps$Id)
## [1] 33
head(daily_sleep)
## # A tibble: 6 × 5
##           Id SleepDay        TotalSleepRecords TotalMinutesAsleep TotalTimeInBed
##        <dbl> <chr>                       <dbl>              <dbl>          <dbl>
## 1 1503960366 4/12/2016 12:0…                 1                327            346
## 2 1503960366 4/13/2016 12:0…                 2                384            407
## 3 1503960366 4/15/2016 12:0…                 1                412            442
## 4 1503960366 4/16/2016 12:0…                 2                340            367
## 5 1503960366 4/17/2016 12:0…                 1                700            712
## 6 1503960366 4/19/2016 12:0…                 1                304            320
colnames(daily_sleep)
## [1] "Id"                 "SleepDay"           "TotalSleepRecords" 
## [4] "TotalMinutesAsleep" "TotalTimeInBed"
n_distinct(daily_sleep$Id)
## [1] 24
head(weight_info)
## # A tibble: 6 × 8
##           Id Date       WeightKg WeightPounds   Fat   BMI IsManualReport   LogId
##        <dbl> <chr>         <dbl>        <dbl> <dbl> <dbl> <lgl>            <dbl>
## 1 1503960366 5/2/2016 …     52.6         116.    22  22.6 TRUE           1.46e12
## 2 1503960366 5/3/2016 …     52.6         116.    NA  22.6 TRUE           1.46e12
## 3 1927972279 4/13/2016…    134.          294.    NA  47.5 FALSE          1.46e12
## 4 2873212765 4/21/2016…     56.7         125.    NA  21.5 TRUE           1.46e12
## 5 2873212765 5/12/2016…     57.3         126.    NA  21.7 TRUE           1.46e12
## 6 4319703577 4/17/2016…     72.4         160.    25  27.5 TRUE           1.46e12
colnames(weight_info)
## [1] "Id"             "Date"           "WeightKg"       "WeightPounds"  
## [5] "Fat"            "BMI"            "IsManualReport" "LogId"
n_distinct(weight_info$Id)
## [1] 8
daily_activity %>% 
  select(TotalSteps, TotalDistance, VeryActiveMinutes) %>% 
  summary()
##    TotalSteps    TotalDistance    VeryActiveMinutes
##  Min.   :    0   Min.   : 0.000   Min.   :  0.00   
##  1st Qu.: 3790   1st Qu.: 2.620   1st Qu.:  0.00   
##  Median : 7406   Median : 5.245   Median :  4.00   
##  Mean   : 7638   Mean   : 5.490   Mean   : 21.16   
##  3rd Qu.:10727   3rd Qu.: 7.713   3rd Qu.: 32.00   
##  Max.   :36019   Max.   :28.030   Max.   :210.00

-Remove duplicate rows

# Remove duplicate rows
daily_activity <- daily_activity %>% distinct()
daily_calories <- daily_calories %>% distinct()
daily_steps <- daily_steps %>% distinct()
daily_sleep <- daily_sleep %>% distinct()
daily_intensities <- daily_intensities %>% distinct()
weight_info <- weight_info %>% distinct()

-Format Dates

# Format date columns
daily_activity <- daily_activity %>%
  mutate(ActivityDate = mdy(ActivityDate))

daily_calories <- daily_calories %>%
  mutate(ActivityDay = mdy(ActivityDay))

daily_steps <- daily_steps %>%
  mutate(ActivityDay = mdy(ActivityDay))

daily_sleep <- daily_sleep %>%
  mutate(SleepDay = mdy_hms(SleepDay))

daily_intensities <- daily_intensities %>%
  mutate(ActivityDay = mdy(ActivityDay))

weight_info <- weight_info %>%
  mutate(Date = mdy_hms(Date))

-Inspect Datasets

# Validate the cleaned datasets
glimpse(daily_activity)
## Rows: 940
## Columns: 15
## $ Id                       <dbl> 1503960366, 1503960366, 1503960366, 150396036…
## $ ActivityDate             <date> 2016-04-12, 2016-04-13, 2016-04-14, 2016-04-…
## $ 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…
glimpse(daily_calories)
## Rows: 940
## Columns: 3
## $ Id          <dbl> 1503960366, 1503960366, 1503960366, 1503960366, 1503960366…
## $ ActivityDay <date> 2016-04-12, 2016-04-13, 2016-04-14, 2016-04-15, 2016-04-1…
## $ Calories    <dbl> 1985, 1797, 1776, 1745, 1863, 1728, 1921, 2035, 1786, 1775…
glimpse(daily_steps)
## Rows: 940
## Columns: 3
## $ Id          <dbl> 1503960366, 1503960366, 1503960366, 1503960366, 1503960366…
## $ ActivityDay <date> 2016-04-12, 2016-04-13, 2016-04-14, 2016-04-15, 2016-04-1…
## $ StepTotal   <dbl> 13162, 10735, 10460, 9762, 12669, 9705, 13019, 15506, 1054…
glimpse(daily_sleep)
## Rows: 410
## Columns: 5
## $ Id                 <dbl> 1503960366, 1503960366, 1503960366, 1503960366, 150…
## $ SleepDay           <dttm> 2016-04-12, 2016-04-13, 2016-04-15, 2016-04-16, 20…
## $ 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…
glimpse(daily_intensities)
## Rows: 940
## Columns: 10
## $ Id                       <dbl> 1503960366, 1503960366, 1503960366, 150396036…
## $ ActivityDay              <date> 2016-04-12, 2016-04-13, 2016-04-14, 2016-04-…
## $ SedentaryMinutes         <dbl> 728, 776, 1218, 726, 773, 539, 1149, 775, 818…
## $ LightlyActiveMinutes     <dbl> 328, 217, 181, 209, 221, 164, 233, 264, 205, …
## $ FairlyActiveMinutes      <dbl> 13, 19, 11, 34, 10, 20, 16, 31, 12, 8, 27, 21…
## $ VeryActiveMinutes        <dbl> 25, 21, 30, 29, 36, 38, 42, 50, 28, 19, 66, 4…
## $ SedentaryActiveDistance  <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
## $ LightActiveDistance      <dbl> 6.06, 4.71, 3.91, 2.83, 5.04, 2.51, 4.71, 5.0…
## $ ModeratelyActiveDistance <dbl> 0.55, 0.69, 0.40, 1.26, 0.41, 0.78, 0.64, 1.3…
## $ VeryActiveDistance       <dbl> 1.88, 1.57, 2.44, 2.14, 2.71, 3.19, 3.25, 3.5…
glimpse(weight_info)
## Rows: 67
## Columns: 8
## $ Id             <dbl> 1503960366, 1503960366, 1927972279, 2873212765, 2873212…
## $ Date           <dttm> 2016-05-02 23:59:59, 2016-05-03 23:59:59, 2016-04-13 0…
## $ WeightKg       <dbl> 52.6, 52.6, 133.5, 56.7, 57.3, 72.4, 72.3, 69.7, 70.3, …
## $ WeightPounds   <dbl> 115.9631, 115.9631, 294.3171, 125.0021, 126.3249, 159.6…
## $ Fat            <dbl> 22, NA, NA, NA, NA, 25, NA, NA, NA, NA, NA, NA, NA, NA,…
## $ BMI            <dbl> 22.65, 22.65, 47.54, 21.45, 21.69, 27.45, 27.38, 27.25,…
## $ IsManualReport <lgl> TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, …
## $ LogId          <dbl> 1.462234e+12, 1.462320e+12, 1.460510e+12, 1.461283e+12,…
#Define the number of unique rows by Id column
n_distinct(daily_activity$Id)
## [1] 33
n_distinct(daily_sleep$Id)
## [1] 24
n_distinct(weight_info$Id)
## [1] 8

Data Processing

-User Segmentation:

Users will be categorized based on their activity levels using quartile-based segmentation.

The following steps were taken:

  1. The average steps, average distance, and average minutes of very active time were calculated for each user.
user_avg_info <-
  daily_activity %>% 
  group_by(Id) %>% 
  drop_na() %>% 
  summarise( avg_step = mean(TotalSteps),
             avg_distance = mean(TotalDistance),
             avg_very_active_min = mean(VeryActiveMinutes)
  )
  1. Then quartile TotalStep, TotalDistance, and VeryActiveMinutes to set up the metric for define users on the next step.
p75_step <- quantile(daily_activity$TotalSteps, probs =0.75)
p75_distance <- quantile(daily_activity$TotalDistance, probs =0.75)
p75_very_active_min <- quantile (daily_activity$VeryActiveMinutes, probs = 0.75)

p25_step <- quantile(daily_activity$TotalSteps, probs =0.25)
p25_distance <- quantile(daily_activity$TotalDistance, probs =0.25)
p25_very_active_min <- quantile (daily_activity$VeryActiveMinutes, probs = 0.25)
  1. Users were then assigned to one of three activity groups based on the following criteria:
  • High Active: Users whose average steps, average distance, and average very active minutes were all at or above the 75th percentile for their respective metrics.

  • Moderate Active: Users whose average steps, average distance, and average very active minutes were all between the 25th and 75th percentiles for their respective metrics.

  • Low Active: Users whose average steps, average distance, and average very active minutes were all at or below the 25th percentile for their respective metrics.

# categorize customer in 3 groups

customer_category<- daily_activity %>%
  group_by(Id) %>%
  summarise(avg_step = mean(TotalSteps),
            avg_distance = mean(TotalDistance),
            avg_very_active_min = mean(VeryActiveMinutes)) %>%
  mutate(CustomerType = case_when(
    avg_step >= p75_step &
      avg_distance >= p75_distance &
      avg_very_active_min >= p75_very_active_min ~ 'High Active',
    avg_step <= p25_step &
      avg_distance <= p25_distance &
      avg_very_active_min <= p25_very_active_min ~ 'Low Active',
    TRUE ~ 'Moderate Active'
  ))

user_avg_info <- merge(user_avg_info, customer_category, by = "Id")
user_avg_info_new <- user_avg_info %>% 
  select(Id, avg_step.x, avg_distance.x, avg_very_active_min.x, 'CustomerType' )


# count users by types

user_avg_info_new %>%
  group_by(CustomerType) %>%
  count()
## # A tibble: 2 × 2
## # Groups:   CustomerType [2]
##   CustomerType        n
##   <chr>           <int>
## 1 High Active         4
## 2 Moderate Active    29

The results of the segmentation process showed 4 users classified as High Active and 29 as Moderate Active. No users were classified as Low Active according to the defined criteria.

-Create new column and merge data

The ID and Activity Date columns were combined to create a new, unique identifier column called ‘NewId’. Additionally, the ‘daily_activity’ and ‘daily_sleep’ tables were joined for further analysis.

# create new column

daily_activity_new <- daily_activity %>% 
  unite('NewId', Id, ActivityDate, sep=' ')
head(daily_activity_new)
## # A tibble: 6 × 14
##   NewId          TotalSteps TotalDistance TrackerDistance LoggedActivitiesDist…¹
##   <chr>               <dbl>         <dbl>           <dbl>                  <dbl>
## 1 1503960366 20…      13162          8.5             8.5                       0
## 2 1503960366 20…      10735          6.97            6.97                      0
## 3 1503960366 20…      10460          6.74            6.74                      0
## 4 1503960366 20…       9762          6.28            6.28                      0
## 5 1503960366 20…      12669          8.16            8.16                      0
## 6 1503960366 20…       9705          6.48            6.48                      0
## # ℹ abbreviated name: ¹​LoggedActivitiesDistance
## # ℹ 9 more variables: VeryActiveDistance <dbl>, ModeratelyActiveDistance <dbl>,
## #   LightActiveDistance <dbl>, SedentaryActiveDistance <dbl>,
## #   VeryActiveMinutes <dbl>, FairlyActiveMinutes <dbl>,
## #   LightlyActiveMinutes <dbl>, SedentaryMinutes <dbl>, Calories <dbl>
daily_sleep_new <- daily_sleep %>% 
  unite('NewId', Id, SleepDay, sep=' ')
head(daily_sleep_new)
## # A tibble: 6 × 4
##   NewId                 TotalSleepRecords TotalMinutesAsleep TotalTimeInBed
##   <chr>                             <dbl>              <dbl>          <dbl>
## 1 1503960366 2016-04-12                 1                327            346
## 2 1503960366 2016-04-13                 2                384            407
## 3 1503960366 2016-04-15                 1                412            442
## 4 1503960366 2016-04-16                 2                340            367
## 5 1503960366 2016-04-17                 1                700            712
## 6 1503960366 2016-04-19                 1                304            320
# merge daily_active table with daily_sleep table

merge_act_sleep <- merge(daily_activity_new, daily_sleep_new, by = 'NewId', all=TRUE )
head(merge_act_sleep)
##                   NewId TotalSteps TotalDistance TrackerDistance
## 1 1503960366 2016-04-12      13162          8.50            8.50
## 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
##   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
##   TotalSleepRecords TotalMinutesAsleep TotalTimeInBed
## 1                 1                327            346
## 2                 2                384            407
## 3                NA                 NA             NA
## 4                 1                412            442
## 5                 2                340            367
## 6                 1                700            712
n_distinct(merge_act_sleep$NewId)
## [1] 940

Analyze Data

-First Glance

A preliminary look at the user groups identified earlier reveals some interesting trends. The data suggests that High Active users burn more calories, but tend to sleep less than Moderate Active users.

merge_cal_act_type <- merge(user_avg_info_new, daily_calories, by='Id') %>% 
  group_by(CustomerType) %>% summarise(AvgCal= mean(Calories))
print(merge_cal_act_type)
## # A tibble: 2 × 2
##   CustomerType    AvgCal
##   <chr>            <dbl>
## 1 High Active      2673.
## 2 Moderate Active  2247.
merge_sleep_act_type <- merge(user_avg_info_new, daily_sleep, by='Id') %>% 
  group_by(CustomerType) %>% summarise(AvgSleepHr= mean((TotalMinutesAsleep/60)))
print(merge_sleep_act_type)
## # A tibble: 2 × 2
##   CustomerType    AvgSleepHr
##   <chr>                <dbl>
## 1 High Active           5.89
## 2 Moderate Active       7.07

-Hypotheses

Rather than immediately analyzing the data, I prefer to first develop several hypotheses and then use the data to test their validity.

  1. Is there a relationship between sleep duration and the number of hours spent sitting?
  2. Is sleep latency (time in bed minus time asleep) correlated with sitting duration?
  3. Is there a relationship between calories burned and the duration of physical activity?
  4. What is the relationship between calories burned, steps taken, and distance walked?
  5. How do daily step counts vary across the days of the week?

-Verify Hypotheses

Hypotheses 1: Is there a relationship between sleep duration and the number of hours spent sitting?

Conclusion: The results suggest a negative correlation between sitting time and sleep duration; as sitting time increases, sleep time tends to decrease.

plot_sleep_vs_sedebtary <-
  ggplot(data = drop_na(merge_act_sleep), aes(x=SedentaryMinutes/60, y=TotalMinutesAsleep/60))+
  geom_point(color="#C4961A")+
  geom_smooth(size=0.5, color="#D16103")+
  labs(title = ' Asleep Hour by Sedentary Hour ', x= 'Sendentary Hour', y='Asleep Hour')+
  theme(text = element_text(family='Calibri'))
plot(plot_sleep_vs_sedebtary)
## `geom_smooth()` using method = 'loess' and formula = 'y ~ x'

Hypotheses 2: Is sleep latency (time in bed minus time asleep) correlated with sitting duration?

Conclusion: No correlation was found between sleep latency (time to fall asleep) and sitting time.

plot_sleeplatency <-
  ggplot(data = drop_na(merge_act_sleep),
         aes(x=SedentaryMinutes/60, y= (TotalTimeInBed - TotalMinutesAsleep)/60))+
  geom_point(color="#C4961A")+
  geom_smooth(size=0.5, color="#D16103")+
  labs(title = 'Sleep Latency Hour by Sedentary Hour', x= 'Sedentary Hour', y = 'Sleep Latency Hour')+
  theme(text = element_text(family = 'Calibri'))
print(plot_sleeplatency)
## `geom_smooth()` using method = 'loess' and formula = 'y ~ x'

Hypotheses 3: Is there a relationship between calories burned and the duration of physical activity?

Conclusion: The data shows a positive correlation between very active hours and calories burned. However, there’s little to no correlation between calories burned and either fairly active or lightly active hours.

plot_cal_va <-
  ggplot(data = drop_na(merge_act_sleep), aes(x=Calories, y=VeryActiveMinutes/60))+
  geom_point(color="#52854C")+
  geom_smooth(size=0.5, color="#D16103")+
  labs(title = 'Very Active Hour by Calories', x= 'Calories', y= 'Active Hour')+
  theme(text = element_text(family = 'Calibri', size = 8))

plot_cal_fa <-
  ggplot(data = drop_na(merge_act_sleep), aes(x=Calories, y=FairlyActiveMinutes/60))+
  geom_point(color="#009E73")+
  geom_smooth(size=0.5, color="#D16103")+
  labs(title = 'Fairly Active Hour by Calories', x= 'Calories', y='Active Hour' )+
  theme(text = element_text(family = 'Calibri', size = 8))


plot_cal_la <-
  ggplot(data = drop_na(merge_act_sleep), aes(x=Calories, y=LightlyActiveMinutes/60))+
  geom_point(color="#C3D7A4")+
  geom_smooth(size=0.5, color="#D16103")+
  labs(title = 'Lightly Active Hour by Calories', x= 'Calories', y ='Active Hour')+
  theme(text = element_text(family = 'Calibri', size=8))


combined_plots_cal_act_type <-grid.arrange(plot_cal_va, plot_cal_fa, plot_cal_la, nrow=1, ncol=3)
## `geom_smooth()` using method = 'loess' and formula = 'y ~ x'
## `geom_smooth()` using method = 'loess' and formula = 'y ~ x'
## `geom_smooth()` using method = 'loess' and formula = 'y ~ x'

Hypotheses 4: What is the relationship between calories burned, steps taken, and distance walked?

Conclusion: Steps taken, calories burned, and distance traveled are all positively correlated.

plot_cal_step <-
  ggplot((data=merge_act_sleep), aes(x=Calories, y=TotalSteps, color=TotalDistance)) +
  geom_point()+
  geom_smooth(size=0.5, color="#D16103")+
  scale_color_gradient(low="#C3D7A4", high = "darkblue" )+
  labs(title = 'Step Count by Calories ', x= 'Calories', y= 'Step Count', color= 'Distance')+
  theme(text = element_text(family = 'Calibri'))

print(plot_cal_step)
## `geom_smooth()` using method = 'loess' and formula = 'y ~ x'

Hypotheses 5: How do daily step counts vary across the days of the week?

Conclusion: Step counts tend to be lowest on Sundays.

custom_order <- c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
merge_act_sleep_new <- merge_act_sleep %>%
  separate(NewId, c("Id", "Date"), sep = " ")
merge_act_sleep_new$Date <- as.Date(merge_act_sleep_new$Date , format = "%Y-%m-%d")
merge_act_sleep_new$DayOfWeek <- weekdays(merge_act_sleep_new$Date) 
merge_act_sleep_new <- merge_act_sleep_new %>% 
  drop_na() %>% 
  mutate(DayOfWeek = reorder(DayOfWeek, match(DayOfWeek, custom_order))) %>%
  group_by(DayOfWeek) %>%
  group_by(DayOfWeek) %>% 
  summarise(StepDow=sum(TotalSteps))


plot_step_dow<-
  ggplot(data=merge_act_sleep_new, aes(x = DayOfWeek, y = StepDow)) +
  geom_bar(stat = "identity", fill = "#4E84C4")+
  labs(title = "Total Steps by Day of Week", x = "Day of Week", y = "Total Steps")+
  scale_y_continuous(labels = scales::comma)+
  annotate('text', x='Sunday', y=480000, label='people tend \nto walk \nless on sunday', size=2.5, color="#D16103", fontface='bold', angle=15)+
  theme(text = element_text(family = 'Calibri'))

print(plot_step_dow)

All plots have been saved for future reference.

plots <- list(
  plot_sleeplatency,
  plot_sleep_vs_sedebtary,
  combined_plots_cal_act_type,
  plot_cal_step,
  plot_step_dow
)

plot_names <- c("plot_sleeplatency", "plot_sleep_vs_sedebtary", "combined_plots_cal_act_type", "plot_cal_step", "plot_step_dow")

for (i in seq_along(plots)) {
  ggsave(paste0(plot_names[i], '.png'), plot =plots[[i]], width = 6, height=4, dpi=300)
}

Share and Act

-The following marketing strategy recommendations are based on the five preceding observations and insights:

  • Insight 1: Increased sitting time is associated with decreased sleep duration.

Suggestion: The Bellabeat app could introduce a “sitting time threshold” feature. Upon reaching the threshold, the app would prompt users to engage in physical activity to improve sleep quality.

  • Insight 2: Very active hours are strongly correlated with calories burned.

  • Insight 3: Steps taken, calories burned, and walking distance are closely correlated.

Suggestion: For weight loss goals, users should focus on increasing high-intensity activity. The Bellabeat app could incorporate features like setting exercise goals based on calories burned during high-intensity activity or distance walked.

  • Insight 4: Step counts tend to be lower on Sundays and Mondays.

Suggestion: The Bellabeat app could send push notifications on Sundays and Mondays, reminding users of their lower activity levels compared to other days and motivating them to be more active.

-Limitations

  • Because the Fitbit data lacks gender information, its applicability to Bellabeat’s primarily female user base is potentially biased. The absence of age data further necessitates careful consideration when interpreting the analysis.

  • The sample size is only minimally sufficient, meeting the absolute minimum of 30 participants.

  • Collected in 2016, the Fitbit data is outdated. Therefore, any conclusions drawn from it should be considered preliminary and used primarily for background information.

Thank you for reviewing this analysis. Please don’t hesitate to contact me if you have any questions :)