7  Supplementary Figures

7.1 SI Fig 1

7.1.1 Histogram (NPXZ)

Code
#histogram of t test P
#POPSID+proteins+y 
perform_histogram_analysis <- function(data, SIZE, group_var, P_text, axis_number, axis_size, title_size, pvalue_cutoff = 0.05, binwidth = 0.02) {
  data <- data[,-1] #proteins+y 
  # Check if 'y' column exists
  if (!"y" %in% colnames(data)) {
    stop("The dataset does not contain a column named 'y'.")
  }
  # Create a matrix to store p-values and t-test statistics
  pvalue_results = matrix(rep(0, (dim(data)[2] - 1) * 2), ncol = 2)
  rownames(pvalue_results) = colnames(data)[1:(dim(data)[2] - 1)]
  colnames(pvalue_results) = c("P", "ttest")
  # Perform t-tests for each column
  for (i in 1:(dim(data)[2] - 1)) {
    pvalue_results[i, 1] = t.test(data[, i][which(data$y == 1)], 
                                  data[, i][which(data$y == 0)], 
                                  alternative = "two.sided", var.equal = FALSE)$p.value
    pvalue_results[i, 2] = t.test(data[, i][which(data$y == 1)], 
                                  data[, i][which(data$y == 0)], 
                                  alternative = "two.sided", var.equal = FALSE)$statistic  }
  # Convert results to data frame
  pvalue_results_df <- data.frame(pvalue_results)
  # Perform Kolmogorov-Smirnov test on p-values
  ks_test=ks.test(pvalue_results_df$P,"punif") 
  D <- ks_test$statistic # KS test statistic
  n <- length(pvalue_results_df$P)
  # Compute the p-value
  P_input <- pkstwo(sqrt(n) * D) 
  cat("KS test P-value:", P_input, "\n")
  # Count the significant p-values (less than pvalue_cutoff)
  significant_p_count <- length(which(pvalue_results_df$P < pvalue_cutoff))
  cat("Number of records with P < 0.05:", significant_p_count, "\n")
  if (anyDuplicated(pvalue_results_df$P) > 0) {
    warning("Ties detected in P values. KS test results may be affected.")
  }
  y_max <- max(table(cut(pvalue_results_df$P, breaks = 50))) + 
         max(table(cut(pvalue_results_df$P, breaks = 50))) * 0.05
ggplot(pvalue_results_df, aes(x = P)) + 
  geom_histogram(
    binwidth = binwidth, 
    fill = "steelblue2", 
    color = "black", 
    breaks = seq(0, 1, by = binwidth)
  ) + 
  labs(x = "P value", y = "Frequency", 
       title = paste0(group_var)) + 
  annotate("text", 
           x = 0.5,  y = 0.9 * y_max,  
           label = bquote(p ~ "=" ~.(sub("^(.*)e.*", "\\1", format(P_input, scientific = TRUE, digits = 3))) ~ "x" ~ 10^{.(as.integer(sub(".*e\\+?([-+]?[0-9]+)", "\\1", format(P_input, scientific = TRUE, digits = 3))))}),
           size = P_text, 
           color = "black", 
           fontface = "italic") + 
  scale_y_continuous(
    expand = c(0, 0), 
    limits = c(0, max(table(cut(pvalue_results_df$P, breaks = 50))) + 
                 max(table(cut(pvalue_results_df$P, breaks = 50))) * 0.05),
    labels = function(y) ifelse(y == 0, "0", scales::comma(y))  
  ) + 
  scale_x_continuous(
    expand = c(0, 0), 
    limits = c(0, 1.05),
    labels = function(x) ifelse(x == 0, "0", scales::comma(x))  
  ) + 
  theme_Publication() + 
  theme(
    panel.grid.major = element_blank(), 
    panel.grid.minor = element_blank(), 
    axis.title = element_text(size = axis_size, face = "bold"), 
    axis.text = element_text(size = axis_number), 
    plot.title = element_text(size = title_size, face = "bold", hjust = 0.5), 
    axis.line = element_line(color = "black"),
    plot.margin = margin(0.5, 0.5, 0.5, 0.5, "cm"),
    aspect.ratio = 1
  ) + 
  coord_fixed(ratio = SIZE) 
}

7.1.1.1 PE_only 12wk-Supp Fig.1a

KS test P-value: 1.122326e-126 
Number of records with P < 0.05: 487 

7.1.1.2 FGR_only 12wk-Supp Fig.1b

KS test P-value: 4.247767e-120 
Number of records with P < 0.05: 547 

7.1.1.3 PE_with_FGR 12wk-Supp Fig.1c

KS test P-value: 1.591853e-27 
Number of records with P < 0.05: 232 

7.1.1.4 Composite 12wk-Supp Fig.1d

