1 Introduction

Urška Sršen and Sando Mur created Bellabeat, a small high-tech fitness and health-focused products for women. Sršen holds the belief that analyzing other smart tech company data could provide insights to help drive and further grow Bellabeat. In this project, we have been asked to analyze a given data set from another smart fitness device manufacturer, and attempt to discover any kinds of insights that could help Bellabeat focus on necessary improvements to their products.

2 Ask

  1. What are some trends in smart device usage?
  2. How could these trends apply to Bellabeat customers?
  3. How could these trends help influence Bellabeat marketing strategy?

3 Prepare

As stated in the introduction, we will be using FitnessBit Fitness Tracker Data hosted on Kaggle. The data was collected on a survey via Amazon Mechanical Turk from 3/12/2016 - 5/12/2016. There are some concerns with the dataset: 1. The dataset is limited to just 30 users, and is a generally small sample size. 2. The dataset does not indicate whether these are male or female users, Bellabeat is focused on products for women. 3. The dataset is from several years ago, and this data very likely is not current given the advances made in the past several years.

The following data tables were analyzed for this project

Table Description
Daily Activity The daily activity summary of users
Daily Steps The daily steps taken by users
Daily Calories The daily calories burned by users
Intensities The intensity of the activity while calories were being burned
Sleep The sleep data of each user
Hourly Steps The hourly step data of each user
Hourly Calories The hourly calorie data of each user

4 Process

For this project, we will be utilizing RStudio to analyze the mentioned data tables, as it allows us to both analyze and create data visualizations in one place.


4.1 Analysis

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)

4.1.1 Install Packages

First, we will install and load all of the necessary packages for this analysis.

install.packages("tidyverse")
install.packages("ggplot2")
install.packages("tidyr")
install.packages("dplyr")
install.packages("lubridate")

library(lubridate)
library(tidyr)
library(tidyverse)
library(ggplot2)
library(dplyr)

4.1.2 Importing Data

Important and renaming all data tables used

daily_activity <- read_csv("dailyActivity_merged.csv")
daily_calories <- read_csv("dailyCalories_merged.csv")
daily_steps <- read_csv("dailySteps_merged.csv")
intensities <- read_csv("dailyIntensities_merged.csv")
sleep <- read_csv("sleepDay_merged.csv")
hourly_steps <- read_csv("hourlySteps_merged.csv")
hourly_calories <- read_csv("hourlyCalories_merged.csv")

4.1.3 Preview Data

