CKD273 Comparison
Related to Supplemental Figure 6 (panels c–f).
CKD273 is a validated 273-peptide urinary proteomics classifier for chronic kidney disease. This analysis scores the CKD273 36-gene approximation alongside the CAAH 10-gene signature in both mouse models and compares discrimination performance by ROC/AUC with DeLong's test. In the Ren1c KO model the two signatures perform comparably; in the captopril model the CAAH signature is significantly superior (p = 5.9 × 10^-48^), suggesting it captures RAAS-specific rather than generic CKD biology.
Define gene lists
ckd273_genes <- c("Col1a1", "Col1a2", "Col2a1", "Col3a1", "Col4a1", "Col5a1", "Col8a2", "Col11a1",
"Serpina1a", "Serpina1b", "Serpina1c", "Serpina1d", "Serpina1e", "Alb", "Fga", "Fgb",
"Fgg", "Apoa1", "B2m", "Serpinc1", "A1bg", "Umod", "Pigr", "Ahsg",
"Clu", "Prg4", "Spp1", "Serpina3n", "Ttr", "Cd99", "Cyb5r3", "Lct",
"Vgf", "Pcsk1", "Ptgds", "Psrc1")
caah_10_genes <- c("Clu", "Lrp2", "Lamp2", "Col4a2", "Spink1",
"Wfdc2", "Pax8", "Lyz2", "S100a9", "Cdh13")
shared_genes <- intersect(caah_10_genes, ckd273_genes)
cat(sprintf("Shared genes: %d / %d CAAH genes in CKD273\n",
length(shared_genes), length(caah_10_genes)))
Only Clu overlaps between the two signatures.
Load Seurat objects
ren1c_obj <- readRDS("ren1c_obj.rds")
captopril_obj <- readRDS("captopril_obj.rds")
Score both signatures with UCell
addSignatureScores scores both the CAAH and CKD273 gene lists in a Seurat object, reporting how many genes from each list are present in the data.
addSignatureScores <- function(seurat_obj) {
ckd273_avail <- ckd273_genes[ckd273_genes %in% rownames(seurat_obj)]
caah_avail <- caah_10_genes[caah_10_genes %in% rownames(seurat_obj)]
cat("CKD273:", length(ckd273_avail), "/", length(ckd273_genes), "genes available\n")
cat("CAAH: ", length(caah_avail), "/", length(caah_10_genes), "genes available\n")
seurat_obj <- AddModuleScore_UCell(seurat_obj,
features = list(ckd273_avail), name = "CKD273_Score", assay = "RNA")
seurat_obj <- AddModuleScore_UCell(seurat_obj,
features = list(caah_avail), name = "CAAH_Score", assay = "RNA")
colnames(seurat_obj@meta.data)[colnames(seurat_obj@meta.data) == "signature_1CKD273_Score"] <- "CKD273_Score"
colnames(seurat_obj@meta.data)[colnames(seurat_obj@meta.data) == "signature_1CAAH_Score"] <- "CAAH_Score"
list(object = seurat_obj,
ckd273_avail = ckd273_avail,
caah_avail = caah_avail)
}
ren1c_scored <- addSignatureScores(ren1c_obj)
ren1c_obj <- ren1c_scored$object
capt_scored <- addSignatureScores(captopril_obj)
captopril_obj <- capt_scored$object
ROC comparison with DeLong's test
calcRocComparison computes ROC curves for both signatures, tests their AUCs with DeLong's method, and returns the roc objects, AUCs, and p-value.
calcRocComparison <- function(seurat_obj, group_var, group1, group2) {
meta <- seurat_obj@meta.data %>%
dplyr::filter(get(group_var) %in% c(group1, group2)) %>%
dplyr::mutate(group_binary = ifelse(get(group_var) == group2, 1, 0))
roc_ckd <- roc(meta$group_binary, meta$CKD273_Score,
levels = c(0, 1), direction = "<", quiet = TRUE)
roc_caah <- roc(meta$group_binary, meta$CAAH_Score,
levels = c(0, 1), direction = "<", quiet = TRUE)
delong <- roc.test(roc_ckd, roc_caah, method = "delong")
list(roc_ckd = roc_ckd,
roc_caah = roc_caah,
auc_ckd = as.numeric(auc(roc_ckd)),
auc_caah = as.numeric(auc(roc_caah)),
p_value = delong$p.value)
}
ren1c_roc <- calcRocComparison(ren1c_obj, "genotype", "WT", "KO")
capt_roc <- calcRocComparison(captopril_obj, "condition", "ctrl", "trt")
cat(sprintf("Ren1c KO — CKD273 AUC: %.4f | CAAH AUC: %.4f | p = %.4e\n",
ren1c_roc$auc_ckd, ren1c_roc$auc_caah, ren1c_roc$p_value))
cat(sprintf("Captopril — CKD273 AUC: %.4f | CAAH AUC: %.4f | p = %.4e\n",
capt_roc$auc_ckd, capt_roc$auc_caah, capt_roc$p_value))
Results: Ren1c KO — CKD273 AUC 0.841, CAAH AUC 0.825, p = 0.15 (comparable). Captopril — CKD273 AUC 0.659, CAAH AUC 0.743, p = 5.9 × 10^-48^ (CAAH superior).
Cell-type-specific ROC
celltypeRocAnalysis <- function(seurat_obj, group_var, group1, group2) {
meta <- seurat_obj@meta.data %>%
dplyr::filter(get(group_var) %in% c(group1, group2)) %>%
dplyr::mutate(group_binary = ifelse(get(group_var) == group2, 1, 0))
results <- lapply(unique(meta$cell_type), function(ct) {
d <- meta[meta$cell_type == ct, ]
if (sum(d$group_binary == 0) < 10 || sum(d$group_binary == 1) < 10) return(NULL)
tryCatch({
data.frame(
cell_type = ct,
n_group1 = sum(d$group_binary == 0),
n_group2 = sum(d$group_binary == 1),
auc_ckd273 = as.numeric(auc(roc(d$group_binary, d$CKD273_Score,
levels = c(0,1), direction = "<", quiet = TRUE))),
auc_caah = as.numeric(auc(roc(d$group_binary, d$CAAH_Score,
levels = c(0,1), direction = "<", quiet = TRUE)))
)
}, error = function(e) NULL)
})
results_df <- bind_rows(Filter(Negate(is.null), results))
results_df$auc_difference <- results_df$auc_caah - results_df$auc_ckd273
results_df %>% arrange(desc(auc_caah))
}
ren1c_celltype_roc <- celltypeRocAnalysis(ren1c_obj, "genotype", "WT", "KO")
capt_celltype_roc <- celltypeRocAnalysis(captopril_obj, "condition", "ctrl", "trt")
fwrite(as.data.table(ren1c_celltype_roc),
paste0(parent_dir, "ren1c_ROC-celltype_CKD273-CAAH.csv"), sep = ",")
fwrite(as.data.table(capt_celltype_roc),
paste0(parent_dir, "captopril_ROC-celltype_CKD273-CAAH.csv"), sep = ",")
Figures — ROC curves and score distributions
createComparisonPlots returns a named list of ggplot objects: ROC curves, score distributions by group, correlation scatter, cell-type boxplots, and per-cell-type effect sizes.
createComparisonPlots <- function(seurat_obj, roc_results, group_var, model_name) {
meta <- seurat_obj@meta.data
# Panel: ROC curves
p_roc <- ggroc(list(CKD273 = roc_results$roc_ckd, CAAH = roc_results$roc_caah),
legacy.axes = TRUE) +
geom_abline(intercept = 0, slope = 1, linetype = "dashed", colour = "grey60") +
annotate("text", x = 0.65, y = 0.25,
label = sprintf("CKD273 AUC: %.3f\nCAAH AUC: %.3f\np = %.2e",
roc_results$auc_ckd, roc_results$auc_caah, roc_results$p_value),
hjust = 0, size = 3) +
scale_colour_manual(values = c("CKD273" = "#E69F00", "CAAH" = "#56B4E9")) +
labs(title = paste(model_name, "— ROC curves"),
x = "1 - Specificity", y = "Sensitivity", colour = NULL) +
theme_classic(base_size = 11)
# Panel: score distributions by group
score_long <- meta %>%
dplyr::select(all_of(c(group_var, "CKD273_Score", "CAAH_Score", "cell_type"))) %>%
tidyr::pivot_longer(cols = c("CKD273_Score", "CAAH_Score"),
names_to = "Signature", values_to = "Score")
p_dist <- ggplot(score_long, aes(x = get(group_var), y = Score,
fill = Signature, colour = Signature)) +
geom_violin(trim = FALSE, alpha = 0.7, position = position_dodge(0.9)) +
geom_boxplot(width = 0.15, position = position_dodge(0.9),
outlier.shape = NA, fill = "white") +
scale_fill_manual(values = c("CKD273_Score" = "#E69F00", "CAAH_Score" = "#56B4E9")) +
scale_colour_manual(values = c("CKD273_Score" = "#E69F00", "CAAH_Score" = "#56B4E9")) +
labs(title = paste(model_name, "— score distributions"), x = NULL, y = "UCell score") +
theme_classic(base_size = 11) + theme(legend.position = "bottom")
# Panel: correlation between scores
cor_res <- cor.test(meta$CKD273_Score, meta$CAAH_Score, method = "spearman")
p_cor <- ggplot(meta, aes(x = CKD273_Score, y = CAAH_Score, colour = get(group_var))) +
geom_point(alpha = 0.3, size = 0.5) +
geom_smooth(method = "lm", colour = "black", linewidth = 0.8, se = FALSE) +
annotate("text", x = min(meta$CKD273_Score), y = max(meta$CAAH_Score),
label = sprintf("rho = %.3f\np = %.2e", cor_res$estimate, cor_res$p.value),
hjust = 0, vjust = 1, size = 3) +
scale_colour_manual(values = c("#E69F00", "#56B4E9")) +
labs(title = paste(model_name, "— signature correlation"),
x = "CKD273 UCell score", y = "CAAH UCell score", colour = group_var) +
theme_classic(base_size = 11) + theme(legend.position = "bottom")
# Panel: scores by cell type
p_celltype <- ggplot(score_long, aes(x = cell_type, y = Score, fill = get(group_var))) +
geom_boxplot(alpha = 0.7, outlier.size = 0.3) +
facet_wrap(~ Signature, ncol = 1) +
scale_fill_manual(values = c("#E69F00", "#56B4E9")) +
labs(title = paste(model_name, "— scores by cell type"),
x = NULL, y = "UCell score", fill = group_var) +
theme_classic(base_size = 11) +
theme(axis.text.x = element_text(angle = 45, hjust = 1),
legend.position = "bottom")
# Panel: Cohen's d per cell type
effect_sizes <- score_long %>%
dplyr::group_by(Signature, cell_type) %>%
dplyr::summarise(
cohens_d = {
g1 <- Score[get(group_var) == unique(get(group_var))[1]]
g2 <- Score[get(group_var) == unique(get(group_var))[2]]
sd_p <- sqrt((var(g1) + var(g2)) / 2)
(mean(g2) - mean(g1)) / sd_p
}, .groups = "drop")
p_effect <- ggplot(effect_sizes, aes(x = cell_type, y = cohens_d, fill = Signature)) +
geom_col(position = "dodge", alpha = 0.8) +
geom_hline(yintercept = 0, linetype = "dashed") +
scale_fill_manual(values = c("CKD273_Score" = "#E69F00", "CAAH_Score" = "#56B4E9")) +
labs(title = paste(model_name, "— Cohen's d by cell type"),
x = NULL, y = "Cohen's d") +
theme_classic(base_size = 11) +
theme(axis.text.x = element_text(angle = 45, hjust = 1),
legend.position = "bottom")
list(roc = p_roc, distributions = p_dist,
correlation = p_cor, celltype = p_celltype, effect_sizes = p_effect)
}
ren1c_plots <- createComparisonPlots(ren1c_obj, ren1c_roc, "genotype", "Ren1c KO")
capt_plots <- createComparisonPlots(captopril_obj, capt_roc, "condition", "Captopril")
for (nm in names(ren1c_plots)) {
ggsave(paste0(parent_dir, "ren1c_CKD273-comparison_", nm, "_", Sys.Date(), ".svg"),
ren1c_plots[[nm]], units = "mm", dpi = 300)
ggsave(paste0(parent_dir, "captopril_CKD273-comparison_", nm, "_", Sys.Date(), ".svg"),
capt_plots[[nm]], units = "mm", dpi = 300)
}
Summary AUC bar chart (both models)
summary_data <- data.frame(
Model = rep(c("Ren1c KO", "Captopril"), each = 2),
Signature = rep(c("CKD273", "CAAH"), 2),
AUC = c(ren1c_roc$auc_ckd, ren1c_roc$auc_caah,
capt_roc$auc_ckd, capt_roc$auc_caah)
)
p_summary <- ggplot(summary_data, aes(x = Model, y = AUC, fill = Signature)) +
geom_col(position = "dodge", alpha = 0.85) +
geom_text(aes(label = sprintf("%.3f", AUC)),
position = position_dodge(0.9), vjust = -0.4, size = 3) +
geom_hline(yintercept = 0.5, linetype = "dashed", colour = "grey50") +
scale_fill_manual(values = c("CKD273" = "#E69F00", "CAAH" = "#56B4E9")) +
ylim(0, 1) +
labs(x = NULL, y = "AUC", fill = NULL) +
theme_classic(base_size = 11) + theme(legend.position = "bottom")
ggsave(paste0(parent_dir, "CKD273-CAAH_summary-AUC_", Sys.Date(), ".svg"),
p_summary, width = 100, height = 90, units = "mm", dpi = 300)