1 Problem 1 — Creating a Dataset of Patients

Background: 10 patients’ cardiovascular profiles (Age, Gender, Blood Pressure, Cholesterol, Diagnosis).
Task: Recreate patients_data and inspect it with View().

patients_data <- data.frame(
  ID          = 1:10,
  Name        = c("Ayesha", "Rahim", "Karim", "Nusrat", "Sadia",
                  "Tanvir", "Farhana", "Imran", "Mim", "Jamal"),
  Age         = c(45, 52, 38, 29, 41, 34, 60, 47, 36, 25),
  Gender      = c("F", "M", "M", "F", "F", "M", "F", "M", "F", "M"),
  BP_Sys      = c(145, 150, 130, 118, 138, 122, 155, 142, 128, 115),
  BP_Dia      = c(95, 98, 85, 76, 88, 80, 100, 92, 84, 74),
  Cholesterol = c(240, 260, 210, 180, 225, 195, 270, 235, 205, 175),
  Diagnosis   = c("Hypertension", "Hypertension", "Borderline", "Normal",
                  "Borderline", "Normal", "Hypertension", "Hypertension",
                  "Borderline", "Normal"),
  stringsAsFactors = FALSE
)

# Rendered inspection of the full data frame
knitr::kable(patients_data, caption = "Table 1. patients_data — 10 cardiovascular profiles")
Table 1. patients_data — 10 cardiovascular profiles
ID Name Age Gender BP_Sys BP_Dia Cholesterol Diagnosis
1 Ayesha 45 F 145 95 240 Hypertension
2 Rahim 52 M 150 98 260 Hypertension
3 Karim 38 M 130 85 210 Borderline
4 Nusrat 29 F 118 76 180 Normal
5 Sadia 41 F 138 88 225 Borderline
6 Tanvir 34 M 122 80 195 Normal
7 Farhana 60 F 155 100 270 Hypertension
8 Imran 47 M 142 92 235 Hypertension
9 Mim 36 F 128 84 205 Borderline
10 Jamal 25 M 115 74 175 Normal
glimpse(patients_data)
## Rows: 10
## Columns: 8
## $ ID          <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
## $ Name        <chr> "Ayesha", "Rahim", "Karim", "Nusrat", "Sadia", "Tanvir", "…
## $ Age         <dbl> 45, 52, 38, 29, 41, 34, 60, 47, 36, 25
## $ Gender      <chr> "F", "M", "M", "F", "F", "M", "F", "M", "F", "M"
## $ BP_Sys      <dbl> 145, 150, 130, 118, 138, 122, 155, 142, 128, 115
## $ BP_Dia      <dbl> 95, 98, 85, 76, 88, 80, 100, 92, 84, 74
## $ Cholesterol <dbl> 240, 260, 210, 180, 225, 195, 270, 235, 205, 175
## $ Diagnosis   <chr> "Hypertension", "Hypertension", "Borderline", "Normal", "B…

View() opens the interactive RStudio spreadsheet viewer. It works in the Console but cannot be embedded in a knitted HTML page, so the chunk below is set to eval = FALSE (run it manually with Ctrl+Enter):

View(patients_data)

Result: 10 patients × 8 variables.

2 Problem 2 — Select Key Clinical Columns

Task: Keep only Name, Age, Cholesterol, Diagnosis for the report.

report_data <- patients_data %>%
  select(Name, Age, Cholesterol, Diagnosis)

report_data
##       Name Age Cholesterol    Diagnosis
## 1   Ayesha  45         240 Hypertension
## 2    Rahim  52         260 Hypertension
## 3    Karim  38         210   Borderline
## 4   Nusrat  29         180       Normal
## 5    Sadia  41         225   Borderline
## 6   Tanvir  34         195       Normal
## 7  Farhana  60         270 Hypertension
## 8    Imran  47         235 Hypertension
## 9      Mim  36         205   Borderline
## 10   Jamal  25         175       Normal

3 Problem 3 — Filter Patients by Age and Gender

Task: Female patients older than 35 (follow-up consultation).