KS test P-value: 1.782131e-203 
Number of records with P < 0.05: 767 

7.2 SI Fig 2:Volcano plot for the composite outcome in the POPS2 validation cohort

7.3 Composite 12wk NPXZ (logistic P)-Fig.d

7.4 SI Fig 3: ROC curve plots of ISM2 and PlGF in the POPS2 validation cohort

POPS2 ISM2 vs PlGF

Code
plot_roc_V3 <- function(data, protein_col, plgf_col, outcome_col, title) {
  # Define colors
  condition_colors <- c(
    "ISM2" = "#56B4E9",
    "PlGF" = "#CC79A7"  )
  df <- data %>%
    dplyr::select(all_of(c(protein_col, plgf_col, outcome_col))) %>%
    dplyr::rename(y = all_of(outcome_col)) %>%
    drop_na()
  # Fit models
  fit_ism2 <- glm(y ~ ., data = df[, c("y", protein_col)], family = binomial)
  fit_plgf <- glm(y ~ ., data = df[, c("y", plgf_col)], family = binomial)
  # Predicted probabilities
  df$pred_ism2 <- fitted(fit_ism2)
  df$pred_plgf <- fitted(fit_plgf)
  # ROC objects
  roc_ism2 <- roc(df$y, df$pred_ism2, quiet = TRUE)
  roc_plgf <- roc(df$y, df$pred_plgf, quiet = TRUE)
  # AUCs
  auc_ism2 <- as.numeric(auc(roc_ism2))
  auc_plgf <- as.numeric(auc(roc_plgf))
  # DeLong test
  delong_p <- roc.test(roc_ism2, roc_plgf, method = "delong")$p.value
  # Labels
  label_ism2 <- sprintf("ISM2 (AUC = %.3f)", auc_ism2)
  label_plgf <- sprintf("PlGF (AUC = %.3f, p = %.3f)", auc_plgf, delong_p)
  roc_df <- rbind(
    data.frame(
      Specificity = roc_ism2$specificities,
      Sensitivity = roc_ism2$sensitivities,
      Protein = "ISM2",
      Label = label_ism2    ),
    data.frame(
      Specificity = roc_plgf$specificities,
      Sensitivity = roc_plgf$sensitivities,
      Protein = "PlGF",
      Label = label_plgf    )  )
  roc_df <- roc_df %>%
    dplyr::distinct(Protein, Specificity, Sensitivity, .keep_all = TRUE) %>%
    dplyr::arrange(Protein, Specificity, Sensitivity)
  p <- ggplot(roc_df,
              aes(x = 1 - Specificity,
                  y = Sensitivity,
                  color = Protein,
                  linetype = Protein)) +
    geom_step(size = 0.6, direction = "hv") +
    geom_abline(slope = 1, intercept = 0,
                linetype = "dashed", color = "gray50") +
    labs(title = title,
         x = "1 - Specificity",
         y = "Sensitivity") +
    scale_x_continuous(limits = c(0, 1), breaks = seq(0, 1, 0.2)) +
    scale_y_continuous(limits = c(0, 1), breaks = seq(0, 1, 0.2)) +
    scale_color_manual(values = condition_colors,
                       labels = c(label_ism2, label_plgf)) +
    scale_linetype_manual(values = c("ISM2" = "solid", "PlGF" = "solid"),
                          labels = c(label_ism2, label_plgf)) +
    theme_Publication() +
    theme(
      legend.position = c(0.7, 0.12), #legend.position = "none",
      legend.title = element_blank(),
      legend.text = element_text(size = 8),
      legend.key.size = unit(0.8, "lines"),
      panel.grid.major = element_blank(),
      panel.grid.minor = element_blank(),
      axis.line = element_line(colour = "black"),
      axis.title = element_text(face = "bold", size = 20),
      axis.text = element_text(size = 16),
      plot.title = element_text(size = 20, face = "bold", hjust = 0.5),
      plot.margin = margin(0.5, 0.5, 0.5, 0.5, "cm"),
      aspect.ratio = 1
    ) +
    coord_fixed(ratio = 1)
  return(list(
    plot = p,
    roc_data = roc_df,
    auc = data.frame(
      Protein = c("ISM2", "PlGF"),
      AUC = c(auc_ism2, auc_plgf)
    ),
    delong_p = delong_p
  ))
}
#composite
df_POPS2_com <- dl.npxz5.pops2.composite.12wk%>%
  dplyr::select(c("ISM2","PGF","y"))
res_pops2 <- plot_roc_V3(
  data = df_POPS2_com,
  protein_col = "ISM2",
  plgf_col = "PGF",
  outcome_col = "y",
  title = " " #"POPS2 Composite Outcome"
)
res_pops2$plot