This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.
Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Ctrl+Shift+Enter.
# ==============================================================================
# MATH 345: Applied Statistics - Draft 1 Data Cleaning & Exploratory Analysis
# Project Title: Analyzing Daily Physical Activity, Sleep Quality, and Duration
# Author: Elad Bakshi
# Date: October 2026
# ==============================================================================
# 1. LOAD REQUIRED LIBRARIES
# ------------------------------------------------------------------------------
# Install packages if not already installed:
# install.packages(c("tidyverse", "corrplot", "gridExtra", "knitr"))
library(tidyverse) # Includes ggplot2, dplyr, readr, tidyr
library(corrplot) # For visual correlation matrices
library(gridExtra) # For combining ggplot panel figures
library(knitr) # For clean table formatting
# 2. DATA IMPORT & INITIAL PREPROCESSING
# ------------------------------------------------------------------------------
# Load the dataset (replace filename if stored locally or use direct URL)
dataset_url <- "https://raw.githubusercontent.com/miayulia/Sleep-and-Health-Lifestyle-Analysis/main/Sleep_health_and_lifestyle_dataset.csv"
# Load data into R dataframe
sleep_data <- read_csv(dataset_url)
# Inspect dataset structure
cat("\n--- DATASET OVERVIEW ---\n")
--- DATASET OVERVIEW ---
glimpse(sleep_data)
Rows: 374
Columns: 13
$ `Person ID` <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, …
$ Gender <chr> "Male", "Male", "Male", "Male", "Male", "Ma…
$ Age <dbl> 27, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29,…
$ Occupation <chr> "Software Engineer", "Doctor", "Doctor", "S…
$ `Sleep Duration` <dbl> 6.1, 6.2, 6.2, 5.9, 5.9, 5.9, 6.3, 7.8, 7.8…
$ `Quality of Sleep` <dbl> 6, 6, 6, 4, 4, 4, 6, 7, 7, 7, 6, 7, 6, 6, 6…
$ `Physical Activity Level` <dbl> 42, 60, 60, 30, 30, 30, 40, 75, 75, 75, 30,…
$ `Stress Level` <dbl> 6, 8, 8, 8, 8, 8, 7, 6, 6, 6, 8, 6, 8, 8, 8…
$ `BMI Category` <chr> "Overweight", "Normal", "Normal", "Obese", …
$ `Blood Pressure` <chr> "126/83", "125/80", "125/80", "140/90", "14…
$ `Heart Rate` <dbl> 77, 75, 75, 85, 85, 85, 82, 70, 70, 70, 70,…
$ `Daily Steps` <dbl> 4200, 10000, 10000, 3000, 3000, 3000, 3500,…
$ `Sleep Disorder` <chr> "None", "None", "None", "Sleep Apnea", "Sle…
summary(sleep_data)
Person ID Gender Age Occupation
Min. : 1.00 Length :374 Min. :27.00 Length :374
1st Qu.: 94.25 N.unique : 2 1st Qu.:35.25 N.unique : 11
Median :187.50 N.blank : 0 Median :43.00 N.blank : 0
Mean :187.50 Min.nchar: 4 Mean :42.18 Min.nchar: 5
3rd Qu.:280.75 Max.nchar: 6 3rd Qu.:50.00 Max.nchar: 20
Max. :374.00 Max. :59.00
Sleep Duration Quality of Sleep Physical Activity Level Stress Level
Min. :5.800 Min. :4.000 Min. :30.00 Min. :3.000
1st Qu.:6.400 1st Qu.:6.000 1st Qu.:45.00 1st Qu.:4.000
Median :7.200 Median :7.000 Median :60.00 Median :5.000
Mean :7.132 Mean :7.313 Mean :59.17 Mean :5.385
3rd Qu.:7.800 3rd Qu.:8.000 3rd Qu.:75.00 3rd Qu.:7.000
Max. :8.500 Max. :9.000 Max. :90.00 Max. :8.000
BMI Category Blood Pressure Heart Rate Daily Steps
Length :374 Length :374 Min. :65.00 Min. : 3000
N.unique : 4 N.unique : 25 1st Qu.:68.00 1st Qu.: 5600
N.blank : 0 N.blank : 0 Median :70.00 Median : 7000
Min.nchar: 5 Min.nchar: 6 Mean :70.17 Mean : 6817
Max.nchar: 13 Max.nchar: 6 3rd Qu.:72.00 3rd Qu.: 8000
Max. :86.00 Max. :10000
Sleep Disorder
Length :374
N.unique : 3
N.blank : 0
Min.nchar: 4
Max.nchar: 11
# Data Cleaning: Standardize categorical labels and check for missing values
sleep_data <- sleep_data %>%
# Fix inconsistent category names in BMI
mutate(`BMI Category` = ifelse(`BMI Category` == "Normal Weight", "Normal", `BMI Category`)) %>%
# Convert categorical columns to factors
mutate(
Gender = as.factor(Gender),
`BMI Category` = factor(`BMI Category`, levels = c("Normal", "Overweight", "Obese")),
`Sleep Disorder` = as.factor(replace_na(`Sleep Disorder`, "None"))
)
# Verify missing data count
cat("\nTotal Missing Values per Column:\n")
Total Missing Values per Column:
print(colSums(is.na(sleep_data)))
Person ID Gender Age
0 0 0
Occupation Sleep Duration Quality of Sleep
0 0 0
Physical Activity Level Stress Level BMI Category
0 0 0
Blood Pressure Heart Rate Daily Steps
0 0 0
Sleep Disorder
0
# 3. NUMERICAL SUMMARY STATISTICS (TABLE 1)
# ------------------------------------------------------------------------------
# Select quantitative variables for summary calculations
num_vars <- sleep_data %>%
select(`Age`, `Sleep Duration`, `Quality of Sleep`,
`Physical Activity Level`, `Stress Level`, `Daily Steps`)
# Function to compute parametric and non-parametric summary statistics
get_summary_stats <- function(df) {
df %>%
pivot_longer(cols = everything(), names_to = "Variable", values_to = "Value") %>%
group_by(Variable) %>%
summarise(
Mean = round(mean(Value, na.rm = TRUE), 2),
SD = round(sd(Value, na.rm = TRUE), 2),
Median = round(median(Value, na.rm = TRUE), 2),
Q1 = round(quantile(Value, 0.25, na.rm = TRUE), 2),
Q3 = round(quantile(Value, 0.75, na.rm = TRUE), 2),
IQR = round(IQR(Value, na.rm = TRUE), 2),
Min = round(min(Value, na.rm = TRUE), 2),
Max = round(max(Value, na.rm = TRUE), 2)
)
}
summary_table <- get_summary_stats(num_vars)
cat("\n--- SUMMARY STATISTICS TABLE ---\n")
--- SUMMARY STATISTICS TABLE ---
print(kable(summary_table, caption = "Summary Statistics for Quantitative Variables (N = 374)"))
| Variable | Mean | SD | Median | Q1 | Q3 | IQR | Min | Max |
|---|---|---|---|---|---|---|---|---|
| Age | 42.18 | 8.67 | 43.0 | 35.25 | 50.0 | 14.75 | 27.0 | 59.0 |
| Daily Steps | 6816.84 | 1617.92 | 7000.0 | 5600.00 | 8000.0 | 2400.00 | 3000.0 | 10000.0 |
| Physical Activity Level | 59.17 | 20.83 | 60.0 | 45.00 | 75.0 | 30.00 | 30.0 | 90.0 |
| Quality of Sleep | 7.31 | 1.20 | 7.0 | 6.00 | 8.0 | 2.00 | 4.0 | 9.0 |
| Sleep Duration | 7.13 | 0.80 | 7.2 | 6.40 | 7.8 | 1.40 | 5.8 | 8.5 |
| Stress Level | 5.39 | 1.77 | 5.0 | 4.00 | 7.0 | 3.00 | 3.0 | 8.0 |
p1 <- ggplot(sleep_data, aes(x = `Physical Activity Level`)) +
geom_histogram(binwidth = 5, fill = "blue", color = "white", alpha = 0.8) +
theme_minimal() +
labs(title = "Distribution of Daily Physical Activity", x = "Activity (mins/day)", y = "Frequency")
p2 <- ggplot(sleep_data, aes(x = `Quality of Sleep`)) +
geom_histogram(binwidth = 1, fill = "blue", color = "white", alpha = 0.8) +
scale_x_continuous(breaks = 1:10) +
theme_minimal() +
labs(title = "Distribution of Sleep Quality Rating", x = "Quality Score (1-10)", y = "Frequency")
p3 <- ggplot(sleep_data, aes(x = `Sleep Duration`)) +
geom_histogram(binwidth = 0.5, fill = "blue", color = "white", alpha = 0.8) +
theme_minimal() +
labs(title = "Distribution of Sleep Duration", x = "Sleep Duration (Hours)", y = "Frequency")
# Combine univariate plots into a 1x3 panel
grid.arrange(p1, p2, p3, ncol = 3)
p_scatter1 <- ggplot(sleep_data, aes(x = `Physical Activity Level`, y = `Quality of Sleep`)) +
geom_count(color = "#1f77b4", alpha = 0.6) +
geom_smooth(method = "lm", se = TRUE, color = "#C0392B", fill = "#E74C3C", alpha = 0.15) +
scale_size_area(max_size = 6) +
theme_minimal() +
labs(
title = "Physical Activity vs. Quality of Sleep",
x = "Physical Activity (mins/day)",
y = "Quality of Sleep (1-10)",
size = "Count"
)
p_scatter2 <- ggplot(sleep_data, aes(x = `Physical Activity Level`, y = `Sleep Duration`)) +
geom_count(color = "#2ca02c", alpha = 0.6) +
geom_smooth(method = "lm", se = TRUE, color = "#C0392B", fill = "#E74C3C", alpha = 0.15) +
scale_size_area(max_size = 6) +
theme_minimal() +
labs(
title = "Physical Activity vs. Sleep Duration",
x = "Physical Activity (mins/day)",
y = "Sleep Duration (hours)",
size = "Count"
)
grid.arrange(p_scatter1, p_scatter2, ncol = 2)
Add a new chunk by clicking the *Insert Chunk* button on the toolbar or by pressing *Ctrl+Alt+I*.
When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the *Preview* button or press *Ctrl+Shift+K* to preview the HTML file).
The preview shows you a rendered HTML copy of the contents of the editor. Consequently, unlike *Knit*, *Preview* does not run any R code chunks. Instead, the output of the chunk when it was last run in the editor is displayed.