1 Data input

data_url <- paste0(
  "https://raw.githubusercontent.com/",
  "tmatis12/datafiles/main/normtemp.csv"
)

heart_data <- read.csv(data_url)


if (ncol(heart_data) != 3) {
  stop
}

names(heart_data) <- c("temperature", "sex", "heart_rate")

heart_data <- heart_data %>%
  mutate(
    sex_label = factor(
      sex,
      levels = c(1, 2),
      labels = c("Male", "Female")
    )
  )

str(heart_data)
## 'data.frame':    130 obs. of  4 variables:
##  $ temperature: num  96.3 96.7 96.9 97 97.1 97.1 97.1 97.2 97.3 97.4 ...
##  $ sex        : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ heart_rate : int  70 71 74 80 73 75 82 64 69 70 ...
##  $ sex_label  : Factor w/ 2 levels "Male","Female": 1 1 1 1 1 1 1 1 1 1 ...
head(heart_data)
##   temperature sex heart_rate sex_label
## 1        96.3   1         70      Male
## 2        96.7   1         71      Male
## 3        96.9   1         74      Male
## 4        97.0   1         80      Male
## 5        97.1   1         73      Male
## 6        97.1   1         75      Male
table(heart_data$sex_label)
## 
##   Male Female 
##     65     65

The output confirms that the dataset contains 65 male observations and 65 female observations.

male_data <- heart_data %>%
  filter(sex_label == "Male")

female_data <- heart_data %>%
  filter(sex_label == "Female")

male_heart_rate <- male_data$heart_rate
female_heart_rate <- female_data$heart_rate

2 Male Resting Heart Rate Analysis

2.1 Descriptive Statistics

male_stats <- data.frame(
  Statistic = c(
    "Sample size", "Minimum", "First quartile (Q1)", "Sample median",
    "Sample mean", "Third quartile (Q3)", "Maximum",
    "Sample standard deviation", "Range", "Interquartile range"
  ),
  Value = c(
    length(male_heart_rate),
    min(male_heart_rate),
    unname(quantile(male_heart_rate, 0.25)),
    median(male_heart_rate),
    mean(male_heart_rate),
    unname(quantile(male_heart_rate, 0.75)),
    max(male_heart_rate),
    sd(male_heart_rate),
    diff(range(male_heart_rate)),
    IQR(male_heart_rate)
  )
)

male_stats$Value <- round(male_stats$Value, 2)

knitr::kable(
  male_stats,
  col.names = c("Statistic", "Value (BPM)"),
  caption = "Descriptive Statistics for Male Resting Heart Rate"
)
Descriptive Statistics for Male Resting Heart Rate
Statistic Value (BPM)
Sample size 65.00
Minimum 58.00
First quartile (Q1) 70.00
Sample median 73.00
Sample mean 73.37
Third quartile (Q3) 78.00
Maximum 86.00
Sample standard deviation 5.88
Range 28.00
Interquartile range 8.00

2.2 Histogram

ggplot(male_data, aes(x = heart_rate)) +
  geom_histogram(
    binwidth = 4,
    boundary = 0,
    fill = "blue",
    color = "black",
    alpha = 0.70
  ) +
  geom_vline(
    xintercept = mean(male_heart_rate),
    color = "red",
    linewidth = 1,
    linetype = "dashed"
  ) +
  labs(
    title = "Distribution of Male Resting Heart Rate",
    subtitle = paste(
      "Dashed line: sample mean =",
      round(mean(male_heart_rate), 2),
      "BPM"
    ),
    x = "Male Resting Heart Rate (BPM)",
    y = "Frequency"
  ) +
  theme_minimal()

2.3 Normal Probability Plot

ggplot(male_data, aes(sample = heart_rate)) +
  stat_qq(color = "blue", size = 2) +
  stat_qq_line(color = "red", linewidth = 1, linetype = "dashed") +
  labs(
    title = "Normal Q-Q Plot of Male Resting Heart Rate",
    x = "Theoretical Normal Quantiles",
    y = "Observed Male Resting Heart Rate (BPM)"
  ) +
  theme_minimal()

3 Female Resting Heart Rate Analysis

3.1 Descriptive Statistics

