Vraag ggplot2: lijn meerdere grafieken uit met verschillende tussenafstanden en voeg er pijlen tussen toe


Ik heb 6 plots die ik netjes in twee stappen wil uitlijnen (zie afbeelding). Bij voorkeur zou ik leuke pijlen willen toevoegen.

Om het even welke ideeën?


UPD. Toen mijn vraag negatieve feedback begon te krijgen, wil ik verduidelijken dat ik alle (gedeeltelijk) gerelateerde vragen bij SO heb gecontroleerd en geen aanwijzingen heb gevonden over hoe je ggplots vrij op een "canvas" kunt positioneren. Bovendien kan ik geen enkele manier bedenken om pijlen te tekenen tussen de plots. Ik vraag niet om een ​​kant-en-klare oplossing. Geef alsjeblieft de weg aan.

enter image description here


11
2018-02-25 15:43


oorsprong


antwoorden:


Hier is een poging tot de lay-out die u wilt. Het vereist enige opmaak met de hand, maar u kunt waarschijnlijk een groot deel hiervan automatiseren door gebruik te maken van het coördinatensysteem dat in de plotlay-out is ingebouwd. Misschien vindt u dat ook grid.curve is beter dan grid.bezier (die ik heb gebruikt) om de pijlcurven precies zo te krijgen als u wilt.

Ik weet precies genoeg over grid om gevaarlijk te zijn, dus ik zou geïnteresseerd zijn in suggesties voor verbeteringen. Hoe dan ook, hier gaat ...

Laad de pakketten die we nodig hebben, maak een paar hulpprogramma's grid objecten en maak een plot om in te lay-out:

library(ggplot2)
library(gridExtra)

# Empty grob for spacing
#b = rectGrob(gp=gpar(fill="white", col="white"))  
b = nullGrob() # per @baptiste's comment, use nullGrob() instead of rectGrob()

# grid.bezier with a few hard-coded settings
mygb = function(x,y) {
  grid.bezier(x=x, y=y, gp=gpar(fill="black"), 
              arrow=arrow(type="closed", length=unit(2,"mm")))
}

# Create a plot to arrange
p = ggplot(mtcars, aes(wt, mpg)) +
  geom_point()

Maak de hoofdplotopstelling. Gebruik de lege grob b die we hierboven hebben gemaakt voor het uit elkaar plaatsen van de plots:

grid.arrange(arrangeGrob(p, b, p, p, heights=c(0.3,0.1,0.3,0.3)),
             b,
             arrangeGrob(b, p, p, b, p, heights=c(0.07,0.3, 0.3, 0.03, 0.3)),
             ncol=3, widths=c(0.45,0.1,0.45))

Voeg de pijlen toe:

# Switch to viewport for first set of arrows
vp = viewport(x = 0.5, y=.75, width=0.09, height=0.4)
pushViewport(vp)

#grid.rect(gp=gpar(fill="black", alpha=0.1)) # Use this to see where your viewport is located on the full graph layout

# Add top set of arrows
mygb(x=c(0,0.8,0.8,1), y=c(1,0.8,0.6,0.6))
mygb(x=c(0,0.6,0.6,1), y=c(1,0.4,0,0))

# Up to "main" viewport (the "full" canvas of the main layout)
popViewport()

# New viewport for lower set of arrows
vp = viewport(x = 0.6, y=0.38, width=0.15, height=0.3, just=c("right","top"))
pushViewport(vp)

#grid.rect(gp=gpar(fill="black", alpha=0.1))  # Use this to see where your viewport is located on the full graph layout

# Add bottom set of arrows
mygb(x=c(1,0.8,0.8,0), y=c(1,0.9,0.9,0.9))
mygb(x=c(1,0.7,0.4,0), y=c(1,0.8,0.4,0.4))

En hier is de resulterende plot:

enter image description here


15
2018-02-25 17:25



Waarschijnlijk met ggplot met annotation_custom hier is een meer handige benadering. Eerst genereren we voorbeeldplots.

