Add Weibull shape export scripts
This commit is contained in:
553
plot_next_token_to_all_future_auc.R
Normal file
553
plot_next_token_to_all_future_auc.R
Normal file
@@ -0,0 +1,553 @@
|
||||
#!/usr/bin/env Rscript
|
||||
|
||||
# Paper-grade single-panel figures supporting the conclusion that fixed-landmark
|
||||
# horizon evaluation favors all_future over next_token.
|
||||
#
|
||||
# Outputs are written as separate panel files. This script intentionally does not
|
||||
# combine panels with plot_grid().
|
||||
|
||||
suppressPackageStartupMessages({
|
||||
library(cowplot)
|
||||
library(dplyr)
|
||||
library(ggplot2)
|
||||
library(jsonlite)
|
||||
library(readr)
|
||||
library(stringr)
|
||||
library(tibble)
|
||||
library(tidyr)
|
||||
})
|
||||
|
||||
root_dir <- "."
|
||||
runs_dir <- file.path(root_dir, "runs")
|
||||
out_dir <- file.path(root_dir, "figures_next_token_to_all_future_absolute_smoking_alcohol_bmi")
|
||||
dir.create(out_dir, showWarnings = FALSE, recursive = TRUE)
|
||||
|
||||
required_time_mode <- "absolute"
|
||||
required_extra_info_types <- c(11L, 66L, 67L)
|
||||
required_extra_info_signature <- paste(sort(required_extra_info_types), collapse = ",")
|
||||
|
||||
theme_set(
|
||||
theme_cowplot(font_size = 9) +
|
||||
theme(
|
||||
plot.background = element_rect(fill = "white", color = NA),
|
||||
panel.background = element_rect(fill = "white", color = NA),
|
||||
legend.background = element_rect(fill = "white", color = NA),
|
||||
legend.key = element_rect(fill = "white", color = NA)
|
||||
)
|
||||
)
|
||||
|
||||
target_cols <- c(
|
||||
"next_token" = "#B54A3A",
|
||||
"all_future" = "#2C7FB8"
|
||||
)
|
||||
|
||||
dist_shapes <- c(
|
||||
"exponential" = 16,
|
||||
"weibull" = 17,
|
||||
"mixed" = 15
|
||||
)
|
||||
|
||||
read_run_config <- function(run_path) {
|
||||
cfg_path <- file.path(run_path, "train_config.json")
|
||||
if (!file.exists(cfg_path)) return(NULL)
|
||||
cfg <- jsonlite::read_json(cfg_path, simplifyVector = TRUE)
|
||||
extra_info_types <- cfg$extra_info_types %||% integer(0)
|
||||
extra_info_signature <- paste(sort(as.integer(extra_info_types)), collapse = ",")
|
||||
tibble(
|
||||
run = basename(run_path),
|
||||
model_target_mode = as.character(cfg$model_target_mode %||% NA_character_),
|
||||
target_mode = as.character(cfg$target_mode %||% NA_character_),
|
||||
dist_mode = as.character(cfg$dist_mode %||% NA_character_),
|
||||
time_mode = as.character(cfg$time_mode %||% NA_character_),
|
||||
readout_name = as.character(cfg$readout_name %||% NA_character_),
|
||||
attn_mask_mode = as.character(cfg$attn_mask_mode %||% NA_character_),
|
||||
extra_info_signature = extra_info_signature
|
||||
)
|
||||
}
|
||||
|
||||
`%||%` <- function(x, y) {
|
||||
if (is.null(x) || length(x) == 0) y else x
|
||||
}
|
||||
|
||||
load_one_result <- function(run_path, file_name, eval_family) {
|
||||
cfg <- read_run_config(run_path)
|
||||
if (is.null(cfg)) return(NULL)
|
||||
fp <- file.path(run_path, file_name)
|
||||
if (!file.exists(fp)) return(NULL)
|
||||
|
||||
df <- suppressMessages(readr::read_csv(fp, show_col_types = FALSE))
|
||||
if (!("auc" %in% names(df)) || nrow(df) == 0) return(NULL)
|
||||
|
||||
out <- df %>%
|
||||
mutate(
|
||||
run = basename(run_path),
|
||||
eval_family = eval_family,
|
||||
auc = as.numeric(auc)
|
||||
) %>%
|
||||
left_join(cfg, by = "run", suffix = c("", "_cfg"))
|
||||
|
||||
coalesce_joined <- function(data, col) {
|
||||
cfg_col <- paste0(col, "_cfg")
|
||||
if (col %in% names(data) && cfg_col %in% names(data)) {
|
||||
dplyr::coalesce(data[[col]], data[[cfg_col]])
|
||||
} else if (col %in% names(data)) {
|
||||
data[[col]]
|
||||
} else if (cfg_col %in% names(data)) {
|
||||
data[[cfg_col]]
|
||||
} else {
|
||||
rep(NA_character_, nrow(data))
|
||||
}
|
||||
}
|
||||
|
||||
for (col in c("model_target_mode", "target_mode", "dist_mode", "time_mode", "readout_name", "attn_mask_mode")) {
|
||||
out[[col]] <- coalesce_joined(out, col)
|
||||
}
|
||||
|
||||
out %>%
|
||||
select(-any_of(c(
|
||||
"model_target_mode_cfg", "target_mode_cfg", "dist_mode_cfg",
|
||||
"time_mode_cfg", "readout_name_cfg", "attn_mask_mode_cfg"
|
||||
)))
|
||||
}
|
||||
|
||||
run_paths <- list.dirs(runs_dir, recursive = FALSE, full.names = TRUE)
|
||||
|
||||
landmark_auc <- bind_rows(lapply(
|
||||
run_paths,
|
||||
load_one_result,
|
||||
file_name = "df_auc_landmark.csv",
|
||||
eval_family = "Fixed landmark + horizon"
|
||||
)) %>%
|
||||
filter(time_mode == "absolute")
|
||||
|
||||
token_auc <- bind_rows(lapply(
|
||||
run_paths,
|
||||
load_one_result,
|
||||
file_name = "df_both.csv",
|
||||
eval_family = "Delphi2M-style token"
|
||||
)) %>%
|
||||
filter(time_mode == "absolute")
|
||||
|
||||
if (nrow(landmark_auc) == 0) {
|
||||
stop("No landmark AUC files found under runs/*/df_auc_landmark.csv")
|
||||
}
|
||||
if (nrow(token_auc) == 0) {
|
||||
stop("No token AUC files found under runs/*/df_both.csv")
|
||||
}
|
||||
|
||||
landmark_auc <- landmark_auc %>%
|
||||
filter(
|
||||
time_mode == required_time_mode,
|
||||
extra_info_signature == required_extra_info_signature
|
||||
)
|
||||
|
||||
token_auc <- token_auc %>%
|
||||
filter(
|
||||
time_mode == required_time_mode,
|
||||
extra_info_signature == required_extra_info_signature
|
||||
)
|
||||
|
||||
if (nrow(landmark_auc) == 0 || nrow(token_auc) == 0) {
|
||||
stop(
|
||||
"No AUC rows remain after filtering for time_mode='",
|
||||
required_time_mode,
|
||||
"' and extra_info_types='",
|
||||
required_extra_info_signature,
|
||||
"'."
|
||||
)
|
||||
}
|
||||
|
||||
message(
|
||||
"Using runs with time_mode='", required_time_mode,
|
||||
"' and extra_info_types='", required_extra_info_signature, "':"
|
||||
)
|
||||
print(sort(unique(landmark_auc$run)))
|
||||
|
||||
classify_endpoint <- function(data) {
|
||||
data %>%
|
||||
mutate(
|
||||
endpoint_type = if_else(
|
||||
str_to_lower(as.character(label_code)) == "death",
|
||||
"Death",
|
||||
"Non-death disease"
|
||||
),
|
||||
endpoint_type = factor(endpoint_type, levels = c("Non-death disease", "Death"))
|
||||
)
|
||||
}
|
||||
|
||||
landmark_auc <- classify_endpoint(landmark_auc)
|
||||
token_auc <- classify_endpoint(token_auc)
|
||||
|
||||
landmark_auc_disease <- landmark_auc %>% filter(endpoint_type == "Non-death disease")
|
||||
token_auc_disease <- token_auc %>% filter(endpoint_type == "Non-death disease")
|
||||
landmark_auc_death <- landmark_auc %>% filter(endpoint_type == "Death")
|
||||
token_auc_death <- token_auc %>% filter(endpoint_type == "Death")
|
||||
|
||||
if (nrow(landmark_auc_death) == 0 || nrow(token_auc_death) == 0) {
|
||||
warning("Death rows were not found in one or both AUC tables.")
|
||||
}
|
||||
|
||||
auc_all <- bind_rows(
|
||||
landmark_auc_disease %>% mutate(horizon = as.numeric(horizon), offset = NA_real_),
|
||||
token_auc_disease %>% mutate(horizon = NA_real_, offset = as.numeric(offset))
|
||||
) %>%
|
||||
mutate(
|
||||
model_target_mode = factor(model_target_mode, levels = c("next_token", "all_future")),
|
||||
eval_family = factor(eval_family, levels = c("Delphi2M-style token", "Fixed landmark + horizon")),
|
||||
dist_mode = factor(dist_mode, levels = c("exponential", "weibull", "mixed")),
|
||||
model_label = recode(
|
||||
as.character(model_target_mode),
|
||||
"next_token" = "next-token objective",
|
||||
"all_future" = "all-future objective"
|
||||
)
|
||||
)
|
||||
|
||||
mean_ci <- function(x) {
|
||||
x <- x[is.finite(x)]
|
||||
n <- length(x)
|
||||
m <- mean(x)
|
||||
se <- sd(x) / sqrt(n)
|
||||
tibble(mean = m, ymin = m - 1.96 * se, ymax = m + 1.96 * se, n = n)
|
||||
}
|
||||
|
||||
save_panel <- function(plot, name, width = 3.6, height = 3.0) {
|
||||
pdf_path <- file.path(out_dir, paste0(name, ".pdf"))
|
||||
png_path <- file.path(out_dir, paste0(name, ".png"))
|
||||
cowplot::save_plot(pdf_path, plot, base_width = width, base_height = height, bg = "white")
|
||||
cowplot::save_plot(png_path, plot, base_width = width, base_height = height, dpi = 600, bg = "white")
|
||||
message("Wrote: ", pdf_path)
|
||||
message("Wrote: ", png_path)
|
||||
}
|
||||
|
||||
# Panel 1: run-level mean AUC under the clinically aligned landmark/horizon task.
|
||||
# Death is excluded here and plotted separately below.
|
||||
landmark_run <- landmark_auc_disease %>%
|
||||
mutate(model_target_mode = factor(model_target_mode, levels = c("next_token", "all_future"))) %>%
|
||||
group_by(run, model_target_mode, dist_mode, time_mode, target_mode) %>%
|
||||
summarise(mean_auc = mean(auc, na.rm = TRUE), median_auc = median(auc, na.rm = TRUE), .groups = "drop")
|
||||
|
||||
landmark_summary <- landmark_run %>%
|
||||
group_by(model_target_mode) %>%
|
||||
summarise(mean_ci(mean_auc), .groups = "drop")
|
||||
|
||||
p1 <- ggplot(landmark_run, aes(x = model_target_mode, y = mean_auc)) +
|
||||
geom_point(
|
||||
aes(color = model_target_mode, shape = dist_mode),
|
||||
position = position_jitter(width = 0.09, height = 0, seed = 1),
|
||||
size = 2.2,
|
||||
alpha = 0.88
|
||||
) +
|
||||
geom_errorbar(
|
||||
data = landmark_summary,
|
||||
aes(x = model_target_mode, y = mean, ymin = ymin, ymax = ymax, color = model_target_mode),
|
||||
width = 0.12,
|
||||
linewidth = 0.55,
|
||||
inherit.aes = FALSE
|
||||
) +
|
||||
geom_point(
|
||||
data = landmark_summary,
|
||||
aes(x = model_target_mode, y = mean, color = model_target_mode),
|
||||
size = 3.4,
|
||||
inherit.aes = FALSE
|
||||
) +
|
||||
scale_color_manual(values = target_cols, guide = "none") +
|
||||
scale_shape_manual(values = dist_shapes, na.translate = FALSE) +
|
||||
scale_x_discrete(labels = c("next_token", "all_future")) +
|
||||
coord_cartesian(ylim = c(0.58, 0.78)) +
|
||||
labs(
|
||||
x = NULL,
|
||||
y = "Mean AUC per run",
|
||||
shape = "Risk head",
|
||||
title = "Non-death landmark AUC (absolute time)"
|
||||
) +
|
||||
theme(
|
||||
plot.title = element_text(face = "bold", size = 10),
|
||||
axis.text.x = element_text(size = 9),
|
||||
legend.position = c(0.72, 0.20),
|
||||
legend.background = element_blank()
|
||||
)
|
||||
|
||||
save_panel(p1, "panel_01_landmark_overall")
|
||||
|
||||
# Panel 2: landmark AUC by prediction horizon.
|
||||
landmark_horizon_run <- landmark_auc_disease %>%
|
||||
mutate(
|
||||
horizon = as.numeric(horizon),
|
||||
model_target_mode = factor(model_target_mode, levels = c("next_token", "all_future"))
|
||||
) %>%
|
||||
group_by(run, model_target_mode, horizon) %>%
|
||||
summarise(mean_auc = mean(auc, na.rm = TRUE), .groups = "drop")
|
||||
|
||||
landmark_horizon_summary <- landmark_horizon_run %>%
|
||||
group_by(model_target_mode, horizon) %>%
|
||||
summarise(mean_ci(mean_auc), .groups = "drop")
|
||||
|
||||
p2 <- ggplot(landmark_horizon_run, aes(x = horizon, y = mean_auc, color = model_target_mode)) +
|
||||
geom_line(aes(group = run), alpha = 0.18, linewidth = 0.35) +
|
||||
geom_point(alpha = 0.32, size = 1.1) +
|
||||
geom_ribbon(
|
||||
data = landmark_horizon_summary,
|
||||
aes(x = horizon, y = mean, ymin = ymin, ymax = ymax, fill = model_target_mode, group = model_target_mode),
|
||||
alpha = 0.13,
|
||||
color = NA,
|
||||
inherit.aes = FALSE
|
||||
) +
|
||||
geom_line(data = landmark_horizon_summary, aes(y = mean), linewidth = 0.85) +
|
||||
geom_point(data = landmark_horizon_summary, aes(y = mean), size = 2.0) +
|
||||
scale_color_manual(
|
||||
values = target_cols,
|
||||
labels = c("next_token", "all_future"),
|
||||
name = NULL
|
||||
) +
|
||||
scale_fill_manual(values = target_cols, guide = "none") +
|
||||
scale_x_continuous(breaks = c(1, 5, 10)) +
|
||||
coord_cartesian(ylim = c(0.58, 0.78)) +
|
||||
labs(
|
||||
x = "Prediction horizon, years",
|
||||
y = "Mean AUC per run",
|
||||
title = "Non-death landmark AUC across horizons"
|
||||
) +
|
||||
theme(
|
||||
plot.title = element_text(face = "bold", size = 10),
|
||||
legend.position = c(0.31, 0.20),
|
||||
legend.background = element_blank()
|
||||
)
|
||||
|
||||
save_panel(p2, "panel_02_landmark_by_horizon", width = 3.8, height = 3.0)
|
||||
|
||||
# Panel 3: Delphi2M-style token AUC by offset. This documents why the old
|
||||
# evaluation can make next_token look competitive, especially near the event.
|
||||
token_offset_run <- token_auc_disease %>%
|
||||
mutate(
|
||||
offset = as.numeric(offset),
|
||||
model_target_mode = factor(model_target_mode, levels = c("next_token", "all_future"))
|
||||
) %>%
|
||||
group_by(run, model_target_mode, offset) %>%
|
||||
summarise(mean_auc = mean(auc, na.rm = TRUE), .groups = "drop")
|
||||
|
||||
token_offset_summary <- token_offset_run %>%
|
||||
group_by(model_target_mode, offset) %>%
|
||||
summarise(mean_ci(mean_auc), .groups = "drop")
|
||||
|
||||
p3 <- ggplot(token_offset_run, aes(x = offset, y = mean_auc, color = model_target_mode)) +
|
||||
geom_line(aes(group = run), alpha = 0.18, linewidth = 0.35) +
|
||||
geom_point(alpha = 0.32, size = 1.1) +
|
||||
geom_ribbon(
|
||||
data = token_offset_summary,
|
||||
aes(x = offset, y = mean, ymin = ymin, ymax = ymax, fill = model_target_mode, group = model_target_mode),
|
||||
alpha = 0.13,
|
||||
color = NA,
|
||||
inherit.aes = FALSE
|
||||
) +
|
||||
geom_line(data = token_offset_summary, aes(y = mean), linewidth = 0.85) +
|
||||
geom_point(data = token_offset_summary, aes(y = mean), size = 2.0) +
|
||||
scale_color_manual(
|
||||
values = target_cols,
|
||||
labels = c("next_token", "all_future"),
|
||||
name = NULL
|
||||
) +
|
||||
scale_fill_manual(values = target_cols, guide = "none") +
|
||||
scale_x_continuous(breaks = c(0.1, 1, 5, 10), trans = "log10") +
|
||||
coord_cartesian(ylim = c(0.55, 0.82)) +
|
||||
labs(
|
||||
x = "Minimum offset before event, years",
|
||||
y = "Mean AUC per run",
|
||||
title = "Non-death token AUC by offset"
|
||||
) +
|
||||
theme(
|
||||
plot.title = element_text(face = "bold", size = 10),
|
||||
legend.position = c(0.31, 0.20),
|
||||
legend.background = element_blank()
|
||||
)
|
||||
|
||||
save_panel(p3, "panel_03_token_auc_by_offset", width = 3.8, height = 3.0)
|
||||
|
||||
# Panel 4: within-run contrast between old token evaluation and landmark
|
||||
# evaluation. Each run contributes one point per evaluation family.
|
||||
run_eval_contrast <- auc_all %>%
|
||||
group_by(run, model_target_mode, dist_mode, eval_family) %>%
|
||||
summarise(mean_auc = mean(auc, na.rm = TRUE), .groups = "drop")
|
||||
|
||||
p4 <- ggplot(run_eval_contrast, aes(x = eval_family, y = mean_auc, color = model_target_mode)) +
|
||||
geom_line(aes(group = run), alpha = 0.34, linewidth = 0.45) +
|
||||
geom_point(aes(shape = dist_mode), size = 2.0, alpha = 0.84) +
|
||||
stat_summary(
|
||||
aes(group = model_target_mode),
|
||||
fun = mean,
|
||||
geom = "point",
|
||||
size = 3.3,
|
||||
shape = 18,
|
||||
position = position_dodge(width = 0.16)
|
||||
) +
|
||||
scale_color_manual(
|
||||
values = target_cols,
|
||||
labels = c("next_token", "all_future"),
|
||||
name = NULL
|
||||
) +
|
||||
scale_shape_manual(values = dist_shapes, na.translate = FALSE, name = "Risk head") +
|
||||
coord_cartesian(ylim = c(0.58, 0.78)) +
|
||||
labs(
|
||||
x = NULL,
|
||||
y = "Mean AUC per run",
|
||||
title = "Evaluation choice changes the conclusion (absolute time)"
|
||||
) +
|
||||
theme(
|
||||
plot.title = element_text(face = "bold", size = 10),
|
||||
axis.text.x = element_text(angle = 18, hjust = 1),
|
||||
legend.position = "right"
|
||||
)
|
||||
|
||||
save_panel(p4, "panel_04_evaluation_contrast", width = 4.3, height = 3.1)
|
||||
|
||||
# Panel 5: disease-level distribution for the landmark task, pooled over
|
||||
# horizons and runs. This shows the shift without hiding heterogeneity.
|
||||
landmark_density <- landmark_auc_disease %>%
|
||||
mutate(model_target_mode = factor(model_target_mode, levels = c("next_token", "all_future"))) %>%
|
||||
filter(is.finite(auc))
|
||||
|
||||
p5 <- ggplot(landmark_density, aes(x = auc, fill = model_target_mode, color = model_target_mode)) +
|
||||
geom_density(alpha = 0.20, linewidth = 0.65, adjust = 1.1) +
|
||||
geom_vline(
|
||||
data = landmark_density %>%
|
||||
group_by(model_target_mode) %>%
|
||||
summarise(mean_auc = mean(auc), .groups = "drop"),
|
||||
aes(xintercept = mean_auc, color = model_target_mode),
|
||||
linewidth = 0.75,
|
||||
linetype = "22"
|
||||
) +
|
||||
scale_color_manual(values = target_cols, labels = c("next_token", "all_future"), name = NULL) +
|
||||
scale_fill_manual(values = target_cols, labels = c("next_token", "all_future"), name = NULL) +
|
||||
coord_cartesian(xlim = c(0.35, 1.0)) +
|
||||
labs(
|
||||
x = "AUC",
|
||||
y = "Density",
|
||||
title = "Non-death landmark AUC distribution"
|
||||
) +
|
||||
theme(
|
||||
plot.title = element_text(face = "bold", size = 10),
|
||||
legend.position = c(0.24, 0.82),
|
||||
legend.background = element_blank()
|
||||
)
|
||||
|
||||
save_panel(p5, "panel_05_landmark_auc_distribution", width = 3.8, height = 3.0)
|
||||
|
||||
# Panel 6: death-only fixed landmark + horizon AUC. Death has one endpoint token,
|
||||
# so each line is a run trajectory across horizons.
|
||||
death_landmark_run <- landmark_auc_death %>%
|
||||
mutate(
|
||||
horizon = as.numeric(horizon),
|
||||
model_target_mode = factor(model_target_mode, levels = c("next_token", "all_future")),
|
||||
dist_mode = factor(dist_mode, levels = c("exponential", "weibull", "mixed"))
|
||||
) %>%
|
||||
group_by(run, model_target_mode, dist_mode, horizon) %>%
|
||||
summarise(mean_auc = mean(auc, na.rm = TRUE), .groups = "drop")
|
||||
|
||||
death_landmark_summary <- death_landmark_run %>%
|
||||
group_by(model_target_mode, horizon) %>%
|
||||
summarise(mean_ci(mean_auc), .groups = "drop")
|
||||
|
||||
p6 <- ggplot(death_landmark_run, aes(x = horizon, y = mean_auc, color = model_target_mode)) +
|
||||
geom_line(aes(group = run), alpha = 0.42, linewidth = 0.45) +
|
||||
geom_point(aes(shape = dist_mode), alpha = 0.9, size = 2.0) +
|
||||
geom_line(data = death_landmark_summary, aes(y = mean, group = model_target_mode), linewidth = 0.9) +
|
||||
geom_point(data = death_landmark_summary, aes(y = mean), size = 2.2) +
|
||||
scale_color_manual(values = target_cols, labels = c("next_token", "all_future"), name = NULL) +
|
||||
scale_shape_manual(values = dist_shapes, na.translate = FALSE, name = "Risk head") +
|
||||
scale_x_continuous(breaks = c(1, 5, 10)) +
|
||||
coord_cartesian(ylim = c(0.58, 0.95)) +
|
||||
labs(
|
||||
x = "Prediction horizon, years",
|
||||
y = "AUC",
|
||||
title = "Death-only landmark AUC"
|
||||
) +
|
||||
theme(
|
||||
plot.title = element_text(face = "bold", size = 10),
|
||||
legend.position = "right"
|
||||
)
|
||||
|
||||
save_panel(p6, "panel_06_death_landmark_by_horizon", width = 3.9, height = 3.0)
|
||||
|
||||
# Panel 7: death-only Delphi2M-style token AUC by offset.
|
||||
death_token_run <- token_auc_death %>%
|
||||
mutate(
|
||||
offset = as.numeric(offset),
|
||||
model_target_mode = factor(model_target_mode, levels = c("next_token", "all_future")),
|
||||
dist_mode = factor(dist_mode, levels = c("exponential", "weibull", "mixed"))
|
||||
) %>%
|
||||
group_by(run, model_target_mode, dist_mode, offset) %>%
|
||||
summarise(mean_auc = mean(auc, na.rm = TRUE), .groups = "drop")
|
||||
|
||||
death_token_summary <- death_token_run %>%
|
||||
group_by(model_target_mode, offset) %>%
|
||||
summarise(mean_ci(mean_auc), .groups = "drop")
|
||||
|
||||
p7 <- ggplot(death_token_run, aes(x = offset, y = mean_auc, color = model_target_mode)) +
|
||||
geom_line(aes(group = run), alpha = 0.42, linewidth = 0.45) +
|
||||
geom_point(aes(shape = dist_mode), alpha = 0.9, size = 2.0) +
|
||||
geom_line(data = death_token_summary, aes(y = mean, group = model_target_mode), linewidth = 0.9) +
|
||||
geom_point(data = death_token_summary, aes(y = mean), size = 2.2) +
|
||||
scale_color_manual(values = target_cols, labels = c("next_token", "all_future"), name = NULL) +
|
||||
scale_shape_manual(values = dist_shapes, na.translate = FALSE, name = "Risk head") +
|
||||
scale_x_continuous(breaks = c(0.1, 1, 5, 10), trans = "log10") +
|
||||
coord_cartesian(ylim = c(0.58, 0.95)) +
|
||||
labs(
|
||||
x = "Minimum offset before event, years",
|
||||
y = "AUC",
|
||||
title = "Death-only token AUC"
|
||||
) +
|
||||
theme(
|
||||
plot.title = element_text(face = "bold", size = 10),
|
||||
legend.position = "right"
|
||||
)
|
||||
|
||||
save_panel(p7, "panel_07_death_token_auc_by_offset", width = 3.9, height = 3.0)
|
||||
|
||||
# Panel 8: death-only contrast between the two evaluation families.
|
||||
death_eval_contrast <- bind_rows(
|
||||
landmark_auc_death %>% mutate(horizon = as.numeric(horizon), offset = NA_real_),
|
||||
token_auc_death %>% mutate(horizon = NA_real_, offset = as.numeric(offset))
|
||||
) %>%
|
||||
mutate(
|
||||
model_target_mode = factor(model_target_mode, levels = c("next_token", "all_future")),
|
||||
eval_family = factor(eval_family, levels = c("Delphi2M-style token", "Fixed landmark + horizon")),
|
||||
dist_mode = factor(dist_mode, levels = c("exponential", "weibull", "mixed"))
|
||||
) %>%
|
||||
group_by(run, model_target_mode, dist_mode, eval_family) %>%
|
||||
summarise(mean_auc = mean(auc, na.rm = TRUE), .groups = "drop")
|
||||
|
||||
p8 <- ggplot(death_eval_contrast, aes(x = eval_family, y = mean_auc, color = model_target_mode)) +
|
||||
geom_line(aes(group = run), alpha = 0.38, linewidth = 0.5) +
|
||||
geom_point(aes(shape = dist_mode), size = 2.2, alpha = 0.9) +
|
||||
stat_summary(
|
||||
aes(group = model_target_mode),
|
||||
fun = mean,
|
||||
geom = "point",
|
||||
size = 3.4,
|
||||
shape = 18,
|
||||
position = position_dodge(width = 0.16)
|
||||
) +
|
||||
scale_color_manual(values = target_cols, labels = c("next_token", "all_future"), name = NULL) +
|
||||
scale_shape_manual(values = dist_shapes, na.translate = FALSE, name = "Risk head") +
|
||||
coord_cartesian(ylim = c(0.58, 0.95)) +
|
||||
labs(
|
||||
x = NULL,
|
||||
y = "Mean AUC per run",
|
||||
title = "Death endpoint evaluated separately"
|
||||
) +
|
||||
theme(
|
||||
plot.title = element_text(face = "bold", size = 10),
|
||||
axis.text.x = element_text(angle = 18, hjust = 1),
|
||||
legend.position = "right"
|
||||
)
|
||||
|
||||
save_panel(p8, "panel_08_death_evaluation_contrast", width = 4.3, height = 3.1)
|
||||
|
||||
# Export the exact run-level summaries used by the figures.
|
||||
readr::write_csv(landmark_run, file.path(out_dir, "landmark_run_summary.csv"))
|
||||
readr::write_csv(token_offset_run, file.path(out_dir, "token_offset_run_summary.csv"))
|
||||
readr::write_csv(run_eval_contrast, file.path(out_dir, "run_evaluation_contrast.csv"))
|
||||
readr::write_csv(death_landmark_run, file.path(out_dir, "death_landmark_run_summary.csv"))
|
||||
readr::write_csv(death_token_run, file.path(out_dir, "death_token_offset_run_summary.csv"))
|
||||
readr::write_csv(death_eval_contrast, file.path(out_dir, "death_evaluation_contrast.csv"))
|
||||
|
||||
message("Done. Panels are in: ", normalizePath(out_dir, winslash = "/"))
|
||||
Reference in New Issue
Block a user