rcolorsvisualizationcategorical-datalinegraph

R - How to change the color of line based on the value of Y-axis?


I am trying to change the color of a line in a line graph based on the Y axis value.

My specific application: I am visualizing wind speed and direction. Ideally I'd do this with a rose plot, but I am comparing it to other data that is presented in line graphs. I currently have wind direction in degrees, but it is understandably difficult to quickly interpret. I would like to classify the line into 8 differently-colored categories based on cardinal directions (N, NE, E, SE, S, SW, W, NW) so it's easier to understand the wind direction. Nevermind the potentially overwhelming number of colors in the figure, I just want to give this a shot as what I currently have is quite difficult to interpret.

I am very rusty in R and am not equipped with the proper terminology to search, nor do I have a good idea of where to start.

Is this possible? Would it require breaking up the series into many different line segments?

A solution in R or even Excel would be appreciated.

Edit: Here is an example of what the data looks like

# random wind speed and direction over 24 hours, hourly
set.seed(123)

date_time <- seq.POSIXt(ISOdate(2023,1,20, hour = 00), ISOdate(2023, 1, 20, hour = 23), "hour")
wind_speed <- runif(24, 0, 20)
wind_direction <- runif(24, 0, 359)
df_wind <- data.frame(date_time, wind_speed, wind_direction)

Solution

  • The idea of a discrete-time x-axis makes this a bit challenging because, for instance, the wind direction may change across all directions over the course of each unit of time. That would make a blended colored line - or even a segmented colored line - quite confusing to look at.

    One alternative option would be to put bands representing the the directions in the background, for instance:

    enter image description here

    The code to recreate this plot is below, which uses rect and adjustcolor to create the transparent bands. I also created a named vector directs to simplify the direction categories.

    ## Identify directions
    directs <- setNames(seq(0, 315, by = 45),
                        c("North", "North-east", "East",
                          "South-east", "South", "South-west", 
                          "West", "North-west"))
    # Initiate blank plot
    plot(df_wind$date_time, df_wind$wind_direction, type = "n",
         bty = "n", xlab = "Time", ylab = "Direction",
         ylim = c(0, 360))
    # Plot bands and text
    rect(xleft = min(df_wind$date_time), xright = max(df_wind$date_time),
           ybottom = directs, ytop = c(directs[-1], 360),
           col = adjustcolor(1:8, alpha.f = 0.2), border = NA)
    text(names(directs), pos = 4,
           x = min(df_wind$date_time), y = directs + 22.5, col = 1:8)
    text(names(directs), pos = 2,
           x = max(df_wind$date_time), y = directs + 22.5, col = 1:8)
    # Plot lines over bands
    lines(df_wind$date_time, df_wind$wind_direction)
    

    Another option is to plot the colors of the points, not the lines, like this:

    enter image description here

    For this I created a new variable in the dataset df_wind$direction:

    df_wind <- dplyr::mutate(df_wind, direction = dplyr::case_when(
      wind_direction <= 45 ~ "North",
      wind_direction > 45 & wind_direction <= 90 ~ "North-east",
      wind_direction > 90 & wind_direction <= 135 ~ "East",
      wind_direction > 135 & wind_direction <= 180 ~ "South-east",
      wind_direction > 180 & wind_direction <= 225 ~ "South",
      wind_direction > 225 & wind_direction <= 270 ~ "South-west",
      wind_direction > 270 & wind_direction <= 315 ~ "West",
      wind_direction > 315 ~ "North-west"
    ))
    
    
    dirs <- names(directs)
      
    plot(df_wind$date_time, df_wind$wind_direction, type = "l",
           bty = "n", xlab = "Time", ylab = "Direction",
           ylim = c(0, 360))
    for(i in seq_along(unique(df_wind$direction))){
      points(df_wind$date_time[df_wind$direction == dirs[i]], 
            df_wind$wind_direction[df_wind$direction == dirs[i]], 
            pch = 21, bg = i)
    }
    legend("topright", dirs, pch = 21, pt.bg = 1:8, bty = "n", ncol = 2)
    
    

    Or why not both?

    enter image description here

    plot(df_wind$date_time, df_wind$wind_direction, type = "l",
           bty = "n", xlab = "Time", ylab = "Direction",
           ylim = c(0, 360))
    rect(xleft = min(df_wind$date_time), xright = max(df_wind$date_time),
         ybottom = directs, ytop = c(directs[-1], 360),
         col = adjustcolor(1:8, alpha.f = 0.2), border = NA)
    for(i in seq_along(unique(df_wind$direction))){
      points(df_wind$date_time[df_wind$direction == dirs[i]], 
            df_wind$wind_direction[df_wind$direction == dirs[i]], 
            pch = 21, bg = i)
    }
    legend("topright", dirs, pch = 21, pt.bg = 1:8, bty = "n", ncol = 2)
    

    Lastly, you could color the lines (and add colored dots - to remove them just take out the points loop) based on the more recent position, as described in your comment, but again you can see the confusion.

    enter image description here

    plot(df_wind$date_time, df_wind$wind_direction, type = "n",
         bty = "n", xlab = "Time", ylab = "Direction",
         ylim = c(0, 360))
    for(i in 1:nrow(df_wind)){
      if(i != nrow(df_wind)){
        lines(df_wind$date_time[i:(i + 1)], df_wind$wind_direction[i:(i + 1)],
              col = grep(df_wind$direction[i+1], names(directs)))
      }
    for(i in seq_along(unique(df_wind$direction))){
      points(df_wind$date_time[df_wind$direction == dirs[i]], 
             df_wind$wind_direction[df_wind$direction == dirs[i]], 
             pch = 21, bg = i)
    }
    }