require(ggplot2)
require(gridExtra)
require(bezier)

# generate sample plots
set.seed(17)
invisible(
  sapply(paste0("gg", 1:6), function(ggname) {
    assign(ggname, ggplotGrob(
      ggplot(data.frame(x = rnorm(10), y = rnorm(10))) +
        geom_path(aes(x,y), size = 1, 
                  color = colors()[sample(1:length(colors()), 1)]) +
        theme_bw()), 
           envir = as.environment(1)) })
)

Daarna kunnen we ze in een grotere plot plotten ggplot.

# necessary plot
ggplot(data.frame(a=1)) + xlim(1, 20) + ylim(1, 32) +
  annotation_custom(gg1, xmin = 1, xmax = 9, ymin = 23, ymax = 31) +
  annotation_custom(gg2, xmin = 11, xmax = 19, ymin = 21, ymax = 29) +
  annotation_custom(gg3, xmin = 11, xmax = 19, ymin = 12, ymax = 20) +
  annotation_custom(gg4, xmin = 1, xmax = 9, ymin = 10, ymax = 18) +
  annotation_custom(gg5, xmin = 1, xmax = 9, ymin = 1, ymax = 9) +
  annotation_custom(gg6, xmin = 11, xmax = 19, ymin = 1, ymax = 9) +
  geom_path(data = as.data.frame(bezier(t = 0:100/100, p = list(x = c(9, 10, 10, 11), y = c(27, 27, 25, 25)))),
            aes(x = V1, y = V2), size = 1, arrow = arrow(length = unit(.01, "npc"), type = "closed")) +
  geom_path(data = as.data.frame(bezier(t = 0:100/100, p = list(x = c(9, 10, 10, 11), y = c(27, 27, 18, 18)))),
            aes(x = V1, y = V2), size = 1, arrow = arrow(length = unit(.01, "npc"), type = "closed")) +
  geom_path(data = as.data.frame(bezier(t = 0:100/100, p = list(x = c(15, 15, 12, 9), y = c(12, 11, 11, 11)))),
            aes(x = V1, y = V2), size = 1, arrow = arrow(length = unit(.01, "npc"), type = "closed")) +
  geom_path(data = as.data.frame(bezier(t = 0:100/100, p = list(x = c(15, 15, 12, 9), y = c(12, 11, 11, 9)))),
            aes(x = V1, y = V2), size = 1, arrow = arrow(length = unit(.01, "npc"), type = "closed")) +
  geom_path(data = as.data.frame(bezier(t = 0:100/100, p = list(x = c(15, 15, 12, 12), y = c(12, 10.5, 10.5, 9)))),
            aes(x = V1, y = V2), size = 1, arrow = arrow(length = unit(.01, "npc"), type = "closed")) +
  theme(rect = element_blank(),
        line = element_blank(),
        text = element_blank(),
        plot.margin = unit(c(0,0,0,0), "mm"))

Hier gebruiken we bezier functie van bezier pakket om coördinaten voor te genereren geom_path. Misschien moet men op zoek gaan naar wat extra informatie over bezier-curven en hun controlepunten om verbindingen tussen percelen er mooier uit te laten zien. Nu volgt de resulterende plot. Resulting plot


3
2018-02-26 12:39



Heel erg bedankt voor je tips en vooral @ eipi10 voor een daadwerkelijke implementatie ervan - het antwoord is geweldig. Ik heb een inboorling gevonden ggplot oplossing die ik wil delen.

UPD Terwijl ik dit antwoord aan het typen was, plaatste @inscaven zijn antwoord met eigenlijk hetzelfde idee. De bezier pakket geeft meer vrijheid om nette gebogen pijlen te maken.


ggplot2::annotation_custom

De eenvoudige oplossing is om te gebruiken ggplot's annotation_custom om de 6 plots te positioneren over de "canvas" ggplot.

Het script

Stap 1. Laad de vereiste pakketten en maak de lijst met 6 vierkante ggplots. Mijn eerste behoefte was om 6 kaarten te regelen, dus ik trigger theme parameter dienovereenkomstig.

