3

I am facing some problem to have one plot instead of two from separate data frames. I explained the situation a bit below. The data frames look like:

df1 <- structure(list(value = c(9921L, 21583L, 11822L, 1054L, 13832L, 
16238L, 13838L, 20801L, 20204L, 13881L, 19935L, 13829L, 14012L, 
20654L, 13862L, 21191L, 3777L, 15552L, 13817L, 20428L, 16850L, 
21003L, 11072L, 22477L, 12321L, 12856L, 16295L, 11431L, 13469L, 
14680L, 10552L, 15272L, 9132L, 9374L, 15123L, 22754L, 10363L, 
12160L, 13729L, 11151L, 11451L, 11272L, 14900L, 14688L, 17133L, 
7315L, 7268L, 6262L, 72769L, 7650L, 16389L, 13027L, 7134L, 6465L, 
6490L, 15183L, 7201L, 14070L, 11210L, 10146L), limit = structure(c(1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 
3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L), .Label = c("1Mbit", 
"5Mbit", "10Mbit"), class = "factor")), class = "data.frame", row.names = c(NA, 
-60L))

df2 <- structure(list(value = c(37262L, 39881L, 30914L, 32976L, 28657L, 
39364L, 39915L, 30115L, 29326L, 36199L, 37976L, 36694L, 33718L, 
36945L, 33182L, 35866L, 34188L, 33426L, 32804L, 34986L, 29355L, 
30470L, 37420L, 26465L, 28975L, 29144L, 27491L, 30507L, 27146L, 
26257L, 31231L, 30521L, 30370L, 31683L, 33774L, 35654L, 34172L, 
38554L, 38030L, 33439L, 34817L, 31278L, 33579L, 31175L, 31001L, 
29908L, 31658L, 33381L, 28709L, 34794L, 34154L, 30157L, 33362L, 
30363L, 31097L, 29116L, 27703L, 31229L, 30196L, 30077L), limit = structure(c(3L, 
3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 
3L, 3L, 3L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L), .Label = c("180ms", 
"190ms", "200ms"), class = "factor")), class = "data.frame", row.names = c(NA, 
-60L))

from the data frames above, I have these plots:

limit_bw <- factor(df1$limit, levels = c("1Mbit", "5Mbit", "10Mbit"))
limit_lt <- factor(df2$limit, levels = c("200ms", "190ms", "180ms"))

(to use them sequentially)

bw_line <- ggplot(df1, aes(x = limit_bw, y = value, group=1)) + geom_quantile(method = "loess")
lt_line <- ggplot(df2, aes(x = limit_lt, y = value, group=1)) + geom_quantile(method = "loess")

(I actually have many data so I used geom_quantile())

And also two plots in a grid using rbind/cbind (which is not I want now):

grid.draw(rbind(ggplotGrob(ggplot(df1, aes(limit_bw,value,group=1)) + geom_quantile(method = "loess") + labs(title = "value vs bw",x="bandwidth",y="value")),
ggplotGrob(ggplot(df2, aes(limit_lt, value, group = 1)) + geom_quantile(method="loess") + labs(title="value vs latency", x="latency", y="value")), size = "last"))

I am seeking your help to merge them together into one plot (putting bw_line and lt_line together in the same graph) showing two x-axes either at the top and bottom or two axes in the bottom mentioning their title. Please note, the value has different range for each of the data set. However I need to show two y-axes for separate ranges for each data frame or may be one y-axis showing all the values (min to max) from the both data frame.

I actually seen one very close solution here from @RichieCotton but could not figure out for my data since I have some factors instead of integer values.

I really appreciate your help. Thank you.

r2evans
  • 141,215
  • 6
  • 77
  • 149
temp
  • 82
  • 1
  • 10

2 Answers2

7

I think it's probably easiest to approach this by combining the data into one data frame first. Here I make combined x-values and map your data to those. Then we map as usual, with the addition of a secondary y axis.

library(tidyverse); library(forcats)

# Create shared x axis and combine data frames
limit_combo <- data.frame(level_num = 1:3, 
                          level = as_factor(c("1Mbit\n200ms",
                                              "5Mbit\n190ms",
                                              "10Mbit\n180ms"))) 
df1b <- df1 %>%
  mutate(level_num = limit %>% as.numeric) %>%
  left_join(limit_combo)