female_over35 <- patients_data %>%
  filter(Gender == "F", Age > 35)

female_over35
##   ID    Name Age Gender BP_Sys BP_Dia Cholesterol    Diagnosis
## 1  1  Ayesha  45      F    145     95         240 Hypertension
## 2  5   Sadia  41      F    138     88         225   Borderline
## 3  7 Farhana  60      F    155    100         270 Hypertension
## 4  9     Mim  36      F    128     84         205   Borderline

Result: 4 female patients over 35: Ayesha, Sadia, Farhana, Mim.

4 Problem 4 — Identify Hypertensive Males

Task: Gender == "M" and Diagnosis == "Hypertension" (drug-trial candidates).

hypertensive_males <- patients_data %>%
  filter(Gender == "M", Diagnosis == "Hypertension")

hypertensive_males
##   ID  Name Age Gender BP_Sys BP_Dia Cholesterol    Diagnosis
## 1  2 Rahim  52      M    150     98         260 Hypertension
## 2  8 Imran  47      M    142     92         235 Hypertension

Result: 2 eligible males — Rahim, Imran.

5 Problem 5 — Select Borderline Female Patients

Task: Gender == "F" and Diagnosis == "Borderline" (lifestyle counselling).

borderline_females <- patients_data %>%
  filter(Gender == "F", Diagnosis == "Borderline")

borderline_females
##   ID  Name Age Gender BP_Sys BP_Dia Cholesterol  Diagnosis
## 1  5 Sadia  41      F    138     88         225 Borderline
## 2  9   Mim  36      F    128     84         205 Borderline

Result: 2 patients — Sadia, Mim.

6 Problem 6 — Arrange by Cholesterol Level

Task: Rank patients lowest → highest, then highest → lowest.

# Ascending (lowest cholesterol first)
patients_data %>%
  arrange(Cholesterol)
##    ID    Name Age Gender BP_Sys BP_Dia Cholesterol    Diagnosis
## 1  10   Jamal  25      M    115     74         175       Normal
## 2   4  Nusrat  29      F    118     76         180       Normal
## 3   6  Tanvir  34      M    122     80         195       Normal
## 4   9     Mim  36      F    128     84         205   Borderline
## 5   3   Karim  38      M    130     85         210   Borderline
## 6   5   Sadia  41      F    138     88         225   Borderline
## 7   8   Imran  47      M    142     92         235 Hypertension
## 8   1  Ayesha  45      F    145     95         240 Hypertension
## 9   2   Rahim  52      M    150     98         260 Hypertension
## 10  7 Farhana  60      F    155    100         270 Hypertension
# Descending (highest cholesterol first)
patients_data %>%
  arrange(desc(Cholesterol))
##    ID    Name Age Gender BP_Sys BP_Dia Cholesterol    Diagnosis
## 1   7 Farhana  60      F    155    100         270 Hypertension
## 2   2   Rahim  52      M    150     98         260 Hypertension
## 3   1  Ayesha  45      F    145     95         240 Hypertension
## 4   8   Imran  47      M    142     92         235 Hypertension
## 5   5   Sadia  41      F    138     88         225   Borderline
## 6   3   Karim  38      M    130     85         210   Borderline
## 7   9     Mim  36      F    128     84         205   Borderline
## 8   6  Tanvir  34      M    122     80         195       Normal
## 9   4  Nusrat  29      F    118     76         180       Normal
## 10 10   Jamal  25      M    115     74         175       Normal

7 Problem 7 — Create a Combined Blood Pressure Score

Task: mutate() a new column BP_Sum = BP_Sys + BP_Dia.

patients_bp <- patients_data %>%
  mutate(BP_Sum = BP_Sys + BP_Dia)