We will take a look at the data using the head function

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>
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
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
head(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
head(hourly_calories)
## # A tibble: 6 × 3
##           Id ActivityHour          Calories
##        <dbl> <chr>                    <dbl>
## 1 1503960366 4/12/2016 12:00:00 AM       81
## 2 1503960366 4/12/2016 1:00:00 AM        61
## 3 1503960366 4/12/2016 2:00:00 AM        59
## 4 1503960366 4/12/2016 3:00:00 AM        47
## 5 1503960366 4/12/2016 4:00:00 AM        48
## 6 1503960366 4/12/2016 5:00:00 AM        48
head(hourly_steps)
## # A tibble: 6 × 3
##           Id ActivityHour          StepTotal
##        <dbl> <chr>                     <dbl>
## 1 1503960366 4/12/2016 12:00:00 AM       373
## 2 1503960366 4/12/2016 1:00:00 AM        160
## 3 1503960366 4/12/2016 2:00:00 AM        151
## 4 1503960366 4/12/2016 3:00:00 AM          0
## 5 1503960366 4/12/2016 4:00:00 AM          0
## 6 1503960366 4/12/2016 5:00:00 AM          0

4.1.4 Cleaning and Manipulating Data

We will now use several different function to clean, merge, and manipulate the data

4.1.4.a Distinct Data

We use n_distinct to count the number of distinct use id’s from each table

n_distinct(daily_activity$Id)
## [1] 33
n_distinct(daily_calories$Id)
## [1] 33
n_distinct(daily_steps$Id)
## [1] 33
n_distinct(sleep$Id)
## [1] 24
n_distinct(hourly_calories$Id)
## [1] 33
n_distinct(hourly_steps$Id)
## [1] 33
n_distinct(intensities$Id)
## [1] 33

We can see that all tables, excluding sleep, contain 33 unique user IDs.

4.1.4.b Removing Duplicates

We will now remove any duplicates from the data tables, and check sums to verify the removal

daily_activity = daily_activity %>% distinct() %>% drop_na()
daily_calories = daily_calories %>% distinct() %>% drop_na()
daily_steps = daily_steps %>% distinct() %>% drop_na()
sleep = sleep %>% distinct() %>% drop_na()
hourly_calories = hourly_calories %>% distinct() %>% drop_na()
hourly_steps = hourly_steps %>% distinct() %>% drop_na()
intensities = intensities %>% distinct() %>% drop_na()

sum(duplicated(daily_activity))
## [1] 0
sum(duplicated(daily_calories))
## [1] 0
sum(duplicated(daily_steps))
## [1] 0
sum(duplicated(sleep))
## [1] 0
sum(duplicated(hourly_calories))
## [1] 0
sum(duplicated(hourly_steps))
## [1] 0
sum(duplicated(intensities))
## [1] 0

4.1.4.c Formatting Date

We will now format the date and time of the data

daily_activity = daily_activity %>%
  mutate(ActivityDate = as.Date(ActivityDate, format = "%m/%d/%Y"))

daily_calories = daily_calories %>%
  mutate(ActivityDate = as.Date(ActivityDate, format = "%m/%d/%Y"))

daily_steps = daily_steps %>%
  mutate(ActivityDate = as.Date(ActivityDate, format = "%m/%d/%Y"))

intensities = intensities %>% 
  mutate(ActivityDate = as.Date(ActivityDate, format = "%m/%d/%Y"))

4.1.5 Summarizing Data for Insights

We will now summarize the data tables to hopefully gain some insights

daily_activity %>%
  select(TotalSteps,
         TotalDistance,
         VeryActiveDistance,
         ModeratelyActiveDistance,
         LightActiveDistance,
         SedentaryActiveDistance,
         VeryActiveMinutes,
         FairlyActiveMinutes,
         LightlyActiveMinutes,
         SedentaryMinutes,
         Calories) %>%
  summary()
##    TotalSteps    TotalDistance    VeryActiveDistance ModeratelyActiveDistance
##  Min.   :    0   Min.   : 0.000   Min.   : 0.000     Min.   :0.0000          
##  1st Qu.: 3790   1st Qu.: 2.620   1st Qu.: 0.000     1st Qu.:0.0000          
##  Median : 7406   Median : 5.245   Median : 0.210     Median :0.2400          
##  Mean   : 7638   Mean   : 5.490   Mean   : 1.503     Mean   :0.5675          
##  3rd Qu.:10727   3rd Qu.: 7.713   3rd Qu.: 2.053     3rd Qu.:0.8000          
##  Max.   :36019   Max.   :28.030   Max.   :21.920     Max.   :6.4800          
##  LightActiveDistance SedentaryActiveDistance VeryActiveMinutes
##  Min.   : 0.000      Min.   :0.000000        Min.   :  0.00   
##  1st Qu.: 1.945      1st Qu.:0.000000        1st Qu.:  0.00   
##  Median : 3.365      Median :0.000000        Median :  4.00   
##  Mean   : 3.341      Mean   :0.001606        Mean   : 21.16   
##  3rd Qu.: 4.782      3rd Qu.:0.000000        3rd Qu.: 32.00   
##  Max.   :10.710      Max.   :0.110000        Max.   :210.00   
##  FairlyActiveMinutes LightlyActiveMinutes SedentaryMinutes    Calories   
##  Min.   :  0.00      Min.   :  0.0        Min.   :   0.0   Min.   :   0  
##  1st Qu.:  0.00      1st Qu.:127.0        1st Qu.: 729.8   1st Qu.:1828  
##  Median :  6.00      Median :199.0        Median :1057.5   Median :2134  
##  Mean   : 13.56      Mean   :192.8        Mean   : 991.2   Mean   :2304  
##  3rd Qu.: 19.00      3rd Qu.:264.0        3rd Qu.:1229.5   3rd Qu.:2793  
##  Max.   :143.00      Max.   :518.0        Max.   :1440.0   Max.   :4900
daily_calories %>%
  select(Calories) %>%
  summary()
##     Calories   
##  Min.   :   0  
##  1st Qu.:1828  
##  Median :2134  
##  Mean   :2304  
##  3rd Qu.:2793  
##  Max.   :4900
daily_steps %>%
  select(StepTotal) %>%
  summary()
##    StepTotal    
##  Min.   :    0  
##  1st Qu.: 3790  
##  Median : 7406  
##  Mean   : 7638  
##  3rd Qu.:10727  
##  Max.   :36019
hourly_calories %>%
  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_steps %>%
  select(StepTotal) %>%
  summary()
##    StepTotal      
##  Min.   :    0.0  
##  1st Qu.:    0.0  
##  Median :   40.0  
##  Mean   :  320.2  
##  3rd Qu.:  357.0  
##  Max.   :10554.0
sleep %>%
  select(TotalTimeInBed,
         TotalMinutesAsleep) %>%
  summary()
##  TotalTimeInBed  TotalMinutesAsleep
##  Min.   : 61.0   Min.   : 58.0     
##  1st Qu.:403.8   1st Qu.:361.0     
##  Median :463.0   Median :432.5     
##  Mean   :458.5   Mean   :419.2     
##  3rd Qu.:526.0   3rd Qu.:490.0     
##  Max.   :961.0   Max.   :796.0

4.1.6 Calculating Daily and Hourly Averages

We will now calculate the daily and hourly averages of steps, calories, and distance

daily_avg = daily_activity %>%
  group_by(Id) %>%
  summarise(avg_daily_steps = mean(TotalSteps),
            avg_daily_distance = mean(TotalDistance),
            avg_daily_calories = mean(Calories))

hourly_calorie_avg = hourly_calories %>%
  group_by(Id) %>%
  summarise(avg_hourly_calories = mean(Calories))

hourly_step_avg = hourly_steps %>%
  group_by(Id) %>%
  summarise(hourly_step_avg = mean(StepTotal))

4.1.7 Categorizing Users Based on Steps

We will categorize the users based on their steps taken

user_type = daily_avg %>%
  mutate(user_type = case_when(
    avg_daily_steps < 5000 ~ "sedentary",
    avg_daily_steps >= 5000 & avg_daily_steps < 7500 ~ "lightly active",
    avg_daily_steps >= 7500 & avg_daily_steps < 10000 ~ "fairly active",
    avg_daily_steps >= 10000 & avg_daily_steps < 12500 ~ "active",
    avg_daily_steps >= 12500 ~ "highly active"))
head(user_type)
## # A tibble: 6 × 5
##           Id avg_daily_steps avg_daily_distance avg_daily_calories user_type    
##        <dbl>           <dbl>              <dbl>              <dbl> <chr>        
## 1 1503960366          12117.              7.81               1816. active       
## 2 1624580081           5744.              3.91               1483. lightly acti…
## 3 1644430081           7283.              5.30               2811. lightly acti…
## 4 1844505072           2580.              1.71               1573. sedentary    
## 5 1927972279            916.              0.635              2173. sedentary    
## 6 2022484408          11371.              8.08               2510. active
user_type %>%
  group_by(user_type) %>%
  summarise(count=n())
## # A tibble: 5 × 2
##   user_type      count
##   <chr>          <int>
## 1 active             5
## 2 fairly active      9
## 3 highly active      2
## 4 lightly active     9
## 5 sedentary          8

4.1.8 Merging Data

We will begin merging tables using the Id and ActivityDate/ActivityHour columns

merged_hourly = merge(hourly_calories, hourly_steps, by = c("Id", "ActivityHour"), all = TRUE)
head(merged_hourly)
##           Id          ActivityHour Calories StepTotal
## 1 1503960366  4/12/2016 1:00:00 AM       61       160
## 2 1503960366  4/12/2016 1:00:00 PM       66       221
## 3 1503960366 4/12/2016 10:00:00 AM       99       676
## 4 1503960366 4/12/2016 10:00:00 PM       65        89
## 5 1503960366 4/12/2016 11:00:00 AM       76       360
## 6 1503960366 4/12/2016 11:00:00 PM       81       338

5 Analyze

5.1 Visualizations

Plotting a chart to show total steps vs calories burned
ggplot(data = merged_hourly) +
  geom_point(mapping = aes(x = StepTotal, y = Calories)) +
  geom_smooth(mapping = aes(x = StepTotal, y = Calories)) +
  labs(title = "Hourly Steps vs. Hourly Calories burned",
       subtitle = "Data from 04-12-16 to 05-09-2016",
       caption = "Data gathered by Mobius on Kaggle") +
  xlab("Steps Taken") +
  ylab("Calories Burned")
## `geom_smooth()` using method = 'gam' and formula = 'y ~ s(x, bs = "cs")'

Data shows a clear correlation between steps taken and calories burned

Plotting a chart showing correlation between daily steps and daily calories burned
ggplot(data = daily_activity) +
  geom_point(mapping = aes(x = TotalSteps, y = Calories)) +
  geom_smooth(mapping = aes(x = TotalSteps, y = Calories))+
  labs(title = "Daily Steps vs. Daily Calories burned",
       subtitle = "Data from 04-12-16 to 05-09-2016",
       caption = "Data gathered by Mobius on Kaggle") +
  xlab("Steps Taken") +
  ylab("Calories Burned")
## `geom_smooth()` using method = 'loess' and formula = 'y ~ x'

Data shows a positive correlation between hourly and daily values

Plotting chart to show sedentary minutes vs calories burned
ggplot(data = daily_activity) +
  geom_point(mapping = aes(x = SedentaryMinutes, y = Calories)) +
  geom_smooth(mapping = aes(x = SedentaryMinutes, y = Calories)) +
  labs(title = "Sedentary Time vs. Daily Calories burned",
       subtitle = "Data from 04-12-16 to 05-09-2016",
       caption = "Data gathered by Mobius on Kaggle") 
## `geom_smooth()` using method = 'loess' and formula = 'y ~ x'

Plotting chart to show active minutes vs calories burned
ggplot(data = daily_activity) +
  geom_point(mapping = aes(x = VeryActiveMinutes, y = Calories)) +
  geom_smooth(mapping = aes(x = VeryActiveMinutes, y = Calories)) +
  labs(title = "Very Active Time vs. Daily Calories burned",
       subtitle = "Data from 04-12-16 to 05-09-2016",
       caption = "Data gathered by Mobius on Kaggle") 
## `geom_smooth()` using method = 'loess' and formula = 'y ~ x'

These charts show a correlation between sedentary and active minutes vs calories burned

Bar Chart showing user categorization
ggplot(data = user_type) +
  geom_bar(mapping = aes(x = user_type, fill = user_type)) +
  labs(title = "Dist. of User Activity based on Step Index",
       subtitle = "Data from 04-12-16 to 05-09-2016",
       caption = "Data gathered by Mobius on Kaggle") +
  xlab("User Type") +
  ylab("Number Of Users")

6 Share Conclusions

The data shows that there is a clear correlation between activity and calories burned. The data between sedentary minutes and calories burned appear to be inconclusive. This could possibly be due to the high number of sedentary and lightly active users in regards to the categorization created. These data insights could possibly used to better strategize methods to increase usage by users. As previously stated, the data as a whole was a rather small dataset that did not specify whether or not the data pertained to just women, our targeted demographic. The data is also quite aged, and it is very possibly that more current data with more current technology could deliver far greater insights on the problem at hand.

7 Act

The following recommendations are made:

  1. More analysis of more current data is recommended, as this older data and the smaller data size is likely insufficient.

  2. From the data that we do have, it is clear that sedentary and light activity is an issue for over half of the users data was collected from. It could be beneficial to implement a reminder function for users to help remind them to stay active and minimize sedentary minutes as the amount of calories burned during active minutes is much higher.

  3. Creation of different “profiles” based on a users categorization should be created so the devices can be more customized; everyone is unique and a one size fits all solution is not ideal.

In regards to the data limitations:

  1. Search for data that is focused on the target demographic, women.

  2. A much larger sample size would be ideal for a more accurate analysis.

  3. In the world of technology, new and updated hardware is released on almost a six month basis. As this data is from 2016, the leaps that have been made in smart fitness technology is likely to be quite great. Many of the previous recommendations made here have likely already been taken by other fitness companies.

Acknowledgement

The format of this capstone project was inspired by another users similar project Bellabeat Analysis. Their work provided the framework for this project in regards to the formatting.