第 3 章 Layers

  • Geometries: geom_line

    • Aesthetics: x, y, color; size
source("support/time-trend.R")

3.1 Time series data

  • package: lubridate

Basic data types do not have time or date type. Time/Date are class that is constructed on top of character data, like

"2020-02-01"
"2020-02-01 13:58:55"
  • All date/time data sources are normally input as character. It requires parsing for computer to under it is date/time.
lubridate::ymd("2020-02-01")
lubridate::ymd_hms("2020-02-01 13:58:55") # UTC time zone by default
lubridate::ymd_hms("2020-02-01 13:58:55", tz="Asia/Taipei")
date = lubridate::ymd(c("2020-02-01", "2020-04-01",
  "2020-06-01", "2020-09-01"))
data = list()
data$Britain <- 
  data.frame(
    date = date,
    y = c(0, 50, 80, 100)
  )
data$Spain <-
  data.frame(
    date = date,
    y = c(0, 32, 53, 103)
  )
data$Italy <-
  data.frame(
    date = date,
    y = c(0, 50, 60, 99)
  )
ggplot(data=dataAll) +
  geom_line(
    aes(
      x=date,
      y=y
    )
  )
ggplot(data=dataAll) +
  geom_line(
    aes(
      x=date,
      y=y,
      group=country
    )
  )

3.2 Aesthetics inheritance

  • In the past, we have done data inheritance (i.e. data in ggplot()) and both data/aesthetics inheritance (i.e, data and mapping in ggplot()). Actually you can do only aesthetics inheritance as well.
sizeInput = 2 #input$sizeInput
plot <- list()
plot$p1 <- {
  ggplot(
    mapping=aes(
      x=date,
      y=y
    ))+
  geom_line(
    data=data$Britain,
    color="#096fa1",
    size = sizeInput
  )+
  geom_line(
    data=data$Spain,
    color="#ad8c97",
    size = sizeInput
  )+
  geom_line(
    data=data$Italy,
    color = "#983d4d",
    size = sizeInput
  )
}
plot$p1

3.3 Sequence of layers

3.3.1 Line stroke

ggplot(
    mapping=aes(
      x=date,
      y=y
    ))+
  geom_line(
    data=data$Britain,
    color="#096fa1",
    size = sizeInput
  ) + 
  geom_line( # the last geom will be on the top
    data=data$Britain,
    color="black",
    size = sizeInput
  )  
prop = 0.6

ggplot(
    mapping=aes(
      x=date,
      y=y
    ))+
  geom_line( # the last geom will be on the top
    data=data$Britain,
    color="black",
    size = sizeInput
  ) +  
  geom_line(
    data=data$Britain,
    color="#096fa1",
    size = sizeInput*prop
  ) 

3.4 Create your first geom

geom_lineWithStroke_error <- function(data, sizeInput, prop) {
    geom_line( # the last geom will be on the top
      data=data,
      color="black",
      size = sizeInput
    ) +  
    geom_line(
      data=data,
      color="#096fa1",
      size = sizeInput*prop
    ) 
  }
class(plot$p1) # ggplot class
  • a list of 9 that complete the definitions of a plot
gm <- geom_line( # the last geom will be on the top
    data=data$Britain,
    color="black",
    size = sizeInput
  ) 
class(gm) # Layer class
  • an environment that defines only a layer of a specific geometric structure and its aesthetics.

  • part of a ggplot class object.


+ operator must have the preceding object a ggplot object.

geom_lineWithStroke_error(
  data=data$Britain, sizeInput=2, prop=.6
)
  • The error comes from the function body. It uses + on two layer class objects. There is no ggplot object presented.

  • ggplot()+... will always return a ggplot object. So all of below are ggplot objects:

    • ggplot(), ggplot()+geom_point(...), ggplot()+geom_point(...)+geom_line(...)

3.4.1 Layer adding

Instead of using + on each layers (i.e. adding geom one after the other), you can put all geom layers as a list, and use + to add the list all at once.

Other than the conventional adding:

ggplot(
    mapping=aes(
      x=date,
      y=y
    ))+
  geom_line( 
    data=data$Britain,
    color="black",
    size = sizeInput
  ) +  
  geom_line( # the last geom will be on the top
    data=data$Britain,
    color="#096fa1",
    size = sizeInput*prop
  ) 

We can:

ggplot(
  mapping=aes(
    x=date,
    y=y
  )) +
  list(
    geom_line( 
      data=data$Britain,
      color="black",
      size = sizeInput
    ),  
    geom_line( # the last geom will be on the top
      data=data$Britain,
      color="#096fa1",
      size = sizeInput*prop
    ) 
  )

3.4.2 New geom function

