Load the following libraries:

library(tidyverse)
library(ggplot2)
library(patchwork)

Run this cell for the functions

# Create a dataframe split 50/50 by water treatment and 50/50 by NIL (25% of each combination)
create_df <- function(num_plots) {
  df <- data.frame(
    treatment = rep(c("ww", "d"), each = num_plots/2),
    nil = rep(c("NIL+", "NIL-"), each = num_plots/4, times=2),
    height = round(rnorm(num_plots, mean=180, sd=10)), # randomly set heights with normal distribution
    plot_id = c(1:num_plots)
  )
  return (df)
}

# Adjust heights based on predictions of different hypotheses
# Environmental: only the water treatment matters
# Genetic: only the presence of the allele matters
# GxE: the gene is only expressed in the presence of drought
# Random: Poor management leads to noise
mutate_df <- function(num_plots, g_effect_size, e_effect_size) {
  df <- create_df(num_plots)
  df %>%
    mutate(
      e_effect = case_when(
        treatment == "d" ~ height + e_effect_size,
        treatment == "ww" ~ height
      ),
      g_effect = case_when(
        nil == "NIL+" ~ height + g_effect_size,
        nil == "NIL-" ~ height
      ),
      gxe_effect = case_when(
        treatment == "d" & nil == "NIL+" ~ height + e_effect_size + g_effect_size,
        treatment == "d" & nil == "NIL-" ~ height + e_effect_size,
        treatment == "ww" ~ height
      ),
      random_effect  = case_when(
        plot_id %% 5 == 0 ~ height + 10,
        plot_id %% 5 != 0 ~ height
      )
  )
}

Run this cell to generate the dataframe

df = mutate_df(num_plots=10000, g_effect_size=10, e_effect_size=-30)
ggplot(df, aes(x=gxe_effect, color=treatment)) + geom_histogram(binwidth=1, position="identity")

Run this cell to visualize the results

p1 <- ggplot(df, aes(x=treatment, y=g_effect, fill=nil)) + geom_boxplot() + labs(title="Genetic Effect Only", x="Treatment Group", y = "Plant Height (cm)")
p2 <- ggplot(df, aes(x=treatment, y=e_effect, fill=nil)) + geom_boxplot() + labs(title="Environmental Effect Only", x="Treatment Group", y = "Plant Height (cm)")
p3 <- ggplot(df, aes(x=treatment, y=gxe_effect, fill=nil)) + geom_boxplot() + labs(title="GxE Effect", x="Treatment Group", y = "Plant Height (cm)")
p4 <- ggplot(df, aes(x=treatment, y=random_effect, fill=nil)) + geom_boxplot() + labs(title="Random Effects", x="Treatment Group", y = "Plant Height (cm)")

(p1|p2)/(p3|p4) + plot_annotation(tag_levels = "a")


