Main Figure: Gene Set Trajectories
This section creates the two-panel figure showing normalized gene set indices and individual gene expression patterns (spaghetti plots).
Prepare Data for Spaghetti Plots
Calculate gene-centered expression for all genes in each set to visualize individual gene trajectories.
# Prepare expression data for photosynthesis genes - centered per gene
xp_photo_genes <- intersect(
custom_annotation$photosynthesis,
rownames(normalized_expression)
)
xp_photosynthesis <- normalized_expression[xp_photo_genes, ] %>%
as.data.frame() %>%
tibble::rownames_to_column("gene") %>%
pivot_longer(-gene, names_to = "sample", values_to = "expression") %>%
mutate(
leaf_stage = as.numeric(sub(".*L([0-9]+)[LR]$", "\\1", sample))
) %>%
group_by(gene) %>%
mutate(centered_expr = expression - mean(expression)) %>%
ungroup() %>%
group_by(gene, leaf_stage) %>%
summarise(mean_expr = mean(centered_expr), .groups = "drop") %>%
arrange(gene) %>%
mutate(gene_set = "Photosynthesis")
# Prepare expression data for senescence genes - centered per gene
xp_senescence_genes <- intersect(
custom_annotation$leaf_senescence,
rownames(normalized_expression)
)
xp_senescence <- normalized_expression[xp_senescence_genes, ] %>%
as.data.frame() %>%
tibble::rownames_to_column("gene") %>%
pivot_longer(-gene, names_to = "sample", values_to = "expression") %>%
mutate(
leaf_stage = as.numeric(sub(".*L([0-9]+)[LR]$", "\\1", sample))
) %>%
group_by(gene) %>%
mutate(centered_expr = expression - mean(expression)) %>%
ungroup() %>%
group_by(gene, leaf_stage) %>%
summarise(mean_expr = mean(centered_expr), .groups = "drop") %>%
arrange(gene) %>%
mutate(gene_set = "Senescence")
cat("Photosynthesis genes:", length(xp_photo_genes), "\n")
## Photosynthesis genes: 525
cat("Senescence genes:", length(xp_senescence_genes), "\n")
## Senescence genes: 157
Panel A: Normalized Gene Set Indices
Normalize indices to 0-1 scale and fit linear models to quantify temporal trends.
# Normalize indices
indices_normalized <- indices %>%
mutate(
photosynthesis = (photosynthesis - min(photosynthesis)) /
(max(photosynthesis) - min(photosynthesis)),
senescence = (senescence - min(senescence)) /
(max(senescence) - min(senescence))
)
cat("=== Photosynthesis Index (Normalized) ===\n")
## === Photosynthesis Index (Normalized) ===
lm_photo <- lm(photosynthesis ~ leaf_stage, data = indices_normalized)
summary(lm_photo) %>% print()
##
## Call:
## lm(formula = photosynthesis ~ leaf_stage, data = indices_normalized)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.44587 -0.07017 0.00795 0.10089 0.19383
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 0.97702 0.04848 20.154 < 2e-16 ***
## leaf_stage -0.13279 0.01794 -7.402 4.49e-09 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.1302 on 41 degrees of freedom
## Multiple R-squared: 0.572, Adjusted R-squared: 0.5615
## F-statistic: 54.79 on 1 and 41 DF, p-value: 4.486e-09
cat("\n=== Senescence Index (Normalized) ===\n")
##
## === Senescence Index (Normalized) ===
lm_sen <- lm(senescence ~ leaf_stage, data = indices_normalized)
summary(lm_sen) %>% print()
##
## Call:
## lm(formula = senescence ~ leaf_stage, data = indices_normalized)
##
## Residuals:
## Min 1Q Median 3Q Max
## -0.45703 -0.15322 0.00116 0.11296 0.49468
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 0.15554 0.07944 1.958 0.057056 .
## leaf_stage 0.11659 0.02940 3.966 0.000286 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.2134 on 41 degrees of freedom
## Multiple R-squared: 0.2773, Adjusted R-squared: 0.2597
## F-statistic: 15.73 on 1 and 41 DF, p-value: 0.0002864
# Extract regression statistics
photo_stats <- summary(lm_photo)
sen_stats <- summary(lm_sen)
photo_r2 <- photo_stats$r.squared
photo_p <- coef(photo_stats)["leaf_stage", "Pr(>|t|)"]
sen_r2 <- sen_stats$r.squared
sen_p <- coef(sen_stats)["leaf_stage", "Pr(>|t|)"]
# Format p-values
format_pval <- function(p) {
if (p < 0.001) return("p < 0.001")
if (p < 0.01) return(sprintf("p = %.3f", p))
return(sprintf("p = %.2f", p))
}
# Create annotation text
stats_text <- sprintf(
"R² = %.3f\n %s\n\n\nR² = %.3f\n %s",
photo_r2, format_pval(photo_p),
sen_r2, format_pval(sen_p)
)
# Summary statistics for normalized indices
indices_summary <- indices_normalized %>%
group_by(leaf_stage) %>%
summarise(
photo_mean = mean(photosynthesis),
photo_se = sd(photosynthesis) / sqrt(n()),
senescence_mean = mean(senescence),
senescence_se = sd(senescence) / sqrt(n()),
.groups = "drop"
) %>%
pivot_longer(
cols = c(photo_mean, senescence_mean),
names_to = "index_type",
values_to = "mean"
) %>%
mutate(
se = ifelse(index_type == "photo_mean", photo_se, senescence_se),
index_type = factor(
index_type,
levels = c("photo_mean", "senescence_mean"),
labels = c("Photosynthesis", "Leaf\nSenescence")
)
)
# Normalized trajectories plot
base_size <- 30
base_family <- "Helvetica"
p_normalized <- ggplot(
indices_summary,
aes(x = leaf_stage, y = mean, color = index_type)
) +
geom_line(linewidth = 1.5) +
geom_point(size = 4) +
geom_errorbar(
aes(ymin = mean - se, ymax = mean + se),
width = 0.2,
linewidth = 1
) +
scale_color_manual(
values = c("Photosynthesis" = "darkgreen", "Leaf\nSenescence" = "orange")
) +
annotate(
"text",
x = 1.0,
y = 0.45,
label = stats_text,
hjust = 0,
vjust = 0,
size = 4
) +
labs(
x = "Leaf",
y = "Normalized Expression Index",
color = NULL
) +
ggpubr::theme_classic2(base_size = base_size) +
theme(
legend.position = c(0.6, 0.9),
legend.text = element_text(size = 20),
legend.background = element_rect(fill = "transparent"),
legend.key = element_blank(),
# plot.margin = margin(5.5, 7, 70, 5.5, "pt")
plot.margin = margin(5.5, 5.5, 50, 7, "pt")
)
print(p_normalized)
Panel B: Spaghetti Plot
Show individual gene trajectories (centered expression) with linear trends for each gene set.
p_spaghetti <- xp_photosynthesis %>%
arrange(gene_set) %>%
ggplot(aes(x = leaf_stage, y = mean_expr, group = gene) ) +
geom_line(
alpha = 0.1,
linewidth = 0.5,
color = "darkgreen"
) +
geom_smooth(
data = xp_photosynthesis,
aes(x = leaf_stage, y = mean_expr, group = 1),
method = "lm",
se = FALSE,
linewidth = 2,
color = "darkgreen"
) +
geom_line(
data= xp_senescence,
aes(x = leaf_stage, y = mean_expr, group = gene),
alpha = 0.1,
linewidth = 0.5,
color = "orange"
) +
geom_smooth(
data = xp_senescence,
aes(x = leaf_stage, y = mean_expr, group = 1),
method = "lm",
se = FALSE,
linewidth = 2,
color = "orange"
) +
labs(
x = "Leaf",
y = expression("Centered " * log[10] * "(CPM)")
) +
coord_cartesian(ylim = c(-1, 1)) +
scale_y_continuous(position = "right") +
ggpubr::theme_classic2(base_size = base_size) +
theme(
axis.title.y.right = element_text(margin = margin(l = -5)),
plot.margin = margin(5.5, 7, 50, 5.5, "pt")
)
print(p_spaghetti)
Combine Top Panels
Create the two-panel figure showing both normalized indices and spaghetti plots side-by-side.
p_top_panel <- ggarrange(
p_normalized + rremove("xlab"),
p_spaghetti + rremove("xlab"), ncol = 2) %>%
annotate_figure(
bottom = text_grob("Leaf", size = base_size,vjust = -2),
)