female_stats <- data.frame(
  Statistic = c(
    "Sample size", "Minimum", "First quartile (Q1)", "Sample median",
    "Sample mean", "Third quartile (Q3)", "Maximum",
    "Sample standard deviation", "Range", "Interquartile range"
  ),
  Value = c(
    length(female_heart_rate),
    min(female_heart_rate),
    unname(quantile(female_heart_rate, 0.25)),
    median(female_heart_rate),
    mean(female_heart_rate),
    unname(quantile(female_heart_rate, 0.75)),
    max(female_heart_rate),
    sd(female_heart_rate),
    diff(range(female_heart_rate)),
    IQR(female_heart_rate)
  )
)

female_stats$Value <- round(female_stats$Value, 2)

knitr::kable(
  female_stats,
  col.names = c("Statistic", "Value (BPM)"),
  caption = "Descriptive Statistics for Female Resting Heart Rate"
)
Descriptive Statistics for Female Resting Heart Rate
Statistic Value (BPM)
Sample size 65.00
Minimum 57.00
First quartile (Q1) 68.00
Sample median 76.00
Sample mean 74.15
Third quartile (Q3) 80.00
Maximum 89.00
Sample standard deviation 8.11
Range 32.00
Interquartile range 12.00

3.2 Histogram

ggplot(female_data, aes(x = heart_rate)) +
  geom_histogram(
    binwidth = 4,
    boundary = 0,
    fill = "pink",
    color = "black",
    alpha = 0.80
  ) +
  geom_vline(
    xintercept = mean(female_heart_rate),
    color = "red",
    linewidth = 1,
    linetype = "dashed"
  ) +
  labs(
    title = "Distribution of Female Resting Heart Rate",
    subtitle = paste(
      "Dashed line: sample mean =",
      round(mean(female_heart_rate), 2),
      "BPM"
    ),
    x = "Female Resting Heart Rate (BPM)",
    y = "Frequency"
  ) +
  theme_minimal()

3.3 Normal Probability Plot

ggplot(female_data, aes(sample = heart_rate)) +
  stat_qq(color = "deeppink", size = 2) +
  stat_qq_line(color = "red", linewidth = 1, linetype = "dashed") +
  labs(
    title = "Normal Q-Q Plot of Female Resting Heart Rate",
    x = "Theoretical Normal Quantiles",
    y = "Observed Female Resting Heart Rate (BPM)"
  ) +
  theme_minimal()

4 Comparison of Male and Female Heart Rates

4.1 Side-by-Side Boxplots

ggplot(
  heart_data,
  aes(x = sex_label, y = heart_rate, fill = sex_label)
) +
  geom_boxplot(width = 0.55, alpha = 0.75, color = "black") +
  scale_fill_manual(values = c("Male" = "blue", "Female" = "pink")) +
  labs(
    title = "Comparison of Male and Female Resting Heart Rates",
    x = "Sex",
    y = "Resting Heart Rate (BPM)"
  ) +
  theme_minimal() +
  theme(legend.position = "none")

4.2 Summary Statistics by Sex

summary_stats <- heart_data %>%
  group_by(sex_label) %>%
  summarise(
    N = n(),
    Minimum = min(heart_rate),
    Q1 = unname(quantile(heart_rate, 0.25)),
    Median = median(heart_rate),
    Mean = mean(heart_rate),
    Q3 = unname(quantile(heart_rate, 0.75)),
    Maximum = max(heart_rate),
    SD = sd(heart_rate),
    Range = diff(range(heart_rate)),
    IQR = IQR(heart_rate),
    .groups = "drop"
  ) %>%
  mutate(across(where(is.numeric), ~ round(.x, 2)))

knitr::kable(
  summary_stats,
  col.names = c(
    "Sex", "N", "Minimum", "Q1", "Median", "Mean", "Q3",
    "Maximum", "SD", "Range", "IQR"
  ),
  caption = "Comparison of Male and Female Resting Heart Rates"
)
Comparison of Male and Female Resting Heart Rates
Sex N Minimum Q1 Median Mean Q3 Maximum SD Range IQR
Male 65 58 70 73 73.37 78 86 5.88 28 8
Female 65 57 68 76 74.15 80 89 8.11 32 12