1

I want to plot two weekly averaged time series (from two different dataframes representing different instruments) using ggplot2. This should be simple, but I must be missing something. I've looked at the following posts:

using-both-geom-point-and-geom-line-for-multiple-x-in-ggplot2 object-not-found-error-with-ggplot2-when-adding-shape-aesthetic

and good old cookbook for r but I keep running into error after error. The dataframes I'm using come from summarizing using ddply, and they are here for reproducibility:

mean_TS_Cond_use<-
structure(list(week_DOY = c(207, 207, 230, 230, 237, 237, 237, 
239, 239, 239, 246, 246, 246, 253, 253, 253, 260, 267, 267, 281, 
281, 281, 288, 288, 288, 295, 295, 316, 316, 323, 323, 330, 330, 
330, 337, 337), Leaf.age.ordered = structure(c(1L, 4L, 1L, 3L, 
1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 1L, 2L, 3L, 3L, 2L, 3L, 1L, 
2L, 3L, 1L, 2L, 3L, 2L, 3L, 2L, 3L, 2L, 3L, 1L, 2L, 3L, 2L, 3L
), .Label = c("young", "mature", "old", "old1"), class = "factor"), 
    week_N_Cond = c(7L, 2L, 7L, 2L, 4L, 6L, 3L, 6L, 2L, 10L, 
    3L, 6L, 7L, 2L, 5L, 4L, 1L, 3L, 1L, 3L, 3L, 6L, 4L, 11L, 
    2L, 5L, 4L, 4L, 6L, 2L, 3L, 6L, 20L, 7L, 6L, 2L), week_mean_Cond = c(46.675, 
    28, 38.125, 59.1, 23.5333333333333, 101.5, 58.1333333333333, 
    16.8, 35.5, 62.4, 31.4, 144, 49.3, 49.7, 55.6333333333333, 
    57.65, 7.3, 4.74, NaN, 69.4, 112.3, 80.35, 47.85, 21.6416666666667, 
    6.41, 70.3333333333333, 59.1, 41.6, 24.9666666666667, 64.3, 
    NaN, 39.1, 95.8909090909091, 44.7333333333333, 20.9733333333333, 
    40), week_sd_Cond = c(17.6941374471885, NA, 24.1760728820874, 
    17.1119841047145, 18.1934970067146, 86.4448379025607, 43.4743985965687, 
    NA, NA, NA, NA, 1.4142135623731, 9.61665222413704, NA, 30.8034630087809, 
    28.0721392131059, NA, 1.40007142674936, NA, 31.5912962697006, 
    23.0774781984514, 20.545478010177, 5.30330085889911, 13.7910353732657, 
    NA, 9.97513575513302, 1.69705627484771, 5.23259018078045, 
    6.02522475376092, NA, NA, 9.33380951166242, 59.2789584008602, 
    7.7693843599949, 20.8945957925329, 33.799704140717)), .Names = c("week_DOY", 
"Leaf.age.ordered", "week_N_Cond", "week_mean_Cond", "week_sd_Cond"
), row.names = c(NA, -36L), class = "data.frame")

mean_TS_Gs_use<-structure(list(week_DOY = c(232, 232, 239, 239, 246, 246, 246, 
267, 267, 267, 281, 316, 316, 316, 323, 323, 330, 330, 330, 337, 
337), Leaf.age.ordered = structure(c(2L, 3L, 1L, 3L, 1L, 2L, 
3L, 1L, 2L, 3L, 3L, 1L, 2L, 3L, 2L, 3L, 1L, 2L, 3L, 2L, 3L), .Label = c("young", 
"mature", "old"), class = "factor"), week_N_GS = c(56L, 49L, 
30L, 30L, 55L, 21L, 54L, 7L, 21L, 19L, 6L, 3L, 8L, 4L, 30L, 15L, 
36L, 99L, 70L, 52L, 23L), week_mean_GS = c(73.2017857142857, 
170.422448979592, 88.1133333333333, 66.4866666666667, 125.794545454545, 
103.247619047619, 70.0981481481481, 154.414285714286, 258.757142857143, 
114.073684210526, 254.15, 167.5, 175.8125, 136.25, 87.9866666666667, 
46.46, 112.455555555556, 111.778787878788, 88.4242857142857, 
169.346153846154, 160.895652173913), week_sd_GS = c(27.4044421818562, 
112.736252423718, 30.7610561377961, 26.4143473727146, 98.1052296302704, 
59.4644819959581, 43.7727299045695, 77.6537062556456, 84.1063943551771, 
67.674177268777, 79.52214157076, 47.4155037935906, 45.4656365527071, 
9.46449505608548, 58.2085118395473, 17.0402800111132, 33.7885563420893, 
97.9779549056591, 76.6287028293478, 130.657736481864, 93.5849467220259
)), .Names = c("week_DOY", "Leaf.age.ordered", "week_N_GS", "week_mean_GS", 
"week_sd_GS"), row.names = c(NA, -21L), class = "data.frame")

Everything is groovy for geom_point and geom_errorbar with the first dataframe:

mGts<-ggplot(data=mean_TS_Cond_use, aes(x = week_DOY, y = week_mean_Cond, color=Leaf.age.ordered, ymax = week_mean_Cond + week_sd_Cond, ymin=week_mean_Cond - week_sd_Cond))+
  geom_point(size=4) +
  geom_errorbar() 
mGts

I tried adding the new time series from the new dataframe like this:

mGts_situ<-mGts + 
  geom_point(aes(x = week_DOY, y = week_mean_GS, color=Leaf.age.ordered), data=mean_TS_Gs_use, size=4, shape=18) +
  geom_errorbar(aes(ymax = week_mean_GS + week_sd_GS, ymin=week_mean_GS - week_sd_GS), data=mean_TS_Gs_use)