patients_bp
##    ID    Name Age Gender BP_Sys BP_Dia Cholesterol    Diagnosis BP_Sum
## 1   1  Ayesha  45      F    145     95         240 Hypertension    240
## 2   2   Rahim  52      M    150     98         260 Hypertension    248
## 3   3   Karim  38      M    130     85         210   Borderline    215
## 4   4  Nusrat  29      F    118     76         180       Normal    194
## 5   5   Sadia  41      F    138     88         225   Borderline    226
## 6   6  Tanvir  34      M    122     80         195       Normal    202
## 7   7 Farhana  60      F    155    100         270 Hypertension    255
## 8   8   Imran  47      M    142     92         235 Hypertension    234
## 9   9     Mim  36      F    128     84         205   Borderline    212
## 10 10   Jamal  25      M    115     74         175       Normal    189
# Focused view of the derived variable
patients_bp %>%
  select(ID, Name, BP_Sys, BP_Dia, BP_Sum) %>%
  arrange(desc(BP_Sum))
##    ID    Name BP_Sys BP_Dia BP_Sum
## 1   7 Farhana    155    100    255
## 2   2   Rahim    150     98    248
## 3   1  Ayesha    145     95    240
## 4   8   Imran    142     92    234
## 5   5   Sadia    138     88    226
## 6   3   Karim    130     85    215
## 7   9     Mim    128     84    212
## 8   6  Tanvir    122     80    202
## 9   4  Nusrat    118     76    194
## 10 10   Jamal    115     74    189

8 Problem 8 — Calculate Average Age and Cholesterol

Task: Overall mean_age and mean_chol.

overall_summary <- patients_data %>%
  summarise(
    mean_age  = mean(Age),
    mean_chol = mean(Cholesterol)
  )

overall_summary
##   mean_age mean_chol
## 1     40.7     219.5

Result: mean age = 40.7 years; mean cholesterol = 219.5 mg/dL.

9 Problem 9 — Compare Cholesterol by Diagnosis Group

Task: group_by(Diagnosis) + summarise() → mean age, mean cholesterol, count n.

group_stats <- patients_data %>%
  group_by(Diagnosis) %>%
  summarise(
    mean_age  = mean(Age),
    mean_chol = mean(Cholesterol),
    n         = n(),
    .groups   = "drop"
  )

group_stats
## # A tibble: 3 × 4
##   Diagnosis    mean_age mean_chol     n
##   <chr>           <dbl>     <dbl> <int>
## 1 Borderline       38.3      213.     3
## 2 Hypertension     51        251.     4
## 3 Normal           29.3      183.     3

10 Problem 10 — Identify Highest Risk Group

Task: Rank the grouped summary with arrange(desc(mean_chol)).

risk_ranked <- group_stats %>%
  arrange(desc(mean_chol))

risk_ranked
## # A tibble: 3 × 4
##   Diagnosis    mean_age mean_chol     n
##   <chr>           <dbl>     <dbl> <int>
## 1 Hypertension     51        251.     4
## 2 Borderline       38.3      213.     3
## 3 Normal           29.3      183.     3

Result: The Hypertension group has the highest average cholesterol (251.25 mg/dL), making it the highest-risk category for lipid-lowering intervention.

11 Problem 11 — Analyze Female Subgroup

Task: filter(Gender == "F") → group_by(Diagnosis) → summarise() → arrange(mean_chol).

female_summary <- patients_data %>%
  filter(Gender == "F") %>%
  group_by(Diagnosis) %>%
  summarise(
    mean_age  = mean(Age),
    mean_chol = mean(Cholesterol),
    n         = n(),
    .groups   = "drop"
  ) %>%
  arrange(mean_chol)

female_summary
## # A tibble: 3 × 4
##   Diagnosis    mean_age mean_chol     n
##   <chr>           <dbl>     <dbl> <int>
## 1 Normal           29         180     1
## 2 Borderline       38.5       215     2
## 3 Hypertension     52.5       255     2

Result: Among females, the Hypertension group shows the highest mean cholesterol (255 mg/dL).

12 Problem 12 — Remove Derived Variables

Task: Keep only the original 8 columns before export.

# Simulate the "mistakenly added" derived columns
patients_extra <- patients_bp %>%
  mutate(
    Chol_mmol = round(Cholesterol / 38.67, 2),
    Age_Group = ifelse(Age >= 50, "Senior", "Adult")
  )

