library(tidyverse)
library(lubridate)
library(lme4)
library(lmerTest)
library(MuMIn)
library(glmmTMB)
library(emmeans)
library(broom)
library(broom.mixed)
library(vegan)
library(RColorBrewer)
library(viridis)
library(gridExtra)
library(patchwork)
library(GGally)
library(ggnewscale)
library(knitr)
knitr::opts_chunk$set(comment="", cache=T, warning = F, message = F,
fig.path = "figures/",
dev="svglite", dev.args=list(fix_text_size=FALSE))#dev="cairo_pdf")
options(digits=4) # for kables
load("data/maxfield_data.rda")
options(contrasts = c("contr.sum", "contr.poly")) #needed for Type III SS
anova_table <- function(df, mod) { #for lmer models and lmerTest::anova
df %>% select(trait, term, p.value) %>%
pivot_wider(names_from=term, values_from=p.value) %>%
mutate(R2m = map_dbl(mod, ~ MuMIn::r.squaredGLMM(.x)[[1,"R2m"]]))
}
Anova_table <- function(df) { #for glmmTMB models and car::Anova
df %>% select(Trait=trait, term, p.value) %>%
pivot_wider(names_from=term, values_from=p.value,names_repair="unique")
}
select <- dplyr::select
lptraits <- c(leaftraits, phystraits)
Sample sizes
count_measurements <- function(df, tr) summarize(df, across(all_of(tr), ~ sum(!is.na(.x)))) %>% deframe()
data.frame(dataset = c("Morphological traits", "Physiological traits"),
leaves = c(count_measurements(lt, "leaf_area_cm2"), count_measurements(pt, "photosynthesis")),
plants = c(lt %>% group_by(plantid) %>% summarize(across(all_of("leaf_area_cm2"), mean, na.rm=T)) %>%
count_measurements("leaf_area_cm2"),
pt %>% group_by(plantid) %>% summarize(across(all_of("photosynthesis"), mean, na.rm=T)) %>%
count_measurements("photosynthesis"))) %>% kable()
Morphological traits |
600 |
264 |
Physiological traits |
327 |
275 |
Trait correlations
full_join(pt %>% select(year, date, plantid, conductance, WUE, photosynthesis),
lt %>% select(year, year.round, plantid, sla, water_content, trichome_density)) %>%
ggcorr(hjust=0.85, layout.exp=2, label=T, label_round=2)

