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.
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 |
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.
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)
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)
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")
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
We will now use several different function to clean, merge, and manipulate the 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.
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
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"))
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
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))
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
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
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
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
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'
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
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")
The following recommendations are made:
More analysis of more current data is recommended, as this older data and the smaller data size is likely insufficient.
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.
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:
Search for data that is focused on the target demographic, women.
A much larger sample size would be ideal for a more accurate analysis.
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.
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.