names(patients_extra)   # 11 columns: 8 original + 3 derived
##  [1] "ID"          "Name"        "Age"         "Gender"      "BP_Sys"     
##  [6] "BP_Dia"      "Cholesterol" "Diagnosis"   "BP_Sum"      "Chol_mmol"  
## [11] "Age_Group"
patients_clean <- patients_extra %>%
  select(ID, Name, Age, Gender, BP_Sys, BP_Dia, Cholesterol, Diagnosis)

names(patients_clean)   # back to the original 8
## [1] "ID"          "Name"        "Age"         "Gender"      "BP_Sys"     
## [6] "BP_Dia"      "Cholesterol" "Diagnosis"
patients_clean
##    ID    Name Age Gender BP_Sys BP_Dia Cholesterol    Diagnosis
## 1   1  Ayesha  45      F    145     95         240 Hypertension
## 2   2   Rahim  52      M    150     98         260 Hypertension
## 3   3   Karim  38      M    130     85         210   Borderline
## 4   4  Nusrat  29      F    118     76         180       Normal
## 5   5   Sadia  41      F    138     88         225   Borderline
## 6   6  Tanvir  34      M    122     80         195       Normal
## 7   7 Farhana  60      F    155    100         270 Hypertension
## 8   8   Imran  47      M    142     92         235 Hypertension
## 9   9     Mim  36      F    128     84         205   Borderline
## 10 10   Jamal  25      M    115     74         175       Normal
identical(names(patients_clean), names(patients_data))
## [1] TRUE
# write.csv(patients_clean, "patients_clean.csv", row.names = FALSE)  # for export

13 Problem 13 — Convert Blood Pressure Data to Long Format

Task: pivot_longer() on BP_Sys and BP_Dia → BP_Type, Value.

bp_long <- patients_clean %>%
  pivot_longer(
    cols      = c(BP_Sys, BP_Dia),
    names_to  = "BP_Type",
    values_to = "Value"
  )

bp_long
## # A tibble: 20 × 8
##       ID Name      Age Gender Cholesterol Diagnosis    BP_Type Value
##    <int> <chr>   <dbl> <chr>        <dbl> <chr>        <chr>   <dbl>
##  1     1 Ayesha     45 F              240 Hypertension BP_Sys    145
##  2     1 Ayesha     45 F              240 Hypertension BP_Dia     95
##  3     2 Rahim      52 M              260 Hypertension BP_Sys    150
##  4     2 Rahim      52 M              260 Hypertension BP_Dia     98
##  5     3 Karim      38 M              210 Borderline   BP_Sys    130
##  6     3 Karim      38 M              210 Borderline   BP_Dia     85
##  7     4 Nusrat     29 F              180 Normal       BP_Sys    118
##  8     4 Nusrat     29 F              180 Normal       BP_Dia     76
##  9     5 Sadia      41 F              225 Borderline   BP_Sys    138
## 10     5 Sadia      41 F              225 Borderline   BP_Dia     88
## 11     6 Tanvir     34 M              195 Normal       BP_Sys    122
## 12     6 Tanvir     34 M              195 Normal       BP_Dia     80
## 13     7 Farhana    60 F              270 Hypertension BP_Sys    155
## 14     7 Farhana    60 F              270 Hypertension BP_Dia    100
## 15     8 Imran      47 M              235 Hypertension BP_Sys    142
## 16     8 Imran      47 M              235 Hypertension BP_Dia     92
## 17     9 Mim        36 F              205 Borderline   BP_Sys    128
## 18     9 Mim        36 F              205 Borderline   BP_Dia     84
## 19    10 Jamal      25 M              175 Normal       BP_Sys    115
## 20    10 Jamal      25 M              175 Normal       BP_Dia     74
c(rows_before = nrow(patients_clean), rows_after = nrow(bp_long))
## rows_before  rows_after 
##          10          20

Each patient now contributes two rows (one systolic, one diastolic), which is the tidy format required by ggplot2:

