Two Indpendent Y axes with ggplot2

I have OTC Notional outstanding amount, here is the dataData1

My objective is to:

  1. Plot Date against Notional,
  2. Plot Date against Percentage, where percentage is calculated as a percentage change in notional.
    However, I want to calculate percentage in r instead of excel.
  3. Plot Date against (GMV as percentage of Notional) , calculate GMV as percentage of notional in r.

I want to plot this in one graph with two independent y-axes

However I only manage to do number 1. (Plot Date against Notional) here is the code

library("ggplot2")
library("scales")
OTCData <- read.csv("~/r programs/Research/DerivativeMeasure.csv")
OTCData <- mutate(OTCData, Date = as.Date(OTCData$Date, "%m/%d/%Y"))
ggplot(data = OTCData, aes(x = OTCData$Date, y = OTCData$Notional)) +
  geom_boxplot()+
geom_line(aes(x = Date, y = Notional), 
          color = "#09557f",
          alpha = 0.6,
          size = 0.6,
          group =1) +
  labs(x = "Date", 
       y = "Notional Amount") +
  ggtitle("Notional Outstanding Amount of OTC derivatives")+
theme(panel.background = element_rect(fill="white", colour="white", size=0.5, 
                                      linetype="solid", color="grey"),
      plot.title = element_text(hjust = 0.5),
      panel.grid.minor = element_line(size = (0.2), colour="grey"),
      axis.text.x=element_text(angle=60, hjust=1))

Now how can I do the 2 and 3 ?

Hi,

Here is an example:

library(ggplot2)
library(tidyr)

# Generate a data frame with multiple y values
myData = data.frame(x = 1:20, y1 = runif(20), y2 = runif(20) + 0.5)
head(myData)
#>   x        y1        y2
#> 1 1 0.7564309 1.3866425
#> 2 2 0.3545455 0.5058629
#> 3 3 0.7552795 1.2198852
#> 4 4 0.8031028 1.4249139
#> 5 5 0.4583529 0.5573563
#> 6 6 0.7699874 0.8416090

# Convert the data into long format
myData = myData %>% pivot_longer(cols = c(-x), names_to = "group", values_to = "y")
head(myData)
#> # A tibble: 6 x 3
#>       x group     y
#>   <int> <chr> <dbl>
#> 1     1 y1    0.756
#> 2     1 y2    1.39 
#> 3     2 y1    0.355
#> 4     2 y2    0.506
#> 5     3 y1    0.755
#> 6     3 y2    1.22

# Plot the data
ggplot(myData, aes(x = x, y = y, group = group, colour = group)) + geom_line()

Created on 2020-07-21 by the reprex package (v0.3.0)

EXPLANATION
To plot multiple lines in ggplot, you have to have a data frame that has a specific format. There should only be one column for the x values, and one for the y values. The way you distinguish between different curves is by adding another column that says which curve each value belongs to (e.g. the group)

We achieve this by converting the original data frame from a wide to a long format. This is done using the pivot_longer function from the tidyr package. It's a powerful function once you grasp how it works. Basically you tell it which columns you like to make long (in my example all but the x value, or in your case the date). Then we provide an optional name for the new column that hold the group (i.e. old column name) and the value. The result is a data frame with multiple columns reduced to one, and repeating the values of columns that were not affected (in our case x).

