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.
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) |
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.
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.
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)
We will now import and rename our data tables.
daily_activity <- read.csv("dailyActivity_merged.csv")
weight_log <- read.csv("weightLogInfo_merged.csv")
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
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
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.
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")
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))
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))
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.
By implementing the following recommendations, bellabeat can effectively address the unique needs of both active and casual users:
While this dataset contains valuable information, it has several limitations:
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.