1

I have multiple datasets that need to be run through a single graphing function to produce a set of graphs. The datasets contain measurements from two Instruments "G" and "H". Each Instrument is either placed in the "Up" or "Down" position, which changes across the datasets. Right now I am trying to solidify the code before putting it in a function.

I need to assign the same color to the same Position in every graph, and need to label the series with both the Instrument and Position. So "Position" == "Up & "Instrument" == "G" and "Position" == "Up" & "Instrument" == "H" need the same color. I would like to use the wesanderson package, "Cavalcanti1" palette, colors 2 and 3. Position "Up" as color 2 and Position "Down" as color 3.

This Question and This Question are almost helpful, but my series names change with every dataset. I thought using regular expression to identify "Up" or "Down" strings in the legend_title column in a term like scale_color_manual(values = c("foo" = "#999999", "bar" = "#E69F00")) might be a solution, but I keep getting errors in my graphing code.

I am just starting to learn more about regular expression. Can you use it outside of regular expression functions like grep()? Or does scale_color_manual() accept that syntax to assign specific colors to values? Any thoughts or suggestions are appreciated!

Here is a simplified version of my data and code:

library(dplyr)
library(ggplot2)
library(hms)
library(wesanderson) # color palette I want to use

df1 <- data.frame(Time = as.hms(c("11:30:00", "11:30:30", "11:31:00", "11:30:00", "11:30:30", "11:31:00")),
                  Chl = c(3.1,3.6,4,2.2,2,1.8),
                  Instrument = c('H','H','H','G','G','G'),
                  Position = c('Up','Up','Up','Down','Down','Down'))
df1$Instrument <- as.character(df1$Instrument) #to mimic my actual data
df1$Position <- as.character(df1$Position) 

df2 <- data.frame(Time = as.hms(c("09:30:00", "09:30:30", "09:31:00", "09:30:00", "09:30:30", "09:31:00")),
                  Chl = c(3.0,3.5,3.7,1.5,1.3,1.0),
                  Instrument = c('H','H','H','G','G','G'),
                  Position = c('Down','Down','Down','Up','Up','Up'))
df2$Instrument <- as.character(df2$Instrument)#to mimic my actual data
df2$Position <- as.character(df2$Position)

### test code chunks for function. Paste in df1 or df2
modify_df <- df2 %>% 
  mutate(legend_title = paste0(Position, ' ', Instrument))
# Create column with desired series names/labels

(one_plot = ggplot(data = modify_df, aes(x = Time, y = Chl, color = legend_title)) +
    geom_line(size = .5) +
    scale_color_manual(values = c("^Down" = wes_palette("Cavalcanti1")[3],
                                  "^Up" = wes_palette("Cavalcanti1")[2])) + # regular expression to look for Up or Down at the beginning of the text 
    theme(axis.text.x = element_text(angle = 45, hjust = 1),
          legend.title = element_blank()) +
    labs(x = ""))
Skiea
  • 117
  • 7
  • Looks like you left out a dependency on the `hms` package. – ulfelder Jan 03 '20 at 19:40
  • 1
    If `Position` is the only value that determines color, then why not use `color = Position`? Why do you need to parse `legend_title` to get the Up/Down strings? – andrew_reece Jan 03 '20 at 19:46

3 Answers3

0

Just call out the four combinations in scale_color_manual:

    scale_color_manual(values = c("Down G" = wes_palette("Cavalcanti1")[3],
                                  "Down H" = wes_palette("Cavalcanti1")[3],
                                  "Up G" = wes_palette("Cavalcanti1")[2],
                                  "Up H" = wes_palette("Cavalcanti1")[2]))
Kent Johnson
  • 3,320
  • 1
  • 22
  • 23
0

Here is a more general solution which will sort the labels based on the position value.

I didn't have wesanderson package installed, so I substituted in the colors green and red. In this case ggplot will sort the legend based on color thus green is first and red is second. This order my change with your color combination, if so remove decreasing =TRUE from the order function.

With that fact, it is a matter of getting the labels in the proper order. In this case I sorted the two based TRUE/FALSE for containing the characters "Up" in the string. Now pass the ordered labels vector to the ggplot call.

See comments for detail.

modify_df<-df2
#Create new column setting color to desired pallete
modify_df$color<-ifelse(modify_df$Position == "Up", "green", "red")
#create the labels 
modify_df$label<-paste(modify_df$Instrument, modify_df$Position)

#Create labels and sort labels so that the one contain Up is always first
labels<-unique(modify_df$label)
labels<-labels[order(grepl("Up", labels), decreasing =TRUE)]

#plot
one_plot = ggplot(data = modify_df, aes(x = Time, y = Chl, color = color)) +
    geom_line(size = .5) +
    scale_color_identity(guide="legend" , label=labels)  +
    theme(axis.text.x = element_text(angle = 45, hjust = 1),
          legend.title = element_blank()) +
    labs(x = "")

one_plot

##removed added columns if necessary
#modify_df<-modify_df %>% select(-color, -label)
Dave2e
  • 22,192
  • 18
  • 42
  • 50
  • Thanks for you answer! I've been trying to understand `grepl()` by reading through the documentation. I thought the output was `True` or `False`. Would you term `labels<-labels[order(grepl("Up", labels), decreasing =TRUE)]` be sorting the `True` or `False` output from `grepl()`? – Skiea Jan 16 '20 at 18:16
  • @Skiea, Yes, that is correct. In the legend, I wanted the label with the "Up" in it to be the first label. With Up/Down as the suffix, the way I did that, was to identify the label with "Up" in (via the grepl) and then sort decreasing (True/1/Up to False/0/not-up). – Dave2e Jan 16 '20 at 20:15
0

You could do this by using conditions in your call to scale_color_manual, e.g.

ggplot(modify_df, aes(x = Time, y = Chl, color = legend_title)) +
  geom_line(size = 0.5) +
  scale_color_manual(values = sapply(modify_df$legend_title, function(i) ifelse(grepl("Down", i), 
                                       wes_palette("Cavalcanti1")[3],
                                       wes_palette("Cavalcanti1")[2]))) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1),
        legend.title = element_blank()) +
  labs(x = "")

If your set of conditions and colors grows, you could use a series of if statements in place of that ifelse.

ulfelder
  • 5,305
  • 1
  • 22
  • 40