This data frame format can now be used in ggplot. You provide the x and y column as usual, but now add another aes argument group. You assign the group we created for the y values to this column, and ggplot will be able to sort out which values go to which curve. The same effect can be achieved by using the colour argument, but this will also provide a different colour to each curve instead of just separating them (remove that if you don't want that).

Hope this helps!
PJ

Oh I see, my bad!

PJ

Thanks @Yarnabrina, this means I will have to calculate the change in the Notional as percentage in r instead of doing it excel, and another issues of sec_axis() how do I scale this the secondary axis?
Let me try

Here's how you can do it from separate columns and build an appropriate legend.

library(tidyverse)

# fake data
df <- tibble(
  date = seq.Date(from = as.Date("2010-01-01"), length.out = 10, by = "year"), 
  n = runif(10, 0, 100000), 
  p = runif(10, 0 , 1) # percent values
)

df
#> # A tibble: 10 x 3
#>    date            n     p
#>    <date>      <dbl> <dbl>
#>  1 2010-01-01 77284. 0.985
#>  2 2011-01-01 67679. 0.224
#>  3 2012-01-01 93586. 0.445
#>  4 2013-01-01 42736. 0.592
#>  5 2014-01-01 44966. 0.785
#>  6 2015-01-01 27145. 0.898
#>  7 2016-01-01 96697. 0.577
#>  8 2017-01-01 88160. 0.967
#>  9 2018-01-01 40810. 0.127
#> 10 2019-01-01 76543. 0.400

# get the rescaling factor from the max of the numerical column and the max percent
n_max <- max(df$n, na.rm = TRUE)
p_max <- max(df$p, na.rm = TRUE)
scaling_factor <- p_max / n_max 

ggplot(df, aes(x = date)) +
  geom_line(aes(y = n, colour = "red"), key_glyph = "timeseries") +
  geom_line(aes(y = p / scaling_factor, colour = "blue"), key_glyph = "timeseries") +
  scale_colour_identity(name = NULL, breaks = c("red", "blue"), labels = c("Notional", "Percent"), guide = "legend") +
  scale_y_continuous(
    name = "Notional Axis", 
    sec.axis = ggplot2::sec_axis(~ . * scaling_factor, 
                                 name = "Percent Axis", 
                                 labels = scales::percent_format(accuracy = 1))
  )

Created on 2020-07-21 by the reprex package (v0.3.0)

2 Likes

Thanks @Paul, will try. I would love to have date as shown here Rplot04
In addition, percentage can be negative or positive.

@paul this is the Data I have used Date

Date Notional
30/06/1998 72106521.77
31/12/1998 80276622.05
30/06/1999 81420274.61
31/12/1999 88156431.71
30/06/2000 93959822.42
31/12/2000 95150854.68
30/06/2001 99648589.78
31/12/2001 111058769.9
30/06/2002 127372621.6
31/12/2002 141513417.2

I calculated percent using Notional_change <-(((Notional_amount[2:n, 1]-Notional_amount[1:(n-1),1])/Notional_amount[1:(n-1),1])*100)

My code are here

library("ggplot2")
library("scales")
library(tidyverse)
OTCData <- read.csv("~/r programs/Research/DerivativeMeasure.csv")
OTCData <- mutate(OTCData, Date = as.Date(OTCData$Date, "%m/%d/%Y"))

Notional_amount <-OTCData[,"Notional", drop=FALSE]
#rownames(Notional_amount) <- OTCData$Date
n <-nrow(Notional_amount)
Notional_change <-(((Notional_amount[2:n, 1]-Notional_amount[1:(n-1),1])/Notional_amount[1:(n-1),1])*100)
class(Notional_change)
names(Notional_change) <- OTCData[2:n, 1]
head(Notional_change)
# get the rescaling factor from the max of the numerical column and the max percent
n_max <- max(OTCData$Notional, na.rm = TRUE)
p_max <- max(Notional_change, na.rm = TRUE)
scaling_factor <- p_max / n_max 

ggplot(OTCData, aes(x = OTCData$Date)) +
  geom_line(aes(y = Notional, color="red"), group =1) +
  geom_line(aes(y = Notional_change / scaling_factor, colour = "blue"), group =1) +
  scale_colour_identity(name = NULL, breaks = c("red", "blue"), labels = c("Notional", "Percent"), guide = "legend") +
  scale_y_continuous(
    name = "Notional Axis", 
    sec.axis = ggplot2::sec_axis(~ . * scaling_factor, 
                                 name = "Percent Axis", 
                                 labels = scales::percent_format(accuracy = 1))
  )

The error I am getting is here:
Version:1.0 StartHTML:0000000107 EndHTML:0000000984 StartFragment:0000000127 EndFragment:0000000966

Error: Aesthetics must be either length 1 or the same as the data (44): y

The problem seem to be transposing Notional_change, how can I do it? please help

This topic was automatically closed 21 days after the last reply. New replies are no longer allowed.

I may have understood wrong, but OP probably wants to stress on multiple independent y axes, so the range of graph may be wildly different.

If that understanding is right, most probably it is not possible in ggplot2.

Here's from the docs (sec_axis):

This function is used in conjunction with a position scale to create a secondary axis, positioned opposite of the primary axis. All secondary axes must be based on a one-to-one transformation of the primary axes.

And, here's one answer by Hadley:

1 Like

Thanks @pieterjanvc, the y-axes are independent, one is notional and the other one is percentage change in notional.

you should chart such a thing more conveniently with plotly which supports independent left y axis and right y axis ('y2')