0

I have the following data and code...

#generate example data
rG_activity <- c("0.230", "0.335", NA, "0.368", "0.368", "0.327", "0.091", "-0.230")
rG_activity_error_intervals <- c("(-0.075, 0.335)", "(0.239, 0.631)", NA, "(0.234, 0.602)", "(0.284, 0.752)", "(0.229, 0.726)", "(-0.259, 0.340)", "(-0.311, 0.252)")
rG_task_persistence <- c("-0.304", "-0.302", "-0.309", "-0.362", "-0.345", "-0.291", "-0.062", "-0.291")
rG_task_persistence_error_intervals <- c("(-0.242, -0.266)", "(-0.268, -0.236)","(-0.256, -0.263)", "(-0.679, -0.244)", "(-0.260, -0.231)", "(-0.396, -0.086)", "(-0.272, 0.247)", "(-0.207, -0.074)")
rE_activity <- c("0.005","-0.024", NA, "0.256", "-0.225", "-0.054", "-0.013", "0.058")
rE_activity_error_intervals <- c("(-0.255, 0.266)", "(0.243, -0.291)", NA,"(-0.021, 0.333)", "(-0.298, 0.048)", "(-0.248, 0.240)", "(-0.266, 0.240)", "(-0.225, 0.241)")
rE_task_persistence <- c("0.211", "-0.006", "-0.098", "0.093", "-0.002", "0.203", "0.047", "0.205")
rE_task_persistence_error_intervals <- c("(-0.046, 0.269)", "(0.257, -0.269)", "(-0.261, 0.064)", "(-0.065, 0.251)", "(-0.274, 0.270)", "(-0.065, 0.272)", "(-0.212, 0.206)", "(-0.071, 0.280)")
rG_emotionality <- c("0.230", "0.335", "-0.309", "0.368", "0.368", "0.327", "0.091", "-0.230")
rG_emotionality_error_intervals <- c("(-0.075, 0.335)", "(0.239, 0.631)", "(-0.256, -0.263)", "(0.234, 0.602)", "(0.284, 0.752)", "(0.229, 0.726)", "(-0.259, 0.340)", "(-0.311, 0.252)")
rE_emotionality <- c("0.005","-0.024", "-0.098", "0.256", "-0.225", "-0.054", "-0.013", "0.058")
rE_emotionality_error_intervals <- c("(-0.255, 0.266)", "(0.243, -0.291)", "(-0.261, 0.064)","(-0.021, 0.333)", "(-0.298, 0.048)", "(-0.248, 0.240)", "(-0.266, 0.240)", "(-0.225, 0.241)")
age <- c("slope", "intercept", "36", "30", "24", "18", "12", "6")
df <- data.frame(age, rG_activity, rG_activity_error_intervals, rG_task_persistence, rG_task_persistence_error_intervals, rE_activity, rE_activity_error_intervals, rE_task_persistence, rE_task_persistence_error_intervals, rG_emotionality, rG_emotionality_error_intervals, rE_emotionality, rE_emotionality_error_intervals)

#produce figure
library(data.table)
setDT(df)
df_tidy <- melt(df , measure.vars = list(values=c("rG_activity","rG_task_persistence","rE_activity","rE_task_persistence", "rG_emotionality", "rE_emotionality"),
                              intervals=c("rG_activity_error_intervals","rG_task_persistence_error_intervals","rE_activity_error_intervals","rE_task_persistence_error_intervals", "rG_emotionality_error_intervals","rE_emotionality_error_intervals")))
df_tidy[ , values:=as.numeric(values)]
df_tidy[ , c("lci", "uci") := tstrsplit(gsub("[()]","",intervals),split=",",type.convert = TRUE)]
df_tidy[ , condition := c("rG_activity", "rG_persistence", "rE_activity", "rE_persistence", "rG_emotionality", "rE_emotionality")[variable]]
df_tidy[ , c("what","type") := tstrsplit(condition,split="_")]

ggplot(df_tidy) + 
  aes(x=age, y=values, ymin=lci, ymax=uci,fill=what) + 
  geom_col(position = "dodge", color = "black", width = 0.7) + 
  geom_errorbar(position=position_dodge(width=0.7),width=0.25) + 
  facet_wrap(~type, ncol=1) + 
  scale_fill_grey(
    start = 0.475 ,
    end = 0.8,
    na.value = "red",
    aesthetics = "fill"
  ) +
  geom_text(size=2.75,aes(label=values,y=if_else(values > 0, pmax(uci, lci) + 0.1, pmin(uci, lci) - 0.1)),
            position=position_dodge(width=0.7)) +
  theme_classic() + 
  labs(y = "Correlation") +
  labs(x = "") +
  theme(legend.position = "bottom", legend.title=element_blank(), legend.margin=margin(0, 0, 0, 0)) + 
  scale_x_discrete(limits = c("6", "12", "18", "24", "30", "36", "slope", "intercept"),
                   labels = c("6"="Age 6", "12"="Age 12", "18"="Age 18", "24"="Age 24", "30"="Age 30", "36"="Age 36", "slope", "intercept")) + 
  geom_text(data = unique(df_tidy[is.na(values),],
            by = c("age", "type")), label = "N/A", y = 0, size = 2.5)

Which produces this figure...