Replicated split-plot models
Table 1
lt.subplot.r2 <- lt.subplotround %>% filter(round=="2")
lt.plantyr.r2 <- lt.plantyr %>% filter(round=="2")
options(contrasts = c("contr.sum", "contr.poly"))
mod.split <- c(map(set_names(leaftraits),
~ lmer(lt.plantyr.r2[[.x]] ~ year * snow * water + (1|snow:plot) + (1|snow:plotid),
data=lt.plantyr.r2)),
map(set_names(phystraits),
~ lmer(pt.plantyr[[.x]] ~ year * snow * water + (1|snow:plot) + (1|snow:plotid) + (1|round),
data=pt.plantyr)))
mod.split.coefs <- map_dfr(mod.split, tidy, .id="trait")
mod.split.tests <- map(mod.split, anova, ddf="Ken") %>% map_dfr(tidy, .id="trait")
mod.split.emm <- map_dfr(mod.split, ~ summary(emmeans(ref_grid(.x), ~ year * snow * water)), .id = "trait")
anova_table(mod.split.tests, mod.split) %>% kable()
sla |
0.0000 |
0.1519 |
0.6661 |
0.0120 |
0.7884 |
0.2773 |
0.1535 |
0.6027 |
trichome_density |
0.0000 |
0.4894 |
0.5712 |
0.5986 |
0.1153 |
0.9208 |
0.5156 |
0.2094 |
water_content |
0.1868 |
0.6817 |
0.0037 |
0.0756 |
0.0068 |
0.3675 |
0.4283 |
0.2171 |
photosynthesis |
0.0620 |
0.6319 |
0.3075 |
0.4575 |
0.4112 |
0.9876 |
0.9137 |
0.1086 |
conductance |
0.0009 |
0.4670 |
0.0023 |
0.4766 |
0.0024 |
0.6613 |
0.6894 |
0.3456 |
WUE |
0.0000 |
0.2108 |
0.0002 |
0.4673 |
0.0026 |
0.3262 |
0.8511 |
0.4422 |
mod.split.tests.water <- map(mod.split, multcomp::glht,
linfct = multcomp::mcp(water = c("Addition - Control = 0", "Reduction - Control = 0"))) %>%
map_dfr(tidy, .id="trait")
anova_table(mod.split.tests, mod.split) %>%
left_join(mod.split.tests.water %>% select(trait, contrast, adj.p.value) %>%
pivot_wider(names_from=contrast, values_from=adj.p.value)) %>% kable()
sla |
0.0000 |
0.1519 |
0.6661 |
0.0120 |
0.7884 |
0.2773 |
0.1535 |
0.6027 |
0.5794 |
0.9137 |
trichome_density |
0.0000 |
0.4894 |
0.5712 |
0.5986 |
0.1153 |
0.9208 |
0.5156 |
0.2094 |
0.5794 |
0.9603 |
water_content |
0.1868 |
0.6817 |
0.0037 |
0.0756 |
0.0068 |
0.3675 |
0.4283 |
0.2171 |
0.0034 |
0.2324 |
photosynthesis |
0.0620 |
0.6319 |
0.3075 |
0.4575 |
0.4112 |
0.9876 |
0.9137 |
0.1086 |
0.5205 |
0.5977 |
conductance |
0.0009 |
0.4670 |
0.0023 |
0.4766 |
0.0024 |
0.6613 |
0.6894 |
0.3456 |
0.1368 |
0.0040 |
WUE |
0.0000 |
0.2108 |
0.0002 |
0.4673 |
0.0026 |
0.3262 |
0.8511 |
0.4422 |
0.0051 |
0.0011 |
Figure 1
plot_split_plot <- function(emm, data, traits.plot, geom = "point", plot.emm = TRUE, facets="year") {
data.long <- data %>% select(c(all_of(traits.plot), all_of(facets), water, snow)) %>%
pivot_longer(all_of(traits.plot), names_to="trait") %>%
mutate(trait = fct_relevel(trait, traits.plot), snow=fct_relevel(snow, "Normal")) %>%
drop_na(value, water) %>% filter(trait %in% traits.plot)
traitnames.multiline <- ifelse(nchar(traitnames.units) > 30, str_replace(traitnames.units, fixed("("),"\n("), traitnames.units)
split_plot <- emm %>% filter(trait %in% traits.plot) %>%
mutate(trait = fct_relevel(trait, traits.plot), snow=fct_relevel(snow, "Normal")) %>%
ggplot(aes(x=water, color=snow)) +
labs(x="Precipitation", y="Standardized trait", color="Snowmelt") +
scale_y_continuous(position="right") + scale_x_discrete(guide=guide_axis(angle=90)) +
scale_color_manual(values=snow_pal_grey, guide=guide_legend(override.aes = list(shape=15, size=ifelse(plot.emm,5,1)))) +
theme_minimal() + theme(text=element_text(color="black", size=14), axis.text = element_text(color="black"),
axis.title.y= element_blank(),
panel.grid.major.x = element_blank(), panel.grid.minor.x=element_blank(), panel.grid.minor.y = element_blank(),
panel.border = element_rect(fill=NA, colour = "black"),
panel.spacing=unit(0, "pt"), plot.margin = margin(0,0,0,0, "pt"), legend.position = "top") +
switch(facets,
year = facet_grid(trait ~ year, scales="free_y", switch="y",
labeller = as_labeller(c(traitnames.multiline, set_names(2018:2020)))),
year.round = facet_grid(trait ~ year.round, scales="free_y", switch="y",
labeller = as_labeller(c(traitnames.multiline, set_names(levels(lt$year.round)))))) +
switch(geom,
boxplot = geom_boxplot(data=data.long, aes(y=value), position=position_dodge(width=0.8), show.legend=!plot.emm,
fatten=ifelse(plot.emm, NULL, 1), outlier.size=0.5),
violin = geom_violin(data=data.long, aes(y=value), position=position_dodge(width=0.8), show.legend=F),
point = geom_point(data=data.long, aes(y=value), position=position_dodge(width=0.8), shape=3))
if(plot.emm) { return(split_plot +
geom_linerange(aes(ymin=emmean-SE, ymax=emmean+SE,), position=position_dodge(width=0.8), size=1, show.legend=F) +
geom_point(aes(y=emmean), position=position_dodge(width=0.8), shape="-", size=16))
} else return(split_plot)
}
#Vegetative and physiological traits - plot of EMMs and subplot means
grid.arrange(
plot_split_plot(mod.split.emm, lt.subplotround %>% filter(round==2), leaftraits), #second round only
plot_split_plot(mod.split.emm, pt.subplot, phystraits)+
guides(color = guide_legend(override.aes = list(color="white", shape=15, size=5))) +
theme(legend.title = element_text(color = "white"), legend.text = element_text(color = "white")), nrow=1)

