Chronic Kidney Disease Project Analysis

Author

OJALA BRIAN OLOO

About the data set

The dataset provides a comprehensive collection of patient clinical information, drug exposure profiles, and drug-related biochemical characteristics to support research on the early identification of Chronic Kidney Disease (CKD). It combines real-world–style patient health indicators with detailed properties of nephrotoxic and non-nephrotoxic medications that may influence kidney function. The data set contains:

Patient Clinical Information

Includes age, gender, blood pressure, blood urea, serum creatinine, albumin levels, random blood glucose, and health conditions such as diabetes and hypertension. These features reflect common clinical factors associated with kidney health.

Drug Exposure Profiles

Each patient was linked to a drug along with dosage and duration of use. A separate label indicates whether the drug is considered nephrotoxic effects.

N/B: This data set was downloaded from Kaggle

Aim of the Project: To analyze patient data associated with neurotoxic effects

Code
# Clearing R environment
rm(list = ls(all.names = TRUE))
# Setting working directory
setwd("C:/Epidemiology")
# Import data set
library(readr)
CDK <- read_csv("CDK.csv")
Rows: 1500 Columns: 37
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr  (1): drug_name
dbl (36): patient_age, gender, bp_systolic, bp_diastolic, blood_urea, serum_...

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
Code
# Explore the first 10 observations
head(CDK, n = 10)
# A tibble: 10 × 37
   patient_age gender bp_systolic bp_diastolic blood_urea serum_creatinine
         <dbl>  <dbl>       <dbl>        <dbl>      <dbl>            <dbl>
 1          69      1       136.          90.4       45.3             1.42
 2          32      1       125           62.6       22.4             0.66
 3          89      0       124.          71.1       36               1.72
 4          78      1        98.7         81.6        4.7             0.34
 5          38      1       148.          81.4       56.6             1.39
 6          41      0       128.          70.5       27.3             0.58
 7          20      0       126.          89.7       35.8             0.98
 8          39      1       194.          88         30.4             0.48
 9          70      0       136           82.7       52.2             1.59
10          19      1       115           75.9       -7.6             0.6 
# ℹ 31 more variables: albumin <dbl>, blood_glucose_random <dbl>,
#   diabetes <dbl>, hypertension <dbl>, drug_name <chr>, drug_dosage_mg <dbl>,
#   exposure_days <dbl>, nephrotoxic_label <dbl>, mol_weight <dbl>, logP <dbl>,
#   hbond_donors <dbl>, hbond_acceptors <dbl>, rotatable_bonds <dbl>,
#   tpsa <dbl>, shape_index_3d <dbl>, inertia_x <dbl>, inertia_y <dbl>,
#   inertia_z <dbl>, charge_distribution <dbl>, clearance_rate <dbl>,
#   half_life_hr <dbl>, bioavailability_pct <dbl>, …

Data Management

Code
# Load packages
library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.2.0     ✔ purrr     1.2.1
✔ forcats   1.0.1     ✔ stringr   1.6.0
✔ ggplot2   4.0.2     ✔ tibble    3.3.1
✔ lubridate 1.9.5     ✔ tidyr     1.3.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
Code
library(expss)
Loading required package: maditr

To modify variables or add new variables:
             let(mtcars, new_var = 42, new_var2 = new_var*hp) %>% head()


Attaching package: 'maditr'

The following objects are masked from 'package:dplyr':

    between, coalesce, first, last

The following object is masked from 'package:purrr':

    transpose

The following object is masked from 'package:readr':

    cols


Use 'expss_output_viewer()' to display tables in the RStudio Viewer.
 To return to the console output, use 'expss_output_default()'.


Attaching package: 'expss'

The following objects are masked from 'package:stringr':

    fixed, regex

The following objects are masked from 'package:dplyr':

    compute, contains, na_if, recode, vars, where

The following objects are masked from 'package:purrr':

    keep, modify, modify_if, when

The following objects are masked from 'package:tidyr':

    contains, nest

The following object is masked from 'package:ggplot2':

    vars
Code
library(summarytools) 

Attaching package: 'summarytools'

The following object is masked from 'package:tibble':

    view