#+ stat_compare_means(comparisons = p_comparisons, aes(label = ..p.format..))
LS0tCnRpdGxlOiAiTklMIFdvcmtzaGVldCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyBMb2FkIHRoZSBmb2xsb3dpbmcgbGlicmFyaWVzOgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShwYXRjaHdvcmspCmBgYAoKIyBSdW4gdGhpcyBjZWxsIGZvciB0aGUgZnVuY3Rpb25zCmBgYHtyfQojIENyZWF0ZSBhIGRhdGFmcmFtZSBzcGxpdCA1MC81MCBieSB3YXRlciB0cmVhdG1lbnQgYW5kIDUwLzUwIGJ5IE5JTCAoMjUlIG9mIGVhY2ggY29tYmluYXRpb24pCmNyZWF0ZV9kZiA8LSBmdW5jdGlvbihudW1fcGxvdHMpIHsKICBkZiA8LSBkYXRhLmZyYW1lKAogICAgdHJlYXRtZW50ID0gcmVwKGMoInd3IiwgImQiKSwgZWFjaCA9IG51bV9wbG90cy8yKSwKICAgIG5pbCA9IHJlcChjKCJOSUwrIiwgIk5JTC0iKSwgZWFjaCA9IG51bV9wbG90cy80LCB0aW1lcz0yKSwKICAgIGhlaWdodCA9IHJvdW5kKHJub3JtKG51bV9wbG90cywgbWVhbj0xODAsIHNkPTEwKSksICMgcmFuZG9tbHkgc2V0IGhlaWdodHMgd2l0aCBub3JtYWwgZGlzdHJpYnV0aW9uCiAgICBwbG90X2lkID0gYygxOm51bV9wbG90cykKICApCiAgcmV0dXJuIChkZikKfQoKIyBBZGp1c3QgaGVpZ2h0cyBiYXNlZCBvbiBwcmVkaWN0aW9ucyBvZiBkaWZmZXJlbnQgaHlwb3RoZXNlcwojIEVudmlyb25tZW50YWw6IG9ubHkgdGhlIHdhdGVyIHRyZWF0bWVudCBtYXR0ZXJzCiMgR2VuZXRpYzogb25seSB0aGUgcHJlc2VuY2Ugb2YgdGhlIGFsbGVsZSBtYXR0ZXJzCiMgR3hFOiB0aGUgZ2VuZSBpcyBvbmx5IGV4cHJlc3NlZCBpbiB0aGUgcHJlc2VuY2Ugb2YgZHJvdWdodAojIFJhbmRvbTogUG9vciBtYW5hZ2VtZW50IGxlYWRzIHRvIG5vaXNlCm11dGF0ZV9kZiA8LSBmdW5jdGlvbihudW1fcGxvdHMsIGdfZWZmZWN0X3NpemUsIGVfZWZmZWN0X3NpemUpIHsKICBkZiA8LSBjcmVhdGVfZGYobnVtX3Bsb3RzKQogIGRmICU+JQogICAgbXV0YXRlKAogICAgICBlX2VmZmVjdCA9IGNhc2Vfd2hlbigKICAgICAgICB0cmVhdG1lbnQgPT0gImQiIH4gaGVpZ2h0ICsgZV9lZmZlY3Rfc2l6ZSwKICAgICAgICB0cmVhdG1lbnQgPT0gInd3IiB+IGhlaWdodAogICAgICApLAogICAgICBnX2VmZmVjdCA9IGNhc2Vfd2hlbigKICAgICAgICBuaWwgPT0gIk5JTCsiIH4gaGVpZ2h0ICsgZ19lZmZlY3Rfc2l6ZSwKICAgICAgICBuaWwgPT0gIk5JTC0iIH4gaGVpZ2h0CiAgICAgICksCiAgICAgIGd4ZV9lZmZlY3QgPSBjYXNlX3doZW4oCiAgICAgICAgdHJlYXRtZW50ID09ICJkIiAmIG5pbCA9PSAiTklMKyIgfiBoZWlnaHQgKyBlX2VmZmVjdF9zaXplICsgZ19lZmZlY3Rfc2l6ZSwKICAgICAgICB0cmVhdG1lbnQgPT0gImQiICYgbmlsID09ICJOSUwtIiB+IGhlaWdodCArIGVfZWZmZWN0X3NpemUsCiAgICAgICAgdHJlYXRtZW50ID09ICJ3dyIgfiBoZWlnaHQKICAgICAgKSwKICAgICAgcmFuZG9tX2VmZmVjdCAgPSBjYXNlX3doZW4oCiAgICAgICAgcGxvdF9pZCAlJSA1ID09IDAgfiBoZWlnaHQgKyAxMCwKICAgICAgICBwbG90X2lkICUlIDUgIT0gMCB+IGhlaWdodAogICAgICApCiAgKQp9CmBgYAoKIyBSdW4gdGhpcyBjZWxsIHRvIGdlbmVyYXRlIHRoZSBkYXRhZnJhbWUKYGBge3J9CmRmID0gbXV0YXRlX2RmKG51bV9wbG90cz0xMDAwMCwgZ19lZmZlY3Rfc2l6ZT0xMCwgZV9lZmZlY3Rfc2l6ZT0tMzApCmdncGxvdChkZiwgYWVzKHg9Z3hlX2VmZmVjdCwgY29sb3I9dHJlYXRtZW50KSkgKyBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aD0xLCBwb3NpdGlvbj0iaWRlbnRpdHkiKQpgYGAKCiMgUnVuIHRoaXMgY2VsbCB0byB2aXN1YWxpemUgdGhlIHJlc3VsdHMKYGBge3J9CnAxIDwtIGdncGxvdChkZiwgYWVzKHg9dHJlYXRtZW50LCB5PWdfZWZmZWN0LCBmaWxsPW5pbCkpICsgZ2VvbV9ib3hwbG90KCkgKyBsYWJzKHRpdGxlPSJHZW5ldGljIEVmZmVjdCBPbmx5IiwgeD0iVHJlYXRtZW50IEdyb3VwIiwgeSA9ICJQbGFudCBIZWlnaHQgKGNtKSIpCnAyIDwtIGdncGxvdChkZiwgYWVzKHg9dHJlYXRtZW50LCB5PWVfZWZmZWN0LCBmaWxsPW5pbCkpICsgZ2VvbV9ib3hwbG90KCkgKyBsYWJzKHRpdGxlPSJFbnZpcm9ubWVudGFsIEVmZmVjdCBPbmx5IiwgeD0iVHJlYXRtZW50IEdyb3VwIiwgeSA9ICJQbGFudCBIZWlnaHQgKGNtKSIpCnAzIDwtIGdncGxvdChkZiwgYWVzKHg9dHJlYXRtZW50LCB5PWd4ZV9lZmZlY3QsIGZpbGw9bmlsKSkgKyBnZW9tX2JveHBsb3QoKSArIGxhYnModGl0bGU9Ikd4RSBFZmZlY3QiLCB4PSJUcmVhdG1lbnQgR3JvdXAiLCB5ID0gIlBsYW50IEhlaWdodCAoY20pIikKcDQgPC0gZ2dwbG90KGRmLCBhZXMoeD10cmVhdG1lbnQsIHk9cmFuZG9tX2VmZmVjdCwgZmlsbD1uaWwpKSArIGdlb21fYm94cGxvdCgpICsgbGFicyh0aXRsZT0iUmFuZG9tIEVmZmVjdHMiLCB4PSJUcmVhdG1lbnQgR3JvdXAiLCB5ID0gIlBsYW50IEhlaWdodCAoY20pIikKCihwMXxwMikvKHAzfHA0KSArIHBsb3RfYW5ub3RhdGlvbih0YWdfbGV2ZWxzID0gImEiKQoKIysgc3RhdF9jb21wYXJlX21lYW5zKGNvbXBhcmlzb25zID0gcF9jb21wYXJpc29ucywgYWVzKGxhYmVsID0gLi5wLmZvcm1hdC4uKSkKYGBgCgpgYGB7cn0KCmBgYAo=