Repeated measures
Table S1
# get plants that were sampled more than once for the first three rounds
lt.long.3 <- lt %>% pivot_longer(all_of(leaftraits), names_to = "trait") %>% arrange(date) %>% drop_na(value) %>%
filter(year.round %in% c("2018.1","2018.2","2019.1"))
lt.long.repeated.3 <- lt.long.3 %>% filter(paste(trait, plantid) %in%
(lt.long.3 %>% group_by(plantid, trait) %>% summarize(n = length(unique(year.round))) %>%
filter(n>1) %>% mutate(trait_plantid = paste(trait, plantid)) %>% pull(trait_plantid))) %>%
group_by(trait, plot, plotid, plantid, water, snow, year.round) %>% summarize(value=mean(value))
lt.repeated.3 <- lt.long.repeated.3 %>% pivot_wider(names_from="trait")
lt.repeated.3.subplotround <- lt.repeated.3 %>% group_by(year.round, water, snow, plotid) %>%
summarize(across(all_of(leaftraits), mean, na.rm=T))
lt.mod.split.rm.3 <- map(set_names(leaftraits),
~ lmer(lt.repeated.3[[.x]] ~ year.round * water * snow + (1|snow:plot) + (1|snow:plotid) + (1|plantid), data=lt.repeated.3))
lt.mod.split.rm.3.tests <- map(lt.mod.split.rm.3, anova, ddf="Ken") %>% map_dfr(tidy, .id="trait")
anova_table(lt.mod.split.rm.3.tests, lt.mod.split.rm.3) %>% kable()
sla |
0 |
0.8730 |
0.3975 |
0.7454 |
0.0636 |
0.7876 |
0.8369 |
0.5824 |
trichome_density |
0 |
0.9137 |
0.1228 |
0.0874 |
0.3010 |
0.7593 |
0.7196 |
0.1529 |
water_content |
0 |
0.0271 |
0.6476 |
0.0000 |
0.1614 |
0.7026 |
0.5422 |
0.6841 |
Models with snowmelt date and total precipitation
Table 2
P-values
lt.subplot.r2 <- lt.subplotround %>% filter(round=="2")
lt.plantyr.r2 <- lt.plantyr %>% filter(round=="2")
options(contrasts = c("contr.sum", "contr.poly"))
mod.abs <- c(map(set_names(leaftraits),
~ lmer(lt.plantyr.r2[[.x]] ~ sun_date * precip_est_mm + (1|plot) + (1|plotid), data=lt.plantyr.r2)),
map(set_names(phystraits),
~ lmer(pt.plantyr[[.x]] ~ sun_date * precip_est_mm + (1|plot) + (1|plotid) + (1|round),
data=pt.plantyr)))
mod.abs.coefs <- map_dfr(mod.abs, tidy, .id="trait")
mod.abs.tests <- map(mod.abs, anova, ddf="Ken") %>% map_dfr(tidy, .id="trait")
anova_table(mod.abs.tests, mod.abs) %>% kable()
sla |
0.0000 |
0.4675 |
0.4396 |
0.5560 |
trichome_density |
0.0015 |
0.0066 |
0.0814 |
0.1893 |
water_content |
0.9537 |
0.2542 |
0.3505 |
0.1781 |
photosynthesis |
0.0052 |
0.0973 |
0.1531 |
0.0820 |
conductance |
0.0003 |
0.0247 |
0.0821 |
0.2300 |
WUE |
0.0022 |
0.0099 |
0.0554 |
0.2054 |
Regression slopes
mod.abs.coefs %>% filter(effect=="fixed", term!="(Intercept)") %>% pivot_wider(id_cols=trait, names_from=term, values_from=c("estimate","std.error")) %>% kable()
sla |
6.6467 |
-0.7503 |
0.0067 |
0.9560 |
1.0007 |
0.0084 |
trichome_density |
-1.3730 |
-1.7834 |
0.0090 |
0.4267 |
0.6503 |
0.0051 |
water_content |
-0.0001 |
0.0024 |
0.0000 |
0.0018 |
0.0020 |
0.0000 |
photosynthesis |
0.2752 |
0.1949 |
-0.0013 |
0.0927 |
0.1165 |
0.0009 |
conductance |
0.0072 |
0.0040 |
0.0000 |
0.0017 |
0.0017 |
0.0000 |
WUE |
-1.7948 |
-1.1076 |
0.0063 |
0.5112 |
0.4252 |
0.0033 |
Figure 2
plot_abs <- function(mod, data, traits.plot, tests=NULL, facets = "trait") {
data.long <- data %>% select(all_of(traits.plot), water, snow, year, sun_date, precip_est_mm) %>%
pivot_longer(all_of(traits.plot), names_to="trait") %>%
mutate(trait = fct_relevel(trait, traits.plot), snow=fct_relevel(snow, "Normal"),
value = (value - alltraits.mean[as.character(trait)])/alltraits.sd[as.character(trait)]) %>%
drop_na(value, water) %>% filter(trait %in% traits.plot)
precip.breaks <- seq(25,175, by=25)
grid_emmeans <- function(mods, use.rounds = F) {
grid.points <- list(sun_date=range(treatments$sun_date), precip_est_mm=precip.breaks)
if(use.rounds) map_dfr(mods, ~ summary(emmeans(.x, ~ precip_est_mm * sun_date * round, at=grid.points)), .id="trait")
else map_dfr(mods, ~ summary(emmeans(.x, ~ precip_est_mm * sun_date, at=grid.points)), .id="trait")
}
emm.grid <- mod[traits.plot] %>% grid_emmeans(use.rounds= facets=="trait.round") %>%
mutate(trait = fct_relevel(trait, traits.plot),
emmean = (emmean - alltraits.mean[as.character(trait)])/alltraits.sd[as.character(trait)])
traitnames.multiline <- ifelse(nchar(traitnames) > 30, str_replace(traitnames, fixed("("),"\n("), traitnames)
if(!is.null(tests)) {
tests.sig <- tests %>% filter(p.value < 0.05) %>%
mutate(term.abbr = str_replace_all(term, c(sun_date="Sn", precip_est_mm="Pr",`:`="\U00D7"))) %>%
group_by(trait) %>% summarize(terms.sig = paste0("(",paste(term.abbr, collapse=", "),")")) %>% deframe
traitnames.multiline[] <- paste(traitnames.multiline, replace_na(tests.sig[alltraits], ""))
}
abs_plot <- ggplot(emm.grid, aes(x=sun_date, y=emmean, color=precip_est_mm, group=precip_est_mm)) +
geom_point(data = data.long, aes(y=value, shape=year), size=2) + geom_line() +
scale_color_gradientn(colors=rev(water_pal),
values=rev(c(1, (treatments %>% group_by(water) %>% summarize(across(precip_est_mm, mean)) %>%
pull(precip_est_mm) %>% scales::rescale(from=range(precip.breaks)))[2],0)),
breaks=precip.breaks) +
labs(x="Snowmelt date (day of year)", y="Standardized trait", color="Summer\nprecipitation\n(mm)", shape="Year") +
scale_y_continuous(position="right")+
theme_minimal() + theme(text=element_text(color="black", size=14), axis.text = element_text(color="black"),
panel.border = element_rect(fill=NA, colour = "black")) +
switch(facets,
trait = facet_wrap(vars(trait), scales="fixed", dir = "v", ncol=2, labeller = as_labeller(traitnames.multiline)),
trait.round = facet_grid(trait ~ round, scales="free_y", switch="y",
labeller = as_labeller(c(traitnames.multiline,
set_names(paste("Round",1:2), 1:2)))))
return(abs_plot)
}
plot_abs(mod.abs, bind_rows(lt.subplot.r2, pt.subplot), c(leaftraits[2],phystraits[c(3,1,2)])) +
scale_y_continuous(limits=c(-1.3, 1.8), breaks=c(-2:1))#subplot means