enter image description here

I would like to modify the code above to create a custom axis/label under my time points (6, 12, 18, 24, 30, 36). Ideally, I would like to generate something like this (see the bottom most part of the figure).:

enter image description here

I know this can be easily done on paint/photoshop, but it could be awesome if I could build it into my code. Would appreciate any help on how to do this.

ericm
  • 173
  • 7
  • 1
    Does this answer your question? [How to add common line and text as second x-axis label](https://stackoverflow.com/questions/54090154/how-to-add-common-line-and-text-as-second-x-axis-label) – S-SHAAF Mar 16 '23 at 21:10
  • Also relevant: https://stackoverflow.com/a/10542622/3358272 – r2evans Mar 16 '23 at 21:13

1 Answers1

2

We can turn clipping off in coord_cartesian and add a geom_line and geom_text to the bottom facet:

ggplot(df_tidy, aes(age, values)) + 
  geom_col(position = "dodge", color = "black", width = 0.7, aes(fill = what)) + 
  geom_errorbar(aes(ymin = lci, ymax = uci, group = what),
                position = position_dodge(width = 0.7), width = 0.25) + 
  geom_text(aes(label = values, group = what,
                y = if_else(values > 0, 
                            pmax(uci, lci) + 0.1, 
                            pmin(uci, lci) - 0.1)),
            position = position_dodge(width = 0.7), size = 2.75) +
  geom_text(data = unique(df_tidy[is.na(values),], by = c("age", "type")), 
            label = "N/A", y = 0, size = 2.5) +
  geom_line(data = data.frame(age = as.character(c(6, 6, 36, 36)),
                              values = c(-1.1, -1.2, -1.2, -1.1),
                              type = "persistence"), aes(group = 1)) +
  geom_text(data = data.frame(age = "18", values = -1.3, type = "persistence"),
            aes(label = "Age (months)"), hjust = 0) +
  scale_x_discrete(name = "", 
                   limits = c(6 * 1:6, "slope", "intercept"),
                   labels = c("6" = "Age 6", "12" = "Age 12", "18" = "Age 18", 
                              "24" = "Age 24", "30" = "Age 30",
                              "36" = "Age 36", "slope", "intercept")) + 
  scale_fill_grey(start = 0.475, end = 0.8, na.value = "red") +
  facet_wrap(~type, ncol = 1) + 
  coord_cartesian(ylim = c(-0.8, 0.9), clip = "off") +
  labs(y = "Correlation") +
  theme_classic() + 
  theme(legend.position = c(0.9, -0.05), 
        legend.direction = 'horizontal',
        legend.title = element_blank(), 
        legend.margin = margin(0, 0, 0, 0),
        strip.background = element_rect(linewidth = 0.5),
        plot.margin = margin(10, 10, 30, 10))

enter image description here

Allan Cameron
  • 147,086
  • 7
  • 49
  • 87
  • Thanks, very helpful. Two things seem to have gotten thrown off in your solution: 1) The position of the labels now is no longer directly above the error bars. 2) Something seems to have occurred where the upper lines/box surrounding each variable name seems to gotten considerably thicker. How can I fix 1) and 2)? Thank you. – ericm Mar 16 '23 at 23:07
  • 1
    You can do 1) add `group = what` to the `aes` of `geom_text` 2) add`strip.background = element_rect(linewidth = 0.5)` to `theme`. See my update @ericm – Allan Cameron Mar 16 '23 at 23:18
  • Thank you @Allan Cameron. I'm not sure if this necessitates another thread entirely, but I am trying to adjust my figure legend to the right corner area of the figure. Trying to do so by specifying the coordinates with legend.position(), but the legend isn't ending up where I want it. I would like to place it in the right hand corner (like so https://i.imgur.com/wE64eKq.png). Could you help me with this? – ericm Mar 16 '23 at 23:44
  • @ericm add `legend.justification = "right"` in `theme` – Allan Cameron Mar 16 '23 at 23:50
  • That was a thought, but it ends up a little "too far right" than I would like. I'm trying to do it by specifying the coordinates to get it to where I would like in the screenshot. – ericm Mar 16 '23 at 23:52
  • @ericm then use `legend.justification = c(0.8, 0.5)`. Just adjust the 0.8 to some number between 0.5 and 1 that you like best. This is in the docs under `?theme`. Always worth having a look at that first if you get stuck. – Allan Cameron Mar 16 '23 at 23:56
  • I appreciate the response. The first thing that I did was consult the guide and play with the coordinates as you've shown above. The problem is that, to get the legend where I would like it, you would need to specify a negative value. This shouldn't be an issue, but, when I do so, the legend get cuts off from the plot entirely (and even a very slight incremental negative value still doesn't get the legend in the desired location on the screenshot). – ericm Mar 17 '23 at 00:03
  • @ericm I don't understand. Setting it to 0.9, 0.5 puts the legend in the same place that you show in the image you linked - see my update. Or am I misunderstanding you? – Allan Cameron Mar 17 '23 at 00:08
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/252560/discussion-between-ericm-and-allan-cameron). – ericm Mar 17 '23 at 00:13
  • @ericm See my update - because clipping is off, we can have a floating legend based on panel co-ordinates. – Allan Cameron Mar 17 '23 at 01:09