ggplot(bp_long, aes(x = factor(ID), y = Value, fill = BP_Type)) +
  geom_col(position = "dodge") +
  labs(title = "Systolic vs Diastolic BP per Patient",
       x = "Patient ID", y = "Blood Pressure (mmHg)", fill = "BP Type") +
  theme_minimal()

14 Problem 14 — Convert the Long Format Back to Wide Format

Task: pivot_wider() to restore separate BP_Sys and BP_Dia columns.

bp_wide <- bp_long %>%
  pivot_wider(
    names_from  = BP_Type,
    values_from = Value
  ) %>%
  select(all_of(names(patients_data)))   # restore original column order

bp_wide
## # A tibble: 10 × 8
##       ID Name      Age Gender BP_Sys BP_Dia Cholesterol Diagnosis   
##    <int> <chr>   <dbl> <chr>   <dbl>  <dbl>       <dbl> <chr>       
##  1     1 Ayesha     45 F         145     95         240 Hypertension
##  2     2 Rahim      52 M         150     98         260 Hypertension
##  3     3 Karim      38 M         130     85         210 Borderline  
##  4     4 Nusrat     29 F         118     76         180 Normal      
##  5     5 Sadia      41 F         138     88         225 Borderline  
##  6     6 Tanvir     34 M         122     80         195 Normal      
##  7     7 Farhana    60 F         155    100         270 Hypertension
##  8     8 Imran      47 M         142     92         235 Hypertension
##  9     9 Mim        36 F         128     84         205 Borderline  
## 10    10 Jamal      25 M         115     74         175 Normal
# Round-trip check: wide data identical to the original?
all.equal(as.data.frame(bp_wide), patients_data)
## [1] TRUE

15 Problem 15 — Compare Two Study Cohorts

Task: intersect() and setdiff() on the two ID vectors, print both, and interpret.

cohort_A <- c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)   # current study
cohort_B <- c(2, 3, 7, 10, 11, 12)             # follow-up study

common_patients <- base::intersect(cohort_A, cohort_B)
only_in_A       <- base::setdiff(cohort_A, cohort_B)
only_in_B       <- base::setdiff(cohort_B, cohort_A)   # bonus: new recruits

cat("Cohort A:", cohort_A, "\n")
## Cohort A: 1 2 3 4 5 6 7 8 9 10
cat("Cohort B:", cohort_B, "\n\n")
## Cohort B: 2 3 7 10 11 12
cat("Common to both cohorts (intersect): ", common_patients, "\n")
## Common to both cohorts (intersect):  2 3 7 10
cat("Unique to Cohort A (setdiff A - B): ", only_in_A, "\n")
## Unique to Cohort A (setdiff A - B):  1 4 5 6 8 9
cat("New in Cohort B (setdiff B - A):    ", only_in_B, "\n")
## New in Cohort B (setdiff B - A):     11 12
tibble(
  Set_operation = c("intersect(A, B)", "setdiff(A, B)", "setdiff(B, A)"),
  IDs           = c(paste(common_patients, collapse = ", "),
                    paste(only_in_A, collapse = ", "),
                    paste(only_in_B, collapse = ", ")),
  Count         = c(length(common_patients), length(only_in_A), length(only_in_B)),
  Meaning       = c("Retained / followed-up patients",
                    "Lost to follow-up (dropouts)",
                    "Newly enrolled patients")
) %>% knitr::kable(caption = "Table 2. Cohort overlap summary")
Table 2. Cohort overlap summary
Set_operation IDs Count Meaning
intersect(A, B) 2, 3, 7, 10 4 Retained / followed-up patients
setdiff(A, B) 1, 4, 5, 6, 8, 9 6 Lost to follow-up (dropouts)
setdiff(B, A) 11, 12 2 Newly enrolled patients
# Who are the retained patients, clinically?
patients_data %>%
  filter(ID %in% common_patients) %>%
  select(ID, Name, Age, Gender, Cholesterol, Diagnosis)