Models with soil moisture
Table 3
lt.subplot.r2 <- lt.subplotround %>% filter(round=="2")
lt.plantyr.r2 <- lt.plantyr %>% filter(round=="2")
pt.plantyr <- pt.plantyr %>% mutate(VWC = ifelse(is.na(VWC.plant), VWC.sm, VWC.plant)) #phys traits had the VWC measured next to the plant in 2019 and 2020 only - substitute VWC in subplot on closest date
pt <- pt %>% mutate(VWC = ifelse(is.na(VWC.plant), VWC.sm, VWC.plant))
options(contrasts = c("contr.sum", "contr.poly"))
mod.sm <- c(map(set_names(leaftraits),
~ lmer(lt.plantyr.r2[[.x]] ~ year * VWC + (1|plotid), data=lt.plantyr.r2)),
map(set_names(phystraits[1:2]),
~ lmer(pt.plantyr[[.x]] ~ year * VWC + I(VWC^2) + (1|plotid) + (1|round), data=pt.plantyr)),
map(set_names(phystraits[3]),
~ lmer(pt.plantyr[[.x]] ~ year * VWC + (1|plotid) + (1|round), data=pt.plantyr)))
#significant effect of quadratic term for sepal_width, photosynthesis, conductance, but not year * VWC^2
mod.sm.coefs <- map_dfr(mod.sm, tidy, .id="trait")
mod.sm.tests <- map(mod.sm, anova, ddf="Ken") %>% map_dfr(tidy, .id="trait")
anova_table(mod.sm.tests, mod.sm) %>% kable()
sla |
0.0116 |
0.1658 |
0.3953 |
NA |
0.5847 |
trichome_density |
0.0001 |
0.9058 |
0.0330 |
NA |
0.1950 |
water_content |
0.1778 |
0.0001 |
0.0829 |
NA |
0.1835 |
photosynthesis |
0.4488 |
0.0002 |
0.6687 |
0.0055 |
0.1497 |
conductance |
0.0009 |
0.0001 |
0.0219 |
0.0389 |
0.3858 |
WUE |
0.0000 |
0.0000 |
0.0030 |
NA |
0.4346 |
Figure 3
plot_sm <- function(mod, data, traits.plot, tests=NULL) {
data.long <- data %>% select(all_of(traits.plot), water, snow, year, sun_date, precip_est_mm, VWC) %>%
pivot_longer(all_of(traits.plot), names_to="trait") %>%
mutate(trait = fct_relevel(trait, traits.plot), snow=fct_relevel(snow, "Normal"),
value = (value - alltraits.mean[as.character(trait)])/alltraits.sd[as.character(trait)]) %>%
drop_na(value, water) %>% filter(trait %in% traits.plot)
VWC.range <- range(data$VWC, na.rm=T)
emm.grid <- mod[traits.plot] %>%
map_dfr(~ emmeans(.x, ~ year*VWC, at=list(VWC=seq(from=floor(VWC.range[1]*10)/10, to=ceiling(VWC.range[2]*10)/10, by=0.1))) %>% as_tibble(), .id="trait") %>%
mutate(trait = fct_relevel(trait, traits.plot),
emmean = (emmean - alltraits.mean[as.character(trait)])/alltraits.sd[as.character(trait)]) %>%
left_join(data %>% drop_na(VWC) %>% group_by(year) %>% summarize(VWC.min = min(VWC), VWC.max=max(VWC))) %>%
group_by(year) %>% filter(VWC >= VWC.min-0.1, VWC <= VWC.max+0.1)
traitnames.multiline <- ifelse(nchar(traitnames) > 30, str_replace(traitnames, fixed("("),"\n("), traitnames)
if(!is.null(tests)) {
tests.sig <- tests %>% filter(p.value < 0.05) %>%
mutate(term.abbr = str_replace_all(term, c(year="Yr", VWC="SM",`:`="\U00D7"))) %>%
group_by(trait) %>% summarize(terms.sig = paste0("(",paste(term.abbr, collapse=", "),")")) %>% deframe
print(tests.sig[traits.plot])
traitnames.multiline[] <- paste(traitnames.multiline, replace_na(tests.sig[alltraits], ""))
}
sm_plot <- ggplot(emm.grid, aes(x=VWC, y=emmean, color=year, group=year)) +
facet_wrap(vars(trait), scales="fixed", dir="v", ncol=ifelse(length(traits.plot)>3,2,4), labeller = as_labeller(traitnames.multiline))+
geom_point(data = data.long, aes(y=value, color=year), size=1) +
geom_line(size=2) +
scale_color_manual(values=year_pal) + ylab("Standardized trait") + labs(color="Year") +
theme_minimal() + theme(text=element_text(color="black", size=14), axis.text = element_text(color="black"),
panel.border = element_rect(fill=NA, colour = "black"))
return(sm_plot)
}
grid.arrange(
plot_sm(mod.sm, lt.plantyr.r2, leaftraits) +
xlab("Summer mean of soil moisture in subplot (%VWC)"),
plot_sm(mod.sm, pt.plantyr, phystraits) +
xlab("Soil moisture next to plant or in subplot on closest date (%VWC)") +
guides(color = guide_legend(override.aes = list(color="white", shape=15, size=5))) +
theme(legend.title = element_text(color = "white"), legend.text = element_text(color = "white")), ncol=1)

