2015-12-10 21 views
11

Sto provando a scrivere uno script che produce quattro grafici diversi in una singola immagine. In particolare, voglio ricreare il più fedelmente questa grafica il più possibile:R - come allocare lo spazio dello schermo su complesse immagini ggplot

Complex Plot

mio script corrente produce quattro trame simili a queste, ma io non riesco a capire come allocare schermo immobiliare di conseguenza. Voglio:

  1. modificare l'altezza e la larghezza delle piazzole modo che tutti e quattro hanno larghezza uniforme, uno è sostanzialmente più alto degli altri aventi altezza uniforme tra loro
  2. definiscono la posizione delle leggende da coordinate in modo da poter utilizzare lo spazio dello schermo in modo efficace
  3. modificare la forma complessiva della mia immagine in modo esplicito come necessario (forse ho bisogno a forma quadrata più vicino ad un certo punto)

generare qualche DATI tracciare

pt_id = c(1:279) # DEFINE PATIENT IDs 
smoke = rbinom(279,1,0.5) # DEFINE SMOKING STATUS 
hpv = rbinom(279,1,0.3) # DEFINE HPV STATUS 
data = data.frame(pt_id, smoke, hpv) # PRODUCE DATA FRAME 

AGGIUNGERE sito anatomico DATI

data$site = sample(1:4, 279, replace = T) 
data$site[data$site == 1] = "Hypopharynx" 
data$site[data$site == 2] = "Larynx" 
data$site[data$site == 3] = "Oral Cavity" 
data$site[data$site == 4] = "Oropharynx" 
data$site_known = 1 # HACK TO FACILITATE PRODUCING BARPLOTS 

ADD mutazione dati di frequenza

data$freq = sample(1:1000, 279, replace = F) 

DEFINIRE BARPLOT

require(ggplot2) 
require(gridExtra) 
bar = ggplot(data, aes(x = pt_id, y = freq)) + geom_bar(stat = "identity") +  theme(axis.title.x = element_blank(), axis.ticks.x = element_blank(), axis.text.x = element_blank()) + ylab("Number of Mutations") 
# DEFINE BINARY PLOTS 
smoke_status = ggplot(data, aes(x=pt_id, y=smoke, fill = "red")) + geom_bar(stat="identity") + theme(legend.position = "none", axis.title.x = element_blank(), axis.ticks.x = element_blank(), axis.text.x = element_blank()) + ylab("Smoking Status") 
hpv_status = ggplot(data, aes(x=pt_id, y = hpv, fill = "red")) + geom_bar(stat="identity") + theme(legend.position = "none", axis.title.x = element_blank(), axis.ticks.x = element_blank(), axis.text.x = element_blank()) + ylab("HPV Status") 
site_status = ggplot(data, aes(x=pt_id, y=site_known, fill = site)) +  geom_bar(stat="identity") 

produrre quattro GRAFICI INSIEME

grid.arrange(bar, smoke_status, hpv_status, site_status, nrow = 4) 

Sospetto che le funzioni necessarie per svolgere queste attività siano già incluse in ggplot2 e gridExtra ma non sono stato in grado di capire come. Inoltre, se qualcuno del mio codice è eccessivamente prolisso o c'è un modo più semplice e più elegante di fare ciò che ho già fatto, per favore sentiti libero di commentare anche questo.

+0

Per la prima domanda è possibile specificare un layout con 6 righe e quindi inserire il grafico superiore nelle prime 3 righe. Vedi la funzione multiplot qui per [http://www.cookbook-r.com/Graphs/Multiple_graphs_on_one_page_(ggplot2)/] – RHA

+3

Questo genere di cose di solito richiede di fare le cose "manualmente" usando gli strumenti nel pacchetto ** grid ** . Vorrei iniziare osservando 'ggplot_build' e' ggplot_gtable' per scomporre i grafici nei grobs del componente, e quindi probabilmente finirai per costruire il tuo layout ('grid.layout') e disegnare ogni grob in finestre specifiche. – joran

+0

Puoi anche dare un'occhiata al ['gtable' wiki] (https://github.com/baptiste/gtable/wiki/Description) – Henrik

risposta

11

Ecco i passi per ottenere il layout si descrive:

1) Estrarre la leggenda come Grob separato ("oggetto grafico"). Possiamo quindi creare la legenda separatamente dalle trame.

