FitBit Tracker Data - bellabeat Analysis

1 Introduction

Urška Sršen and Sando Mur founded Bellabeat, a high-tech company that manufactures health-focused smart products. Sršen used her background as an artist to develop beautifully designed technology that informs and inspires women around the world. Collecting data on activity, sleep, stress, and reproductive health has allowed Bellabeat to empower women with knowledge about their own health and habits. Since it was founded in 2013, Bellabeat has grown rapidly and quickly positioned itself as a tech-driven wellness company for women.

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

For this project we will be using the FitBit Fitness Tracker Data which is hosted on Kaggle as a public domain and a usability score of 8.75. The data was collected on a survey via Amazon Mechanical Turk from 3/12/2016 - 5/12/2016.

In this project we will be using the following tables:

Table Description
Activity Daily activity of the users - in terms of number of steps walked, distance covered, intensity, and number of calories burned
Weight Log Daily track of weight by day (both in kilograms and pounds), and BMI (data available for 8 users only, out of whom 5 have reported their weight manually)


3.1 Dataset Concerns

The limited sample size of approximately thirty users, coupled with the absence of demographic information, raises concerns about potential sampling bias, as it is unclear if this sample accurately represents the broader population. Additionally, the dataset is outdated, and the survey was conducted over a brief period of just two months, further impacting the representativeness and validity of the findings.

4 Process

For this project, we will be using RStudio for our analysis because it is user-friendly, easy to read, accessible, and allows us to create visualizations all within a single program.



4.1 R Analysis

4.1.1 Install Packages

We will begin by installing packages in R that we may use during the analysis.

install.packages("tools")
install.packages("tidyverse")
install.packages("here")
install.packages("skimr")
install.packages("janitor")
install.packages("lubridate")
install.packages("ggplot2")
install.packages("dbplyr")
install.packages("dplyr")
install.packages("dtplyr")
install.packages("readr")

library(tools)
library(tidyverse)
library(here)
library(skimr)
library(janitor)
library(ggplot2)
library(dbplyr)
library(dtplyr)
library(readr)
library(magrittr)
library(dplyr)
library(lubridate)

4.1.2 Importing Tables

We will now import and rename our data tables.

daily_activity <- read.csv("dailyActivity_merged.csv")

weight_log <- read.csv("weightLogInfo_merged.csv")

4.1.3 Previewing Tables

To get an idea of the data we have to work with we will preview the tables.

head(daily_activity)
##           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
head(weight_log)
##           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

4.1.4 Data Cleaning

To make the data easier to analyze and merge we will format all dates to be the same format.

daily_activity <- daily_activity %>% 
  rename(date = ActivityDate) %>% 
  mutate(date = as_date(date,format="%m/%d/%Y"))
head(daily_activity)
##           Id       date 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
weight_log <- weight_log %>% 
  rename(date = Date) %>% 
  mutate(date = as_date(date,format="%m/%d/%Y %I:%M:%S %p",tz=Sys.timezone()))
## Warning: There was 1 warning in `mutate()`.
## ℹ In argument: `date = as_date(date, format = "%m/%d/%Y %I:%M:%S %p", tz =
##   Sys.timezone())`.
## Caused by warning:
## ! `tz` argument is ignored by `as_date()`
head(weight_log)
##           Id       date WeightKg WeightPounds Fat   BMI IsManualReport
## 1 1503960366 2016-05-02     52.6     115.9631  22 22.65           True
## 2 1503960366 2016-05-03     52.6     115.9631  NA 22.65           True
## 3 1927972279 2016-04-13    133.5     294.3171  NA 47.54          False
## 4 2873212765 2016-04-21     56.7     125.0021  NA 21.45           True
## 5 2873212765 2016-05-12     57.3     126.3249  NA 21.69           True
## 6 4319703577 2016-04-17     72.4     159.6147  25 27.45           True
##          LogId
## 1 1.462234e+12
## 2 1.462320e+12
## 3 1.460510e+12
## 4 1.461283e+12
## 5 1.463098e+12
## 6 1.460938e+12

You will notice in the weight_log table that the time is removed, because we do not need this for the analysis

4.1.5 Merging Data

Next, we will merge the tables using “Id” and “date” as primary keys.