df2b <- df2 %>%
  mutate(level_num = 4 - (limit %>% as.numeric)) %>%
  left_join(limit_combo)
df3 <- bind_rows(df1b, df2b, .id = "plot") %>%
  mutate(plot = if_else(plot == "1", "bw", "lt"))

# plot with adjusted y values and second axis for reference
ggplot(df3, aes(x = level, 
                y = value * if_else(plot == "lt", 0.44, 1), 
                group=plot, color = plot)) + 
  geom_quantile(method = "loess") +
  scale_y_continuous("value", sec.axis = sec_axis(~./0.44)) +
  theme(axis.text.y.left  = element_text(color = "#F8766D"),
        axis.text.y.right = element_text(color = "#00BFC4"))

enter image description here

Jon Spring
  • 55,165
  • 4
  • 35
  • 53
5

Here is a different approach to create a single plot from the two datasets which avoids to combine both datasets into one and deal with the factors of limit. df1, df2, limit_bw, and limit_lt are used as given by the OP.

The plot is refined in three steps.

1. Common x axis, common y scale

library(ggplot2)
ggplot() + aes(y = value) +
  geom_quantile(aes(x = as.integer(limit_bw), colour = "bw"), df1, method = "loess") + 
  geom_quantile(aes(x = as.integer(limit_lt), colour = "lt"), df2, method = "loess") +
  scale_x_continuous("limit",
    breaks = 1:nlevels(limit_bw), 
    labels = paste(levels(limit_bw), levels(limit_lt), sep = "\n")) +
  scale_colour_discrete(NULL)

enter image description here

2. Separate x axes, common y scale

library(ggplot2)
ggplot() + aes(y = value) +
  geom_quantile(aes(x = as.integer(limit_bw), colour = "bw"), df1, method = "loess") + 
  geom_quantile(aes(x = as.integer(limit_lt), colour = "lt"), df2, method = "loess") +
  scale_x_continuous("limit",
                     breaks = 1:nlevels(limit_bw), 
                     labels = levels(limit_bw), 
                     sec.axis = dup_axis(labels = levels(limit_lt))) +
  scale_colour_manual(NULL, values = c(bw = "blue", lt = "red")) +
  theme(axis.text.x.bottom = element_text(color = "blue"),
        axis.text.x.top    = element_text(color = "red"))

enter image description here

3. Separate x axes, separate y axes

Here, the y-values of the second dataset are scaled such that the min and max values of the two datasets will coincide.

# compute scaling factor and offset
library(magrittr)   # used to improve readability
bw_rng <- loess(df1$value ~ as.integer(limit_bw)) %>% fitted() %>% range()
lt_rng <- loess(df2$value ~ as.integer(limit_lt)) %>% fitted() %>% range()
scl <- diff(bw_rng) / diff(lt_rng)
ofs <- bw_rng[1] - scl * lt_rng[1]
library(ggplot2)
ggplot() +
  geom_quantile(aes(x = as.integer(limit_bw), y = value, colour = "bw"), 
                df1, method = "loess") + 
  geom_quantile(aes(x = as.integer(limit_lt), y = scl * value + ofs, colour = "lt"), 
                df2, method = "loess") +
  scale_x_continuous("limit",
                     breaks = 1:nlevels(limit_bw), 
                     labels = levels(limit_bw), 
                     sec.axis = dup_axis(labels = levels(limit_lt))) +
  scale_y_continuous(sec.axis = sec_axis(~ (. - ofs) / scl)) +
  scale_colour_manual(NULL, values = c(bw = "blue", lt = "red")) +
  theme(axis.text.x.bottom = element_text(color = "blue"),
        axis.text.x.top    = element_text(color = "red"),
        axis.text.y.left   = element_text(color = "blue"),
        axis.text.y.right  = element_text(color = "red"))

enter image description here

Community
  • 1
  • 1
Uwe
  • 41,420
  • 11
  • 90
  • 134
  • 1
    @samm: If this or any answer has solved your question please consider [accepting it](https://meta.stackexchange.com/q/5234/179419) by clicking the check-mark. This will help future readers who might run into the same problem. It will also give some reputation to both the answerer and yourself. There is no obligation to do this. – Tung Dec 30 '18 at 21:29