A geom function that delivers a mixture of multiple geoms must return the mixture as a list, avoiding using + inside the function body.

geom_lineWithStroke_prototype <- function(data, sizeInput, prop) {
  list(
    geom_line( 
      data=data,
      color="black",
      size = sizeInput
    ), 
    geom_line( # the last geom will be on the top
      data=data,
      color="#096fa1",
      size = sizeInput*prop
    ) 
  )
}
{
  ggplot(
    mapping=aes(
      x=date,
      y=y
    ))+
  geom_lineWithStroke_prototype(
    data=data$Britain, sizeInput, prop
  )+
  geom_lineWithStroke_prototype(
    data=data$Spain, sizeInput, prop
  )+
  geom_lineWithStroke_prototype(
    data=data$Italy, sizeInput, prop
  )
}

3.4.3 dot-dot-dot

geom_lineWithStroke_prototype2 <- function(data, sizeInput, prop, color, stroke, size) {
  list(
    geom_line(
      data=data,
      color=stroke,
      size = sizeInput
    ), 
    geom_line(  # the last geom will be on the top
      data=data,
      color=color,
      size = sizeInput*prop
    ) 
  )
}
{
  ggplot(
    mapping=aes(
      x=date,
      y=y
    ))+
  geom_lineWithStroke_prototype2(
    data=data$Britain, sizeInput=sizeInput, prop=prop,
    color="#096fa1",
    stroke="white"
  )+
  geom_lineWithStroke_prototype2(
    data=data$Spain, sizeInput=sizeInput, prop=prop,
    color="#ad8c97",
    stroke="white"
  )+
  geom_lineWithStroke_prototype2(
    data=data$Italy, sizeInput=sizeInput, prop=prop,
    color="#983d4d",
    stroke="white"
  )
}

geom_lineWithStroke is an extension to an existing geom_line function. It would be better to make the input argument inline with geom_line so as to keep other geom_line options, such as linetype.

help(geom_line)
geom_lineWithStroke <- function(
  mapping = NULL,
  data = NULL,
  stat = "identity",
  position = "identity",
  na.rm = FALSE,
  orientation = NA,
  show.legend = NA,
  inherit.aes = TRUE,
  # set up default makes your function easy to use
  stroke = "white",
  prop = 0.9,
  size = 2, 
  ...){
  list(
    geom_line(
      data=data,
      color=stroke,
      size = size
    ),
    geom_line( # the last geom will be on the top
      mapping = mapping,
      data = data,
      stat = stat,
      position = position,
      na.rm = na.rm,
      orientation = orientation,
      show.legend = show.legend,
      inherit.aes = inherit.aes,
      size = size*prop,
      ...)
  )
}

... is a special argument for function. It means “whatever input whose input name is not specified in function arguments.

  • it can be passed directly into other functions who use ....

  • it can be accessed via list().

mySum <- function(...){
  browser()
  argList <- c(...)
  sum(argList)
}
mySum(2,3)
greeting <- function(name, ...){
  browser()
  argList <- list(...)
  extraGreeting <- ""
  if(length(argList)!=0){
    extraGreeting <- paste(" Your", names(argList), " is ", argList)
  }
  
  cat("Hi ", name, ". ", extraGreeting)
  
  return(xx)
}
greeting("John", age=33)
{
  ggplot(
    mapping=aes(
      x=date,
      y=y
    ))+
  geom_lineWithStroke(
    data=data$Britain,
    color="#096fa1"
  )+
  geom_lineWithStroke(
    data=data$Spain,
    color="#ad8c97"
  )+
  geom_lineWithStroke(
    data=data$Italy,
    color="#983d4d"
  )
}
{
  ggplot(
    mapping=aes(
      x=date,
      y=y
    ))+
  geom_lineWithStroke(
    data=data$Britain,
    color="#096fa1",
    linetype=2
  )+
  geom_lineWithStroke(
    data=data$Spain,
    color="#ad8c97", 
    linetype=3
  )+
  geom_lineWithStroke(
    data=data$Italy,
    color="#983d4d",
    stroke="black",
    size=5
  )
}

3.5 Summary

  • Layers (of geoms) can be added on top of each other through + operator by either:

    • ggplot object + geom1 + geom2; or

    • ggplote object + list(geom1, geom2)

  • When create a new geom function that consists of multiple geoms, use list(geom1, geom2) as function return.

  • When writing a function input argument names:

    • if in definition specifically WITHOUT default: it means something necessary. Users can not ignore.

    • if in definition specifically WITH default: it means something necessary, but there is a good choice of default that users will be happy with it most of the time.

    • ...: a flexible argument especially when our function body has a call to some other function that has ...