2) Allineare a sinistra i bordi dei quattro grafici in modo che i bordi sinistro e le scale x siano allineati correttamente. Il codice per farlo viene da this SO answer. Quella risposta ha una funzione per allineare un numero arbitrario di grafici, ma non ero in grado di farlo funzionare quando volevo anche cambiare lo spazio proporzionale assegnato a ogni trama, così ho finito per farlo "lungo cammino" di regolando ogni trama separatamente.

3) Disporre i grafici e la legenda utilizzando grid.arrange e arrangeGrob. L'argomento heights assegna proporzioni diverse dello spazio verticale totale a ciascun grafico. Usiamo anche l'argomento widths per allocare lo spazio orizzontale ai grafici in una colonna ampia e la legenda in un'altra colonna stretta.

4) Traccia su un dispositivo di qualsiasi dimensione desideri. Ecco come ottieni una particolare forma o proporzioni.

library(gridExtra) 
library(grid) 

# Function to extract the legend from a ggplot graph as a separate grob 
# Source: https://stackoverflow.com/a/12539820/496488 
get_leg = function(a.gplot){ 
    tmp <- ggplot_gtable(ggplot_build(a.gplot)) 
    leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box") 
    legend <- tmp$grobs[[leg]] 
    legend 
} 

# Get legend as a separate grob 
leg = get_leg(site_status) 

# Add a theme element to change the plot margins to remove white space between the plots 
thm = theme(plot.margin=unit(c(0,0,-0.5,0),"lines")) 

# Left-align the four plots 
# Adapted from: https://stackoverflow.com/a/13295880/496488 
gA <- ggplotGrob(bar + thm) 
gB <- ggplotGrob(smoke_status + thm) 
gC <- ggplotGrob(hpv_status + thm) 
gD <- ggplotGrob(site_status + theme(plot.margin=unit(c(0,0,0,0), "lines")) + 
        guides(fill=FALSE)) 

maxWidth = grid::unit.pmax(gA$widths[2:5], gB$widths[2:5], gC$widths[2:5], gD$widths[2:5]) 
gA$widths[2:5] <- as.list(maxWidth) 
gB$widths[2:5] <- as.list(maxWidth) 
gC$widths[2:5] <- as.list(maxWidth) 
gD$widths[2:5] <- as.list(maxWidth) 

# Lay out plots and legend 
p = grid.arrange(arrangeGrob(gA,gB,gC,gD, heights=c(0.5,0.15,0.15,0.21)), 
       leg, ncol=2, widths=c(0.8,0.2)) 

È quindi possibile determinare la forma o il rapporto aspetto del grafico finale impostando i parametri del dispositivo di uscita. (Potrebbe essere necessario regolare le dimensioni dei caratteri quando si creano i grafici sottostanti in modo che il layout finale appaia come desiderato.) La trama incollata in basso è un file png salvato direttamente dalla finestra del grafico RStudio. Ecco come si potrebbe salvare il grafico in formato PDF (ma ci sono molti altri "dispositivi" è possibile utilizzare (ad esempio, png, jpeg, ecc) per salvare in diversi formati):

pdf("myPlot.pdf", width=10, height=5) 
p 
dev.off() 

enter image description here

Hai anche chiesto informazioni sul codice più efficiente. Una cosa che puoi fare è creare una lista di elementi di trama che usi più volte e quindi aggiungere il nome dell'oggetto della lista a ogni trama. Ad esempio:

my_gg = list(geom_bar(stat="identity", fill="red"), 
      theme(legend.position = "none", 
        axis.title.x = element_blank(), 
        axis.ticks.x = element_blank(), 
        axis.text.x = element_blank()), 
        plot.margin = unit(c(0,0,-0.5,0), "lines")) 

smoke_status = ggplot(data, aes(x=pt_id, y=smoke)) + 
        labs(y="Smoking Status") + 
        my_gg