daily_activity_weight <- merge(daily_activity, weight_log, by=c("Id","date"))
head(daily_activity_weight)
##           Id       date TotalSteps TotalDistance TrackerDistance
## 1 1503960366 2016-05-02      14727          9.71            9.71
## 2 1503960366 2016-05-03      15103          9.66            9.66
## 3 1927972279 2016-04-13        356          0.25            0.25
## 4 2873212765 2016-04-21       8859          5.98            5.98
## 5 2873212765 2016-05-12       7566          5.11            5.11
## 6 4319703577 2016-04-17         29          0.02            0.02
##   LoggedActivitiesDistance VeryActiveDistance ModeratelyActiveDistance
## 1                        0               3.21                     0.57
## 2                        0               3.73                     1.05
## 3                        0               0.00                     0.00
## 4                        0               0.13                     0.37
## 5                        0               0.00                     0.00
## 6                        0               0.00                     0.00
##   LightActiveDistance SedentaryActiveDistance VeryActiveMinutes
## 1                5.92                    0.00                41
## 2                4.88                    0.00                50
## 3                0.25                    0.00                 0
## 4                5.47                    0.01                 2
## 5                5.11                    0.00                 0
## 6                0.02                    0.00                 0
##   FairlyActiveMinutes LightlyActiveMinutes SedentaryMinutes Calories WeightKg
## 1                  15                  277              798     2004     52.6
## 2                  24                  254              816     1990     52.6
## 3                   0                   32              986     2151    133.5
## 4                  10                  371             1057     1970     56.7
## 5                   0                  268              720     1431     57.3
## 6                   0                    3             1363     1464     72.4
##   WeightPounds Fat   BMI IsManualReport        LogId
## 1     115.9631  22 22.65           True 1.462234e+12
## 2     115.9631  NA 22.65           True 1.462320e+12
## 3     294.3171  NA 47.54          False 1.460510e+12
## 4     125.0021  NA 21.45           True 1.461283e+12
## 5     126.3249  NA 21.69           True 1.463098e+12
## 6     159.6147  25 27.45           True 1.460938e+12

Now we have a single table to analyze data from.

5 Analyze

5.1 Visualization

Using this data frame, we will first calculate the average number of calories burned, average number of steps walked, and average time sedentary (in minutes) by each user per day.

average_activity_weight <- daily_activity_weight %>% 
  group_by(Id) %>% 
  summarise(mean_daily_cal=mean(Calories),mean_daily_sedentary=mean(SedentaryMinutes),mean_daily_steps=mean(TotalSteps),mean_daily_weight=mean(WeightPounds))
head(average_activity_weight)
## # A tibble: 6 × 5
##        Id mean_daily_cal mean_daily_sedentary mean_daily_steps mean_daily_weight
##     <dbl>          <dbl>                <dbl>            <dbl>             <dbl>
## 1  1.50e9          1997                  807            14915               116.
## 2  1.93e9          2151                  986              356               294.
## 3  2.87e9          1700.                 888.            8212.              126.
## 4  4.32e9          1873                 1234.            5229               160.
## 5  4.56e9          2064.                1063.            7961               154.
## 6  5.58e9          4552                  525            12231               200.

We can then create three categories of users:

Sedentary Calculation User
>= 1000.0000 Casual
>= 700.0000 Active
>= 500.0000 Highly Active
user_type <- average_activity_weight %>% 
  mutate(user_type = case_when(
    mean_daily_sedentary >= 1000.0000 ~ "casual",
    mean_daily_sedentary >= 700.0000 ~ "active",
    mean_daily_sedentary >= 500.0000 ~ "highly active"
  ))
head(user_type)
## # A tibble: 6 × 6
##        Id mean_daily_cal mean_daily_sedentary mean_daily_steps mean_daily_weight
##     <dbl>          <dbl>                <dbl>            <dbl>             <dbl>
## 1  1.50e9          1997                  807            14915               116.
## 2  1.93e9          2151                  986              356               294.
## 3  2.87e9          1700.                 888.            8212.              126.
## 4  4.32e9          1873                 1234.            5229               160.
## 5  4.56e9          2064.                1063.            7961               154.
## 6  5.58e9          4552                  525            12231               200.
## # ℹ 1 more variable: user_type <chr>

We can then visualize this in percentages:

user_type_percentage <- user_type %>% 
  group_by(user_type) %>% 
  summarise(total=n()) %>% 
  mutate(totals = sum(total)) %>% 
  group_by(user_type) %>% 
  summarise(total_percentage = total/totals) %>% 
  mutate(labels=scales::percent(total_percentage))
head(user_type_percentage)
## # A tibble: 3 × 3
##   user_type     total_percentage labels
##   <chr>                    <dbl> <chr> 
## 1 active                   0.375 38%   
## 2 casual                   0.375 38%   
## 3 highly active            0.25  25%

To show this in a more appealing way we will create a pie chart.