Fitness vs. treatments
Table S2
#Model effect of treatments on survival and flowering of vegetative plants - both years combined
cen.status.fitness <- cen.status.long %>% filter(status=="vegetative", census %in% c("2018","2019","2020")) %>%
full_join(cen.RGR.long %>% drop_na(RGR) %>% filter(census!="18r2") %>% select(-census)) %>% left_join(treatments) %>% left_join(sm.subplotyear)
options(contrasts = c("contr.sum", "contr.poly"))
fitness.mod <- map(set_names(fitnesstraits),
~ glmmTMB(cen.status.fitness[[.x]] ~ year * snow * water + (1|snow:plot) + (1|snow:plotid), data=cen.status.fitness,
family=ifelse(.x =="RGR", "gaussian", "binomial")))
fitness.mod.coefs <- map_dfr(fitness.mod, tidy, .id="trait")
fitness.mod.tests <- map(fitness.mod, car::Anova, type=3) %>% map_dfr(tidy, .id="trait")
fitness.mod.emm <- map_dfr(fitness.mod, ~ summary(emmeans(ref_grid(.x), ~ year * snow * water)), .id = "trait") %>%
mutate(uSE = ifelse(trait=="RGR", emmean+SE, plogis(emmean+SE)),
lSE = ifelse(trait=="RGR", emmean-SE, plogis(emmean-SE)),
emmean = ifelse(trait=="RGR", emmean, plogis(emmean)),
trait = fct_relevel(trait, fitnesstraits))
Anova_table(fitness.mod.tests) %>% kable()
survived |
0 |
0.0654 |
0.7972 |
0.4211 |
0.5528 |
0.3943 |
0.5450 |
0.0168 |
RGR |
0 |
0.0000 |
0.0897 |
0.0785 |
0.0485 |
0.0000 |
0.5893 |
0.0082 |
flowered |
0 |
0.0000 |
0.8817 |
0.0885 |
0.3232 |
0.4297 |
0.6055 |
0.0903 |
Figure S3
ggplot(fitness.mod.emm %>% mutate(snow=fct_relevel(snow, "Normal", "Early")), aes(x=water, color=snow, y=emmean)) +
facet_grid(trait ~ year, scales="free_y", labeller = as_labeller(c(fitnessnames, set_names(2018:2020))), switch="y", as.table=F)+
geom_hline(data=data.frame(trait = fitnesstraits, plot=c(NA,NA,"yes")), aes(linetype=plot, yintercept=0), color="grey")+
guides(linetype="none")+
geom_linerange(aes(ymin=lSE, ymax=uSE), position=position_dodge(width=0.8), size=1, show.legend=F) +
geom_point(position=position_dodge(width=0.8), shape="-", size=16) +
labs(x="Precipitation", color="Snowmelt") +
scale_y_continuous(position="right", expand=expansion(mult=c(0,0.05))) + scale_x_discrete(guide=guide_axis(angle=90))+
scale_color_manual(values=snow_pal_grey, guide=guide_legend(override.aes = list(shape=15, size=5))) +
theme_minimal() + theme(text=element_text(color="black", size=14), axis.text = element_text(color="black"),
axis.title.y= element_blank(),
panel.border = element_rect(fill=NA, colour = "black"),
panel.spacing.x=unit(0, "lines"), panel.spacing.y=unit(1, "lines"),
plot.margin = margin(0,0,0,0, "pt"), legend.position = "top")