mGts_situ

But I get an error that 'object 'week_mean_Cond' not found.' Since ggplot was 'looking' for an object from the first dataframe, I tried getting rid of the inherited aes and moving the definition of 'data=' before the aes call. (I also defined the errorbar limits outside of the ggplot call and other minor changes). Here is the new attempt:

Gs_upper<-mean_TS_Gs_use$week_mean_GS + mean_TS_Gs_use$week_sd_GS
Gs_lower<-mean_TS_Gs_use$week_mean_GS - mean_TS_Gs_use$week_sd_GS

mGts_situ<-mGts + 
  geom_point(data=mean_TS_Gs_use, inherit.aes = FALSE, aes(x = week_DOY, y = week_mean_GS, color=Leaf.age.ordered, ymax = Gs_upper, ymin = Gs_lower), size=4, shape=18) +
  geom_errorbar()+
  scale_x_continuous("DOY", limits = c(200, 350)) +
  scale_y_continuous("Weekly Mean", limits = c(0, 345))+
  theme_bw()
mGts_situ

Which does not give an error about any objects, but it still does not show the error bars for the new dataset ('mean_TS_Gs_use'). You can see that the first plotted dataframe (circles) has the errorbars, but the second plotted dataframe (triangles) does not: Two time series: one missing errorbars

2 Answers 2

2

You can't have your cake and eat it too with inherit.aes, you either inherit everything or specify everything.

In your case, your new data has a different column name for the ymin and ymax, so we will indeed need to set inherit.aes = F in the new geom_errorbar layer, but then we'll need to specify all the aesthetics.

We can save ourselves a little trouble if the ymin and ymax in the original plot are only set at the geom_errorbar level, not the top level:

mGts <-
    ggplot(
        data = mean_TS_Cond_use,
        aes(
            x = week_DOY,
            y = week_mean_Cond,
            color = Leaf.age.ordered
        )
    ) +
    geom_point(size = 4) +
    geom_errorbar(
        # move these down here
        aes(ymax = week_mean_Cond + week_sd_Cond,
            ymin = week_mean_Cond - week_sd_Cond)
    )

With that change, the new geom_point layer is fine, but we'll set inherit.aes = F and respecify aesthetics for the geom_errorbar:

mGts_situ <- mGts +
    geom_point(
        mapping = aes(
            x = week_DOY,
            y = week_mean_GS,
            color = Leaf.age.ordered
        ),
        data = mean_TS_Gs_use,
        size = 4,
        shape = 18
    ) +
    geom_errorbar(
        mapping = aes(
            ymax = week_mean_GS + week_sd_GS,
            ymin = week_mean_GS - week_sd_GS,
            x = week_DOY,
            color = Leaf.age.ordered
        ),
        data = mean_TS_Gs_use,
        inherit.aes = FALSE
    )
mGts_situ
Sign up to request clarification or add additional context in comments.

1 Comment

Aha! Thanks @Gregor, that works (after ditching the comma after Leaf.age.ordered in the first aes).
1

I think this plot will be easier to create if we combine the two data frames:

library(dplyr)
library(ggplot2)

Rename columns so we have common names across the two data frames. Add a new column to differentiate which data frame the source data came from. Then combine the two data frames:

mean_TS_Cond_use = mean_TS_Cond_use %>% 
  rename(week_mean=week_mean_Cond, week_sd=week_sd_Cond) %>%
  mutate(Source="Cond")

mean_TS_Gs_use = mean_TS_Gs_use %>% 
  rename(week_mean=week_mean_GS, week_sd=week_sd_GS) %>%
  mutate(Source="Gs")

df = bind_rows(list(mean_TS_Cond_use, mean_TS_Gs_use))

Reset order for Leaf.age.ordered:

df$Leaf.age.ordered = factor(df$Leaf.age.ordered, levels=c("young","mature","old","old1"))

Convert week_DOY to factor (so dodging will work properly):

df$week_DOY_f = factor(df$week_DOY, levels=min(df$week_DOY):max(df$week_DOY))

Plot with dodging to avoid overlap. The group aesthetic is there to get the dodging right:

pd = position_dodge(0.5)

ggplot(df, aes(x=week_DOY_f, 
               y=week_mean, colour=Source, fill=Source,
               ymax=week_mean + week_sd, ymin=week_mean - week_sd)) +
  geom_errorbar(position=pd, aes(group=interaction(Leaf.age.ordered, Source)), 
                width=0.1, alpha=0.5) +
  geom_point(position=pd, aes(group=interaction(Leaf.age.ordered, Source),
                              size=Leaf.age.ordered), 
             pch=21, color="black", stroke=0.2) +
  theme_bw() +
  scale_size_discrete(range=c(1,3)) +
  guides(size=guide_legend(override.aes=list(fill="grey30")))

The plot is still very busy, but hopefully easier to read:

enter image description here

Or maybe text labels would work better for differentiating age:

ggplot(df, aes(x=week_DOY_f, 
               y=week_mean, colour=Source,
               ymax=week_mean + week_sd, ymin=week_mean - week_sd)) +
  geom_errorbar(position=pd, aes(group=interaction(Leaf.age.ordered, Source)), 
                                 width=0.1, alpha=0.5) +
  geom_label(position=pd, aes(label=toupper(substr(Leaf.age.ordered,1,1)), 
                              group=interaction(Leaf.age.ordered, Source)), 
             fontface="bold", fill="white", label.size=0, size=2.5,
             label.padding=unit(0.05,"lines"), show.legend=FALSE) +
  theme_bw() +
  guides(colour=guide_legend(override.aes=list(alpha=1,lwd=1)))

enter image description here

2 Comments

Nice full-service answer here!
Neat solution! And great to see how you used 'dodge' and the text labels.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.