user_type_percentage %>% 
  ggplot(aes(x="",y=total_percentage,fill=user_type))+
  geom_bar(stat="identity",width=1)+
  coord_polar("y",start=0)+
  theme_minimal()+
  theme(axis.title.x=element_blank(),
        axis.title.y=element_blank(),
        panel.grid=element_blank(), 
        axis.text.x=element_blank(),
        plot.title=element_text(hjust=0.5,size=14,face="bold"))+
  scale_fill_manual(values=c("#79DDA1","#FDDE62","#D64829")) +
  geom_text(aes(label=labels),
            position=position_stack(vjust=0.5))+
  labs(title="User Type Distribution as per mean_daily_sedentary")

5.2 Visualization

For this analysis we will look at the casual users and what day of the week they are the most active.

First, we want to identify which users are “Casual”

casual_users <- user_type %>% 
  filter(user_type =="casual")
head(casual_users)
## # A tibble: 3 × 6
##        Id mean_daily_cal mean_daily_sedentary mean_daily_steps mean_daily_weight
##     <dbl>          <dbl>                <dbl>            <dbl>             <dbl>
## 1  4.32e9          1873                 1234.            5229               160.
## 2  4.56e9          2064.                1063.            7961               154.
## 3  8.88e9          3454.                1105.           17022.              188.
## # ℹ 1 more variable: user_type <chr>

We have identified users: 4319703577, 4558609924, and 8877689391

Now we will create a table filtered to casual users to analyze their data.

casual_users_daily <- daily_activity_weight %>% 
  filter(Id %in% c("4319703577", "4558609924", "8877689391")) %>% 
  group_by(Id)
head(casual_users_daily)
## # A tibble: 6 × 21
## # Groups:   Id [2]
##           Id date       TotalSteps TotalDistance TrackerDistance
##        <dbl> <date>          <int>         <dbl>           <dbl>
## 1 4319703577 2016-04-17         29        0.0200          0.0200
## 2 4319703577 2016-05-04      10429        7.02            7.02  
## 3 4558609924 2016-04-18       8940        5.91            5.91  
## 4 4558609924 2016-04-25       8095        5.35            5.35  
## 5 4558609924 2016-05-01       3428        2.27            2.27  
## 6 4558609924 2016-05-02       7891        5.22            5.22  
## # ℹ 16 more variables: LoggedActivitiesDistance <dbl>,
## #   VeryActiveDistance <dbl>, ModeratelyActiveDistance <dbl>,
## #   LightActiveDistance <dbl>, SedentaryActiveDistance <dbl>,
## #   VeryActiveMinutes <int>, FairlyActiveMinutes <int>,
## #   LightlyActiveMinutes <int>, SedentaryMinutes <int>, Calories <int>,
## #   WeightKg <dbl>, WeightPounds <dbl>, Fat <int>, BMI <dbl>,
## #   IsManualReport <chr>, LogId <dbl>

Now we will summarize the days of the week and how long (minutes) the casual users were sedentary.

weekdays_casual_users <- casual_users_daily %>% 
  mutate(day_of_week=weekdays(date))

weekdays_casual_users$day_of_week <- ordered(weekdays_casual_users$day_of_week,
levels=c("Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"))

weekdays_casual_users <- weekdays_casual_users %>% 
  group_by(day_of_week) %>% 
  summarize(daily_sedentary=mean(SedentaryMinutes))
head(weekdays_casual_users)
## # A tibble: 6 × 2
##   day_of_week daily_sedentary
##   <ord>                 <dbl>
## 1 Monday                1092.
## 2 Tuesday               1128.
## 3 Wednesday             1119.
## 4 Thursday              1023.
## 5 Friday                1151 
## 6 Saturday              1003

Now we will create a bar chart to show stakeholders.

ggplot(weekdays_casual_users, aes(x=day_of_week, y=daily_sedentary)) +
  geom_bar(stat = "identity", fill="#6D9D58") +
  labs(title = "Casual Users", x="", y="Sedentary Minutes") +
  theme(axis.text.x=element_text(angle=45, vjust=0.5, hjust=1))

5.3 Visualization

Let’s do the same for active users.

  active_users <- user_type %>% 
  filter(user_type =="active")
head(active_users)
## # A tibble: 3 × 6
##        Id mean_daily_cal mean_daily_sedentary mean_daily_steps mean_daily_weight
##     <dbl>          <dbl>                <dbl>            <dbl>             <dbl>
## 1  1.50e9          1997                  807            14915               116.
## 2  1.93e9          2151                  986              356               294.
## 3  2.87e9          1700.                 888.            8212.              126.
## # ℹ 1 more variable: user_type <chr>

We have identified users: 1503960366, 1927972279, and 2873212765

Now we will create a table filtered to casual users to analyze their data.

active_users_daily <- daily_activity_weight %>% 
  filter(Id %in% c("1503960366", "1927972279", "2873212765"))