Selection
lt.plantyr.r2 <- lt.plantyr %>% filter(round=="2")
step_selection_mod <- function(trait, response, treatment, set, drop=TRUE) {
family <- ifelse(response=="RGR", "gaussian", "binomial")
re <- ifelse(treatment =="snow", "snow:plot", "plot")
t.plantyr <- list(lt = lt.plantyr.r2, pt = pt.plantyr)[[set]] %>% rename("trait"=trait)
options(contrasts = c("contr.sum", "contr.poly"))
mod <- glmmTMB(formula(paste(response, "~ trait * year * ", treatment," + (1|",re,")")),
data=t.plantyr, family=family)
if(drop) {
droporder <- c(paste0("trait:year:", treatment))
for(term_test in droporder) {
pval <- car::Anova(mod) %>% tidy %>% filter(term==term_test) %>% pull(p.value)
if(pval < 0.05) return(mod)
else mod <- update(mod, formula(paste0(". ~ . -", term_test)))
}
}
return(mod)
}
selection.combos <- expand_grid(response = fitnesstraits, treatment=c("water","snow"), set = c("lt", "pt"))
mod.selection <- selection.combos %>%
full_join(tibble(trait=c(leaftraits, phystraits),
set = c(rep("lt", length(leaftraits)), rep("pt", length(phystraits))))) %>%
mutate(full_model = pmap(., step_selection_mod, drop=F),
model = pmap(., step_selection_mod, drop=T),
coefs = map(model, tidy),
tests = map(model, ~ tidy(car::Anova(., type=3))))
emt.selection <- mod.selection %>%
mutate(yrtrend = map2(full_model, treatment,
~tidy(emtrends(.x, specs=c("year",as.character(.y)), var="trait")))) %>%
select(response, treatment, trait, yrtrend) %>% unnest(yrtrend) %>%
mutate(sd.trait=alltraits.sd[trait],
trait.trend.sd = trait.trend*sd.trait, std.error.sd=std.error*sd.trait) #multiply by SDto get change in fitness per SD
Table S3
mod.selection %>% group_by(treatment, response) %>%
group_walk(function(.x,.y) {cat(paste("Effect of trait on whether a plant",
ifelse(.y$response=="RGR", "grew faster", as.character(.y$response)),
"under different",.y$treatment,"conditions"));
Anova_table(.x %>% unnest(tests)) %>% kable() %>% print})
Effect of trait on whether a plant flowered under different snow conditions
sla |
0.3317 |
0.1159 |
0.4337 |
0.7854 |
0.4741 |
0.9760 |
0.0315 |
0.0292 |
trichome_density |
0.0001 |
0.5036 |
0.1254 |
0.2700 |
0.9762 |
0.5908 |
0.8727 |
NA |
water_content |
0.9481 |
0.6631 |
0.1364 |
0.4622 |
0.1781 |
0.5110 |
0.3869 |
NA |
photosynthesis |
0.0049 |
0.8196 |
0.0241 |
0.6761 |
0.7605 |
0.8938 |
0.9809 |
NA |
conductance |
0.0000 |
0.1004 |
0.0802 |
0.6564 |
0.5563 |
0.2719 |
0.7909 |
NA |
WUE |
0.2426 |
0.2001 |
0.0113 |
0.0846 |
0.1994 |
0.0825 |
0.3988 |
NA |
Effect of trait on whether a plant grew faster under different snow conditions
sla |
0.7052 |
0.3816 |
0.8329 |
0.2508 |
0.5087 |
0.3092 |
0.5174 |
trichome_density |
0.2207 |
0.5082 |
0.0726 |
0.5553 |
0.5807 |
0.4760 |
0.6075 |
water_content |
0.0360 |
0.0185 |
0.3471 |
0.7810 |
0.2428 |
0.8452 |
0.6853 |
photosynthesis |
0.9038 |
0.3558 |
0.5644 |
0.3127 |
0.1033 |
0.9416 |
0.2709 |
conductance |
0.4514 |
0.8889 |
0.8290 |
0.3335 |
0.0692 |
0.5832 |
0.2541 |
WUE |
0.1721 |
0.5663 |
0.0367 |
0.6402 |
0.6375 |
0.7271 |
0.2155 |
Effect of trait on whether a plant survived under different snow conditions
sla |
0.0815 |
0.4025 |
0.8758 |
0.2835 |
0.9105 |
0.3375 |
0.5697 |
trichome_density |
0.0068 |
0.2875 |
0.6033 |
0.5454 |
0.9035 |
0.3011 |
0.6245 |
water_content |
0.4344 |
0.7356 |
0.7422 |
0.3973 |
0.8272 |
0.4326 |
0.9464 |
photosynthesis |
0.0000 |
0.2788 |
0.3959 |
0.3410 |
0.4093 |
0.4677 |
0.5303 |
conductance |
0.0000 |
0.5287 |
0.9881 |
0.9740 |
0.9567 |
0.4986 |
0.9033 |
WUE |
0.0001 |
0.2857 |
0.2494 |
0.0283 |
0.2041 |
0.0354 |
0.6202 |
Effect of trait on whether a plant flowered under different water conditions
sla |
0.5329 |
0.1689 |
0.9884 |
0.2451 |
0.9449 |
0.3231 |
0.0555 |
trichome_density |
0.0007 |
0.5407 |
0.1706 |
0.7915 |
0.9078 |
0.9320 |
0.3337 |
water_content |
0.8205 |
0.5025 |
0.1750 |
0.6309 |
0.2268 |
0.7004 |
0.3672 |
photosynthesis |
0.0495 |
0.6821 |
0.0300 |
0.1912 |
0.8794 |
0.3623 |
0.8151 |
conductance |
0.0004 |
0.5845 |
0.0654 |
0.2286 |
0.4345 |
0.1494 |
0.7986 |
WUE |
0.3530 |
0.2789 |
0.0076 |
0.5942 |
0.1261 |
0.8670 |
0.8587 |
Effect of trait on whether a plant grew faster under different water conditions
sla |
0.6134 |
0.9749 |
0.7442 |
0.7135 |
0.9586 |
0.7040 |
0.4100 |
trichome_density |
0.3216 |
0.4079 |
0.1476 |
0.5695 |
0.2681 |
0.9229 |
0.0053 |
water_content |
0.8444 |
0.6500 |
0.4799 |
0.8091 |
0.6138 |
0.9147 |
0.1149 |
photosynthesis |
0.6730 |
0.5055 |
0.1163 |
0.0069 |
0.6106 |
0.3151 |
0.0400 |
conductance |
0.0320 |
0.3380 |
0.3055 |
0.0144 |
0.6306 |
0.4573 |
0.0343 |
WUE |
0.7578 |
0.2953 |
0.5070 |
0.0731 |
0.9072 |
0.2128 |
0.0087 |
Effect of trait on whether a plant survived under different water conditions
sla |
0.0344 |
0.2066 |
0.9756 |
0.6896 |
0.8533 |
0.7193 |
0.5622 |
trichome_density |
0.0085 |
0.3653 |
0.8689 |
0.6346 |
0.9392 |
0.8250 |
0.2619 |
water_content |
0.2525 |
0.4137 |
0.8880 |
0.9807 |
0.9246 |
0.9974 |
0.2139 |
photosynthesis |
0.0000 |
0.1342 |
0.4205 |
0.5193 |
0.4314 |
0.3896 |
0.9250 |
conductance |
0.0000 |
0.6037 |
0.9599 |
0.0738 |
0.9287 |
0.0133 |
0.4460 |
WUE |
0.0019 |
0.3059 |
0.1869 |
0.6154 |
0.1268 |
0.5005 |
0.8476 |
plot_selection_facets <- function(response, treatment) {
emt.selection.filtered <- emt.selection %>%
select(!!!treatment, response, year, trait, p.value) %>%
drop_na(!!!treatment) %>% filter(response==!!response) %>%
mutate(sig = ifelse(p.value < 0.05,"signif","ns"))
p <- bind_rows(lt.plantyr.r2, pt.plantyr) %>%
pivot_longer(all_of(c(leaftraits, phystraits)), names_to="trait") %>%
left_join(emt.selection.filtered) %>%
mutate(trait = fct_relevel(trait, c(leaftraits, phystraits))) %>%
ggplot(aes_string(y=response, x="value", color=treatment, linetype="year")) +
facet_wrap(vars(trait), scales="free_x", labeller = as_labeller(traitnames.units))+
scale_color_manual(values=list(water=water_pal, snow=snow_pal)[[treatment]]) +
scale_linetype_manual(values=c("22","solid","42")) +
labs(linetype="Year", shape="Year", x="", y=fitnessnames[response],
color=c(water="Precipitation",snow="Snowmelt")[treatment]) +
theme_minimal() +
theme(text = element_text(color="black"), axis.text = element_text(color="black"),
panel.border = element_rect(fill=NA, colour = "black"),
panel.spacing=unit(0, "pt"), plot.margin = margin(0,0,0,0, "pt"),
legend.position = "top", legend.key.width = unit(2, "lines"))
if(response =="RGR") {
p <- p + geom_hline(yintercept=0, color="grey") +
geom_point(aes(shape=year)) +
geom_line(stat="smooth", size=1, se=F, method="lm")
} else {
p <- p + geom_point(shape="|") +
geom_line(stat="smooth", size=1, se=F,
method="glm", method.args = list(family="binomial")) +
scale_y_continuous(labels=scales::label_percent(accuracy=1))
}
#get rightmost edge of geom_smooths to add signif labels
smooth.ends <- layer_data(p, ifelse(response=="RGR", 3, 2)) %>% #hline adds a layer
group_by(group, PANEL) %>% filter(x==max(x)) %>% ungroup() %>% select(-PANEL) %>%
bind_cols(emt.selection.filtered) %>%
filter(sig=="signif") %>%
mutate(trait = factor(trait, levels=c(leaftraits, phystraits)))
p + geom_point(data=smooth.ends, aes(x=x,y=y), color="black", shape=8, size=2, inherit.aes = F)
}
selection_plots <- expand_grid(treatment=c("water","snow"), response = fitnesstraits) %>%
mutate(plot = pmap(., plot_selection_facets))
Timing
Figure S1
timings_labels <- c(snowcloth="Snow cloths applied to early plots",
meltdates="Mean early and normal snowmelt timing",
waterdates="Summer precipitation treatments applied",
sm="Soil moisture recorded", ph="Inflorescence height recorded",
mt="Floral morphology recorded", nt="Nectar traits recorded",
pt= "Physiology traits recorded", lt="Vegetative traits recorded",
sds="Flower collection period", cen="Rosette size and survival recorded")
plot_timings <- function(data) {
data %>% mutate(variable=fct_reorder(variable, begin, na.rm=T, .desc=T),
drawline = ifelse(variable %in% c("lt","meltdates"), NA, TRUE)) %>%
ggplot(aes(y=variable, color=year))+
geom_linerange(aes(xmin=begin, xmax=end, linetype=drawline),
position = position_dodge2(width=0.7, reverse = T), size=1.2, show.legend=FALSE) +
geom_text(data=data %>% drop_na(plots) %>% filter(year=="2019"),
aes(x = end+7, label=paste(ifelse(nchar(plots)>1, "Plots","Plot"), str_replace(plots,","," & "))),
position = position_dodge2(width=0.8, reverse = T), size=4, hjust=0, show.legend=FALSE)+
geom_point(aes(x=begin), position = position_dodge2(width=0.7, reverse = T), shape=15, size=2)+
geom_point(aes(x=end), position = position_dodge2(width=0.7, reverse = T), shape=15, size=2) +
scale_color_manual("Year", values=c(year_pal, `2021`=brewer.pal(8, name="Set2")[[4]]), guide=guide_legend(override.aes = list(size=5))) +
scale_y_discrete("", labels=timings_labels)+
scale_x_continuous("Day of year", breaks=seq(80,240, by=20)) +
theme_minimal() + theme(legend.position = "top", text=element_text(size=14, color="black"), axis.text = element_text(color="black"),
panel.border = element_rect(fill=NA, colour = "black"))
}
plot_timings(filter(timings, !variable %in% c("sds","mt","nt","ph")))

Experiment map
Methods S1
subplot_offset <- 1/6
size_meter <- 6
ggplot(treatments_map) + coord_fixed(clip="off")+
geom_point(aes(x=plot_x, y=plot_y, fill=snow), size=7*size_meter, shape=22, stroke=1)+
scale_fill_manual("Snowmelt", values=snow_pal, guide=guide_legend(override.aes = list(size = 2*size_meter)))+
new_scale_fill()+
geom_point(aes(x=plot_x+subplot_x*subplot_offset, y=plot_y+subplot_y*subplot_offset, fill=water4), size=2*size_meter, shape=22, stroke=1) +
geom_text(aes(x=plot_x+subplot_x*subplot_offset, y=plot_y+subplot_y*subplot_offset, label=subplot)) +
scale_fill_manual("Precipitation", values=water4_pal) +
geom_text(aes(x=plot_x, y=plot_y, label=plot))+ theme_minimal() +
theme(legend.position="right", axis.title=element_blank(), axis.text = element_blank(),
axis.ticks = element_blank(), panel.background = element_blank(), panel.grid = element_blank())