Code
library(table1)
Registered S3 method overwritten by 'table1':
  method    from
  [.indexed plyr

Attaching package: 'table1'

The following objects are masked from 'package:summarytools':

    label, label<-

The following objects are masked from 'package:base':

    units, units<-
Code
library(psych)

Attaching package: 'psych'

The following objects are masked from 'package:ggplot2':

    %+%, alpha
Code
library(gtsummary)

Attaching package: 'gtsummary'

The following objects are masked from 'package:expss':

    contains, vars, where
Code
library(flextable)

Attaching package: 'flextable'

The following object is masked from 'package:gtsummary':

    continuous_summary

The following object is masked from 'package:expss':

    set_caption

The following object is masked from 'package:purrr':

    compose
Code
library(officer)
library(broom)
library(broom.helpers)

Attaching package: 'broom.helpers'

The following objects are masked from 'package:gtsummary':

    all_categorical, all_continuous, all_contrasts, all_dichotomous,
    all_interaction, all_intercepts

The following objects are masked from 'package:expss':

    contains, vars, where
Code
library(gt)

Attaching package: 'gt'

The following objects are masked from 'package:expss':

    contains, gt, tab_caption, vars, where
Code
library(readxl)

Attaching package: 'readxl'

The following object is masked from 'package:officer':

    read_xlsx
Code
library(writexl)
library(finalfit)
df<-CDK|>
  select(patient_age,gender,bp_systolic,bp_diastolic,blood_urea,
         serum_creatinine,diabetes,hypertension,ckd_risk_label,nephrotoxic_label,drug_name)|>
  mutate(
    gender= factor(gender,
                levels = c(0,1),
                      labels = c("Female","Male"),
                      exclude = NA),
    diabetes=factor(diabetes,
              levels = c(0,1),
              labels = c("No","Yes"),
              exclude = NA),
    hypertension=factor(hypertension,
                levels = c(0,1),
                labels = c("No","Yes"),
                exclude = NA),
    nephrotoxic_label=factor(nephrotoxic_label,
                    levels = c(0,1),
                    labels = c("non-nephrotoxic","nephrotoxic"),
                    exclude = NA),

    ckd_risk_label=factor(ckd_risk_label,
                  levels = c(0,1,2),
                  labels=c("Low risk","Moderate risk","High risk"),
                  exclude = NA))|>
  apply_labels(
        patient_age=    " Age(years)" ,
        gender= "Sex",
        bp_systolic=    "Systolic blood pressure(mm Hg)",
       bp_diastolic=    "Diastolic blood pressure(mm/HG)" ,
        blood_urea= "Blood urea(mg/dl)",
        serum_creatinine=   "Serum creatinine(mg/dl)",
      ckd_risk_label=   "Risk of chronic Kidney disease" ,
      nephrotoxic_label=    "Nephrotoxicity" ,
     drug_name= "Type of drug")|>
  mutate(Age_cat=case_when(
      patient_age<25~1,
      patient_age>=25 & patient_age<29~2,
      patient_age>=30 & patient_age<35~3,
      patient_age>=36 & patient_age<44~4,
      patient_age>=45 & patient_age<54~5,
      patient_age>=55 & patient_age<64~6,
      patient_age>=65~7 
    ))|>
  mutate(
    Age_cat=factor(Age_cat,
                   levels = c(1,2,3,4,5,6,7),
                          labels = c("18-24","25-29","30-35","36-44","45-54",
                                     "55-64","65+"),))|>
  apply_labels(
        Age_cat=    " Patient Age group(years)")
head(df,n=10)
# A tibble: 10 × 12
   patient_age gender bp_systolic bp_diastolic blood_urea serum_creatinine
   <labelled>  <fct>  <labelled>  <labelled>   <labelled> <labelled>      
 1 69          Male   136.5       90.4         45.3       1.42            
 2 32          Male   125.0       62.6         22.4       0.66            
 3 89          Female 124.2       71.1         36.0       1.72            
 4 78          Male    98.7       81.6          4.7       0.34            
 5 38          Male   147.7       81.4         56.6       1.39            
 6 41          Female 128.4       70.5         27.3       0.58            
 7 20          Female 126.4       89.7         35.8       0.98            
 8 39          Male   193.9       88.0         30.4       0.48            
 9 70          Female 136.0       82.7         52.2       1.59            
10 19          Male   115.0       75.9         -7.6       0.60            
# ℹ 6 more variables: diabetes <fct>, hypertension <fct>, ckd_risk_label <fct>,
#   nephrotoxic_label <fct>, drug_name <labelled>, Age_cat <fct>

Exploratory Data Analysis

Gender distribution

Code
# Pre-calculate Frequencies and Percentages
plot_data <- df %>%
  count(gender) %>% # Generates 'n' column for counts
  mutate(
    Percentage = (n / sum(n)) * 100,
    Label = paste0(n, " (", round(Percentage, 1), "%)") # Combine Count + %
  )
# Create the Bar Chart
ggplot(plot_data, aes(x = gender, y = n, fill = gender)) +
  geom_col(alpha = 0.8, show.legend = FALSE) + # geom_col plots the exact values
  geom_text(aes(label = Label), vjust = -0.5, size = 4.5) + # Add labels above bars
  labs(
    title = "Gender Distribution",
    x = "Sex Category",
    y = "Frequency (Count)"
  ) +
  ylim(0, max(plot_data$n) * 1.1) + # Leave space at the top for labels
  theme_minimal()

Prevalence of nephrotoxicity

Code
# Pre-calculate Frequencies and Percentages
plot_data_1 <- df %>%
  count(nephrotoxic_label) %>% # Generates 'n' column for counts
  mutate(
    Percentage = (n / sum(n)) * 100,
    Label = paste0(n, " (", round(Percentage, 1), "%)") # Combine Count + %
  )
# Create the Bar Chart
ggplot(plot_data_1, aes(x = nephrotoxic_label, y = n, fill = nephrotoxic_label)) +
  geom_col(alpha = 0.8, show.legend = FALSE) + 
  geom_text(aes(label = Label), vjust = -0.5, size = 4.5) + # Add labels above bars
  labs(
    title = "Prevalence of nephrotoxicity",
    x = "Nephrotoxicity",
    y = "Frequency (Count)"
  ) +
  ylim(0, max(plot_data_1$n) * 1.1) + # Leave space at the top for labels
  theme_minimal()

Diabetes distribution

Code
# Pre-calculate Frequencies and Percentages
plot_data_2 <- df %>%
  count(diabetes) %>% # Generates 'n' column for counts
  mutate(
    Percentage = (n / sum(n)) * 100,
    Label = paste0(n, " (", round(Percentage, 1), "%)") # Combine Count + %
  )
# Create the Bar Chart
ggplot(plot_data_2, aes(x = diabetes, y = n, fill = diabetes)) +
  geom_col(alpha = 0.8, show.legend = FALSE) + 
  geom_text(aes(label = Label), vjust = -0.5, size = 4.5) + # Add labels above bars
  labs(
    title = "Diabetes distribution",
    x = "Patients with diabetes",
    y = "Frequency (Count)"
  ) +
  ylim(0, max(plot_data_2$n) * 1.1) + # Leave space at the top for labels
  theme_minimal()

Hypertension distribution

Code
# Pre-calculate Frequencies and Percentages
plot_data_3 <- df %>%
  count(hypertension) %>% # Generates 'n' column for counts
  mutate(
    Percentage = (n / sum(n)) * 100,
    Label = paste0(n, " (", round(Percentage, 1), "%)") # Combine Count + %
  )
# Create the Bar Chart
ggplot(plot_data_3, aes(x = hypertension, y = n, fill = hypertension)) +
  geom_col(alpha = 0.8, show.legend = FALSE) + 
  geom_text(aes(label = Label), vjust = -0.5, size = 4.5) + # Add labels above bars
  labs(
    title = "Hypertension distribution",
    x = "Patients with hypertension",
    y = "Frequency (Count)"
  ) +
  ylim(0, max(plot_data_3$n) * 1.1) + # Leave space at the top for labels
  theme_minimal()

Code
# Pre-calculate Frequencies and Percentages
plot_data_4 <- df %>%
  count(ckd_risk_label) %>% # Generates 'n' column for counts
  mutate(
    Percentage = (n / sum(n)) * 100,
    Label = paste0(n, " (", round(Percentage, 1), "%)") # Combine Count + %
  )
# Create the Bar Chart
ggplot(plot_data_4, aes(x = ckd_risk_label, y = n, fill = ckd_risk_label)) +
  geom_col(alpha = 0.8, show.legend = FALSE) + 
  geom_text(aes(label = Label), vjust = -0.5, size = 4.5) + # Add labels above bars
  labs(
    title = "Risk of Chronic Kidney Disease ",
    x = "Risk of CKD",
    y = "Frequency (Count)"
  ) +
  ylim(0, max(plot_data_4$n) * 1.1) + # Leave space at the top for labels
  theme_minimal()

Drug names

Code
# Pre-calculate Frequencies and Percentages
plot_data_5 <- df %>%
  count(drug_name) %>% # Generates 'n' column for counts
  mutate(
    Percentage = (n / sum(n)) * 100,
    Label = paste0(n, " (", round(Percentage, 1), "%)") # Combine Count + %
  )
# Create the Bar Chart
ggplot(plot_data_5, aes(x = drug_name, y = n, fill = drug_name)) +
  geom_col(alpha = 0.8, show.legend = FALSE) + 
  geom_text(aes(label = Label), vjust = -0.5, size = 4.5) +
  # Add labels above bars
  labs(
    title = "Drugs names used in CKD Medications ",
    x = "Drug Name",
    y = "Frequency (Count)"
  ) +
  ylim(0, max(plot_data_5$n) * 1.1) + # Leave space at the top for labels
  theme_minimal()

Explanatory Data Analysis of Continuous Variables

Code
# Distribution of patients age
df%>%
  ggplot(aes(gender,patient_age,fill = gender))+
  geom_boxplot(show.legend = FALSE)+
  labs(
    title = "Patients Age distribution ",
    x = "Age distribution",
    y = "Median"
  )+
theme_minimal()

Code
# Systolic Blood bressure
df%>%
  ggplot(aes(gender,bp_systolic,fill = gender))+
  geom_boxplot(show.legend = FALSE)+
  labs(
    title = "Distribution in systolic blood pressure ",
    x = "Systolic blood pressure",
    y = "Median"
  )+
theme_minimal()

Code
# Diastolic blood pressure
df%>%
  ggplot(aes(gender,bp_diastolic,fill = gender))+
  geom_boxplot(show.legend = FALSE)+
  labs(
    title = "Distribution in diastolic blood pressure ",
    x = "Diastolic blood pressure",
    y = "Median"
  )+
theme_minimal()

Code
df%>%
  ggplot(aes(gender,blood_urea,fill = gender))+
  geom_boxplot(show.legend = FALSE)+
  labs(
    title = "Blood Urea distribution ",
    x = "Blood urea distribution",
    y = "Median"
  )+
theme_minimal()

Code
df%>%
  ggplot(aes(gender,serum_creatinine,fill = gender))+
  geom_boxplot(show.legend = FALSE)+
  labs(
    title = "Serum creatinine distribution ",
    x = "Serum creatinine",
    y = "Median"
  )+
theme_minimal()

Descriptive Statistics

Code
Table1<-df
  Mystat<-list(all_continuous()~"{mean} ± {sd}",
             all_categorical()~"{n} ({p})")
MyDigit<-list(all_continuous()~c(2,2),all_categorical()~c(0,2))
Table1<-df%>%
  tbl_summary(by=nephrotoxic_label,missing = "no",statistic = Mystat,digits = MyDigit)%>%
  bold_labels()
Table1
Characteristic non-nephrotoxic
N = 5781
nephrotoxic
N = 9221
Age(years) 53.54 ± 21.00 52.45 ± 20.77
Sex

    Female 286 (49.48) 490 (53.15)
    Male 292 (50.52) 432 (46.85)
Systolic blood pressure(mm Hg) 131.12 ± 19.91 129.81 ± 19.48
Diastolic blood pressure(mm/HG) 84.62 ± 12.04 84.58 ± 12.11
Blood urea(mg/dl) 34.64 ± 14.89 34.51 ± 15.35
Serum creatinine(mg/dl) 1.09 ± 0.61 1.08 ± 0.63
diabetes 253 (43.77) 402 (43.60)
hypertension 335 (57.96) 500 (54.23)
Risk of chronic Kidney disease

    Low risk 336 (58.13) 43 (4.66)
    Moderate risk 222 (38.41) 506 (54.88)
    High risk 20 (3.46) 373 (40.46)
Type of drug

    Amphotericin-B 0 (0.00) 169 (18.33)
    Aspirin 187 (32.35) 0 (0.00)
    Cisplatin 0 (0.00) 193 (20.93)
    Gentamicin 0 (0.00) 185 (20.07)
    Ibuprofen 211 (36.51) 0 (0.00)
    Paracetamol 180 (31.14) 0 (0.00)
    Tobramycin 0 (0.00) 178 (19.31)
    Vancomycin 0 (0.00) 197 (21.37)
Patient Age group(years)

    18-24 64 (11.68) 94 (11.15)
    25-29 24 (4.38) 55 (6.52)
    30-35 46 (8.39) 69 (8.19)
    36-44 64 (11.68) 104 (12.34)
    45-54 78 (14.23) 108 (12.81)
    55-64 73 (13.32) 100 (11.86)
    65+ 199 (36.31) 313 (37.13)
1 Mean ± SD; n (%)

Bivariate Analysis

Code
Table <-df%>%
  tbl_summary(
    by = nephrotoxic_label,  # Uncomment if you want group-wise summary
    statistic = list(
      all_continuous() ~ "{median} ({p25}, {p75})",
      all_categorical() ~ "{n} ({p}%)"
    ),
    percent = "column",
    missing = "no"
  ) %>%
  add_overall() %>%
  add_p(pvalue_fun = ~style_pvalue(.x, digits = 2)) %>%
  modify_footnote(all_stat_cols() ~ "Median (IQR)") %>%
  modify_spanning_header(c("stat_1", "stat_2") ~ "**nephrotoxic_label **") %>%
  modify_caption("Table 1: Patient Clinical Information") %>%
  bold_labels() %>%
  add_n() %>%
  as_flex_table()

sect_properties <- prop_section(page_size = page_size(orient = "portrait"))#, width = 8.3, height = 11.7)
save_as_docx(Table,path="Table1a.docx", pr_section = sect_properties)
Table

nephrotoxic_label

Characteristic

N

Overall
N = 1,5001

non-nephrotoxic
N = 5781

nephrotoxic
N = 9221

p-value2

Age(years)

1,500

53 (34, 71)

53 (36, 72)

52 (34, 71)

0.32

Sex

1,500

0.17

Female

776 (52%)

286 (49%)

490 (53%)

Male

724 (48%)

292 (51%)

432 (47%)

Systolic blood pressure(mm Hg)

1,500

130 (117, 143)

131 (118, 144)

129 (117, 142)

0.19

Diastolic blood pressure(mm/HG)

1,500

85 (76, 93)

84 (77, 93)

85 (76, 93)

0.88

Blood urea(mg/dl)

1,500

35 (25, 45)

35 (25, 45)

34 (25, 44)

0.61

Serum creatinine(mg/dl)

1,500

1.07 (0.65, 1.52)

1.07 (0.66, 1.49)

1.08 (0.65, 1.53)

0.91

diabetes

1,500

655 (44%)

253 (44%)

402 (44%)

0.95

hypertension

1,500

835 (56%)

335 (58%)

500 (54%)

0.16

Risk of chronic Kidney disease

1,500

<0.001

Low risk

379 (25%)

336 (58%)

43 (4.7%)

Moderate risk

728 (49%)

222 (38%)

506 (55%)

High risk

393 (26%)

20 (3.5%)

373 (40%)

Type of drug

1,500

<0.001

Amphotericin-B

169 (11%)

0 (0%)

169 (18%)

Aspirin

187 (12%)

187 (32%)

0 (0%)

Cisplatin

193 (13%)

0 (0%)

193 (21%)

Gentamicin

185 (12%)

0 (0%)

185 (20%)

Ibuprofen

211 (14%)

211 (37%)

0 (0%)

Paracetamol

180 (12%)

180 (31%)

0 (0%)

Tobramycin

178 (12%)

0 (0%)

178 (19%)

Vancomycin

197 (13%)

0 (0%)

197 (21%)

Patient Age group(years)

1,391

0.67

18-24

158 (11%)

64 (12%)

94 (11%)

25-29

79 (5.7%)

24 (4.4%)

55 (6.5%)

30-35

115 (8.3%)

46 (8.4%)

69 (8.2%)

36-44

168 (12%)

64 (12%)

104 (12%)

45-54

186 (13%)

78 (14%)

108 (13%)

55-64

173 (12%)

73 (13%)

100 (12%)

65+

512 (37%)

199 (36%)

313 (37%)

1Median (IQR)

2Wilcoxon rank sum test; Pearson's Chi-squared test

N/B: Please call this number 0743670039 if you need the following services