##   ID    Name Age Gender Cholesterol    Diagnosis
## 1  2   Rahim  52      M         260 Hypertension
## 2  3   Karim  38      M         210   Borderline
## 3  7 Farhana  60      F         270 Hypertension
## 4 10   Jamal  25      M         175       Normal

Biomedical interpretation

  • intersect(A, B) = 2, 3, 7, 10 — the 4 patients enrolled in the baseline study and returning for follow-up. These are the only subjects with paired longitudinal measurements, so they form the valid analysis set for before/after or treatment-effect comparisons (retention rate = 40%).
  • setdiff(A, B) = 1, 4, 5, 6, 8, 9 — the 6 baseline patients missing from the follow-up: loss to follow-up / attrition. They must be checked for dropout bias (e.g. if sicker or older patients preferentially disappear, outcome estimates become over-optimistic).
  • setdiff(B, A) = 11, 12 — IDs appearing only at follow-up: new recruits (or ID/data-entry errors that need verification), since they have no baseline record.

16 Conclusion

tibble(
  Metric = c("Total patients", "Female patients", "Male patients",
             "Mean age (years)", "Mean cholesterol (mg/dL)",
             "Highest-risk diagnosis"),
  Value  = c(nrow(patients_data),
             sum(patients_data$Gender == "F"),
             sum(patients_data$Gender == "M"),
             round(mean(patients_data$Age), 2),
             round(mean(patients_data$Cholesterol), 2),
             risk_ranked$Diagnosis[1])
) %>% knitr::kable(caption = "Table 3. Key takeaways")
Table 3. Key takeaways
Metric Value
Total patients 10
Female patients 5
Male patients 5
Mean age (years) 40.7
Mean cholesterol (mg/dL) 219.5
Highest-risk diagnosis Hypertension

All 15 tasks were completed with the tidyverse grammar: select() / filter() for subsetting, arrange() for ranking, mutate() for derived variables, group_by() + summarise() for aggregation, pivot_longer() / pivot_wider() for reshaping, and base set operations (intersect(), setdiff()) for cohort comparison.

17 Session Info

sessionInfo()
## R version 4.4.3 (2025-02-28)
## Platform: x86_64-conda-linux-gnu
## Running under: Ubuntu 24.04.5 LTS
## 
## Matrix products: default
## BLAS/LAPACK: /home/neolith/anaconda3/envs/rstudio_env/lib/libopenblasp-r0.3.33.so;  LAPACK version 3.12.0
## 
## locale:
##  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
##  [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
##  [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       
## 
## time zone: Asia/Dhaka
## tzcode source: system (glibc)
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
##  [1] lubridate_1.9.5 forcats_1.0.1   stringr_1.6.0   dplyr_1.2.1    
##  [5] purrr_1.2.2     readr_2.2.0     tidyr_1.3.2     tibble_3.3.1   
##  [9] ggplot2_4.0.4   tidyverse_2.0.0
## 
## loaded via a namespace (and not attached):
##  [1] gtable_0.3.6       jsonlite_2.0.0     compiler_4.4.3     tidyselect_1.2.1  
##  [5] jquerylib_0.1.4    scales_1.4.0       yaml_2.3.12        fastmap_1.2.0     
##  [9] R6_2.6.1           labeling_0.4.3     generics_0.1.4     knitr_1.51        
## [13] bslib_0.11.0       pillar_1.11.1      RColorBrewer_1.1-3 tzdb_0.5.0        
## [17] rlang_1.3.0        utf8_1.2.6         stringi_1.8.7      cachem_1.1.0      
## [21] xfun_0.59          sass_0.4.10        S7_0.2.2           timechange_0.4.0  
## [25] cli_3.6.6          withr_3.0.3        magrittr_2.0.5     digest_0.6.39     
## [29] grid_4.4.3         rstudioapi_0.19.0  hms_1.1.4          lifecycle_1.0.5   
## [33] vctrs_0.7.3        evaluate_1.0.5     glue_1.8.1         farver_2.1.2      
## [37] rmarkdown_2.31     tools_4.4.3        pkgconfig_2.0.3    htmltools_0.5.9