library(ggplot2)
library(ggthemes)
library(gridExtra)
library(dplyr)

p <- ggplot(mtcars, aes(mpg,wt))+
        geom_point()+
        theme_map()+
        theme(aspect.ratio=1,
              panel.border=element_rect(color = 'black',size=.5,fill = NA))+
        scale_x_continuous(expand=c(0,0)) +
        scale_y_continuous(expand=c(0,0)) +
        labs(x = NULL, y = NULL)

plots <- list(p,p,p,p,p,p)

Stap 2. Ik maak een gegevensframe voor de canvasplot. Ik weet zeker dat er een betere manier is om dit te doen. Het idee is om een ​​30x20 canvas zoals een A4-vel te krijgen.

df <- data.frame(x=factor(sample(1:21,1000,replace = T)),
                 y=factor(sample(1:31,1000,replace = T)))

Stap 3. Teken het canvas en plaats het vierkante plot eroverheen.

canvas <- ggplot(df,aes(x=x,y=y))+

        annotation_custom(ggplotGrob(plots[[1]]),
                          xmin = 1,xmax = 9,ymin = 23,ymax = 31)+

        annotation_custom(ggplotGrob(plots[[2]]),
                          xmin = 13,xmax = 21,ymin = 21,ymax = 29)+
        annotation_custom(ggplotGrob(plots[[3]]),
                          xmin = 13,xmax = 21,ymin = 12,ymax = 20)+

        annotation_custom(ggplotGrob(plots[[4]]),
                          xmin = 1,xmax = 9,ymin = 10,ymax = 18)+
        annotation_custom(ggplotGrob(plots[[5]]),
                          xmin = 1,xmax = 9,ymin = 1,ymax = 9)+
        annotation_custom(ggplotGrob(plots[[6]]),
                          xmin = 13,xmax = 21,ymin = 1,ymax = 9)+

        coord_fixed()+
        scale_x_discrete(expand = c(0, 0)) +
        scale_y_discrete(expand = c(0, 0)) +
        theme_bw()

        theme_map()+
        theme(panel.border=element_rect(color = 'black',size=.5,fill = NA))+
        labs(x = NULL, y = NULL)

Stap 4. Nu moeten we de pijlen toevoegen. Ten eerste is een gegevensframe met de coördinaten van pijlen vereist.

df.arrows <- data.frame(id=1:5,
                        x=c(9,9,13,13,13),
                        y=c(23,23,12,12,12),
                        xend=c(13,13,9,9,13),
                        yend=c(22,19,11,8,8))

Stap 5. Breng tenslotte de pijlen in kaart.

gg <- canvas + geom_curve(data = df.arrows %>% filter(id==1),
                    aes(x=x,y=y,xend=xend,yend=yend),
                    curvature = 0.1, 
                    arrow = arrow(type="closed",length = unit(0.25,"cm"))) +
        geom_curve(data = df.arrows %>% filter(id==2),
                   aes(x=x,y=y,xend=xend,yend=yend),
                   curvature = -0.1, 
                   arrow = arrow(type="closed",length = unit(0.25,"cm"))) +
        geom_curve(data = df.arrows %>% filter(id==3),
                   aes(x=x,y=y,xend=xend,yend=yend),
                   curvature = -0.15, 
                   arrow = arrow(type="closed",length = unit(0.25,"cm"))) +
        geom_curve(data = df.arrows %>% filter(id==4),
                   aes(x=x,y=y,xend=xend,yend=yend),
                   curvature = 0, 
                   arrow = arrow(type="closed",length = unit(0.25,"cm"))) +
        geom_curve(data = df.arrows %>% filter(id==5),
                   aes(x=x,y=y,xend=xend,yend=yend),
                   curvature = 0.3, 
                   arrow = arrow(type="closed",length = unit(0.25,"cm"))) 

Het resultaat

ggsave('test.png',gg,width=8,height=12)

enter image description here


2
2018-02-26 13:04