load libraries
library(tidyverse)
## ─ Attaching packages ─────────────────────────────────── tidyverse 1.2.1 ─
## ✔ ggplot2 3.0.0 ✔ purrr 0.2.5
## ✔ tibble 1.4.2 ✔ dplyr 0.7.6
## ✔ tidyr 0.8.1 ✔ stringr 1.3.1
## ✔ readr 1.1.1 ✔ forcats 0.3.0
## ─ Conflicts ──────────────────────────────────── tidyverse_conflicts() ─
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
library(grid)
library(gtable)
function
plot_segment_across_facets <- function(p, from=1, to=2, point_id=1,
plotout = F,
gp=gpar(lty=1, alpha=0.5)){
if (TRUE %in% grepl("ggplot", class(p))) {
g <- ggplot_gtable(ggplot_build(p))
} else {
g <- p
}
# collect panel viewport names and index numbers in the grob
panel_vps <- c()
id_n <- c()
for (i in 1:length(g$grobs)) {
if (str_detect(g$layout[i, "name"], "panel") & g$grobs[[i]]$name != "NULL") {
p_name <- g$layout[i, "name"]
panel_vps <- c(panel_vps, p_name)
id_n <- c(id_n, i)
}
}
# preprocessing for converting the panel #
panel_vps %>%
str_replace("panel-", "") %>%
str_split("[\\-\\.]") %>%
map_chr(1) -> ind_col
ind_col <- as.numeric(ind_col)
panel_vps %>%
str_replace("panel-", "") %>%
str_split("[\\-\\.]") %>%
map_chr(2) -> ind_row
ind_row <- as.numeric(ind_row)
my_dim <- c(max(ind_row), max(ind_col))
x <- 1:length(id_n)
L <- length(x)
x[(L+1):(my_dim[1]*my_dim[2])] <- NA
m1 <- as.vector(matrix(x, nrow=my_dim[1], byrow=T))
x2 <- 1:L
xx <- as.vector(!is.na(m1))
xx[xx] <- x2
xx[!xx] <- NA
m2 <- as.vector(matrix(xx, nrow=my_dim[1]))
# convert panel # to match the sequence
from <- m2[m1==from]
from <- from[complete.cases(from)]
to <- m2[m1==to]
to <- to[complete.cases(to)]
# select points to be connected
pnames1 <- names(g$grobs[[id_n[from]]]$children)
pnames2 <- names(g$grobs[[id_n[to]]]$children)
pname1 <- pnames1[str_detect(pnames1, "geom_point.points")]
pname2 <- pnames2[str_detect(pnames2, "geom_point.points")]
p1 <- g$grobs[[id_n[from]]]$children[[pname1[1]]]
p2 <- g$grobs[[id_n[to]]]$children[[pname2[1]]]
g <- with(g$layout[id_n[from],],
gtable_add_grob(g,
moveToGrob(p1$x[point_id],
p1$y[point_id]), t=t, l=l))
g <- with(g$layout[id_n[to],],
gtable_add_grob(g,
lineToGrob(p2$x[point_id], p2$y[point_id], gp=gp),
t=t, l=l))
g$layout$clip <- "off"
if (plotout==TRUE) grid.draw(g)
return(g)
}
sample data
d <- data.frame(x = rnorm(10), y = rnorm(10), id = c("a", "a", "b", "b", "c",
"d", "e", "f", "g", "g"))
d
## x y id
## 1 -0.8424990 1.61146573 a
## 2 0.4055838 -0.38709906 a
## 3 -0.8226788 -0.04705237 b
## 4 -1.4608169 -1.07634814 b
## 5 0.6728395 0.41672519 c
## 6 0.3464522 -0.86289034 d
## 7 -2.3731611 -1.85731566 e
## 8 0.9625376 -1.17604202 f
## 9 1.3791485 1.09029835 g
## 10 0.3123674 -0.41158379 g
plot points
p <- ggplot(d, aes(x, y)) +
geom_point() +
facet_wrap(~id) +
theme_bw() +
theme(
strip.background = element_blank(),
#strip.text.x = element_blank(),
#axis.text.y=element_blank(),
axis.line=element_blank(),
#axis.ticks.y=element_blank(),
axis.title=element_blank()
#axis.text.x = element_blank()
)
plot(p)
connect first point in each facet panels
g <- plot_segment_across_facets(p, 1, 2)
ps <- c(2,3,3,4,4,5,5,6,6,7)
while (length(ps) > 0){
g <- plot_segment_across_facets(g, from=ps[1], to=ps[2])
ps <- ps[-c(1:2)]
}
grid.newpage()
grid.draw(g)