head(active_users_daily)
##           Id       date TotalSteps TotalDistance TrackerDistance
## 1 1503960366 2016-05-02      14727          9.71            9.71
## 2 1503960366 2016-05-03      15103          9.66            9.66
## 3 1927972279 2016-04-13        356          0.25            0.25
## 4 2873212765 2016-04-21       8859          5.98            5.98
## 5 2873212765 2016-05-12       7566          5.11            5.11
##   LoggedActivitiesDistance VeryActiveDistance ModeratelyActiveDistance
## 1                        0               3.21                     0.57
## 2                        0               3.73                     1.05
## 3                        0               0.00                     0.00
## 4                        0               0.13                     0.37
## 5                        0               0.00                     0.00
##   LightActiveDistance SedentaryActiveDistance VeryActiveMinutes
## 1                5.92                    0.00                41
## 2                4.88                    0.00                50
## 3                0.25                    0.00                 0
## 4                5.47                    0.01                 2
## 5                5.11                    0.00                 0
##   FairlyActiveMinutes LightlyActiveMinutes SedentaryMinutes Calories WeightKg
## 1                  15                  277              798     2004     52.6
## 2                  24                  254              816     1990     52.6
## 3                   0                   32              986     2151    133.5
## 4                  10                  371             1057     1970     56.7
## 5                   0                  268              720     1431     57.3
##   WeightPounds Fat   BMI IsManualReport        LogId
## 1     115.9631  22 22.65           True 1.462234e+12
## 2     115.9631  NA 22.65           True 1.462320e+12
## 3     294.3171  NA 47.54          False 1.460510e+12
## 4     125.0021  NA 21.45           True 1.461283e+12
## 5     126.3249  NA 21.69           True 1.463098e+12

Now we will summarize the days of the week and how long (minutes) the casual users were sedentary.

weekdays_active_users <- active_users_daily %>% 
  mutate(day_of_week=weekdays(date))

weekdays_active_users$day_of_week <- ordered(weekdays_active_users$day_of_week,
levels=c("Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"))

weekdays_active_users <- weekdays_active_users %>% 
  group_by(day_of_week) %>% 
  summarize(daily_sedentary=mean(SedentaryMinutes))
head(weekdays_active_users)
## # A tibble: 4 × 2
##   day_of_week daily_sedentary
##   <ord>                 <dbl>
## 1 Monday                 798 
## 2 Tuesday                816 
## 3 Wednesday              986 
## 4 Thursday               888.

Next, a simple bar chart.

ggplot(weekdays_active_users, aes(x=day_of_week, y=daily_sedentary)) +
  geom_bar(stat = "identity", fill="#2E9999") +
  labs(title = "Active Users", x="", y="Sedentary Minutes") +
  theme(axis.text.x=element_text(angle=45, vjust=0.5, hjust=1))

5.4 Takeaway Points

Based on the analysis of these graphs, it is evident that casual users exhibit higher activity levels on Thursdays and Saturdays, whereas active users demonstrate increased activity on Mondays and Tuesdays. These insights can be leveraged to formulate targeted strategies for enhancing user engagement.

6 Act

6.1 Recommendations

By implementing the following recommendations, bellabeat can effectively address the unique needs of both active and casual users:

  1. For casual users, providing reminders through the smart device throughout the day can help increase activity levels and reduce sedentary minutes. These reminders are less critical on Thursdays and Saturdays due to naturally higher activity levels on these days.
  2. For active users, deploying the same reminder technique on Wednesdays and Thursdays can help maintain and enhance their activity levels.
  3. Continuously collecting data on users’ sedentary times will enable bellabeat to better identify optimal days for targeted notifications, thereby improving overall user engagement and activity.

6.2 Data Issues and Recommendations

While this dataset contains valuable information, it has several limitations:

  1. Limited Data Collection Period: The data collection was conducted over a few months, resulting in a small dataset. A longer study with a more diverse group of participants is recommended to enhance the robustness of the findings.
  2. Incomplete Weekly Data: Some days of the week lacked data, possibly due to the smart device not logging information or users not wearing the device consistently. To ensure accurate data collection, it is advisable to select a test group committed to wearing the device daily.
  3. Outdated Information: The dataset is outdated. Collecting more recent data, specifically from 2020 onwards, would provide a more accurate representation of current trends and user behaviors. By addressing these issues, future studies can yield more comprehensive and reliable insights.

7 Acknowledgement

This capstone project was inspired by a study conducted by a Kaggle user, who analyzed the same dataset in their work titled Capstone Project: Case Study on Bellabeat. Their study served as a foundational reference and provided valuable inspiration for the analytical methods and code employed in this project.