2015-07-23 25 views
6

Voglio tracciare una spline cubica ristretta come trama principale e aggiungere un grafico a scatola e baffi per mostrare la variazione della variabile X. Tuttavia, la cerniera inferiore (x = 42), la mediana (x = 51) e la cerniera superiore (x = 61) non si adattavano perfettamente alla linea della griglia corrispondente della trama principale.Adattamento perfetto del grafico ggplot2 nel grafico

library(Hmisc) 
library(rms) 
library(ggplot2) 
library(gridExtra) 

data(pbc) 
d <- pbc 
rm(pbc) 
d$status <- ifelse(d$status != 0, 1, 0) 

dd = datadist(d) 
options(datadist='dd') 

f <- cph(Surv(time, status) ~ rcs(age, 4), data=d) 
p <- Predict(f, fun=exp) 
df <- data.frame(age=p$age, yhat=p$yhat, lower=p$lower, upper=p$upper) 

### 1st PLOT: main plot 
(g <- ggplot(data=df, aes(x=age, y=yhat)) + geom_line(size=1)) 

# CI 
(g <- g + geom_ribbon(data=df, aes(ymin=lower, ymax=upper), alpha=0.5, linetype=0, fill='#FFC000')) 

# white background 
(g <- g + theme_bw()) 

# X-axis 
(breaks <- round(boxplot.stats(p[,"age"])$stats)) 
(g <- g + scale_x_continuous(breaks=breaks, limits=range(p[,"age"]), labels=round(breaks))) 
(g <- g + xlab("Age")) 

# Y-Achse 
(g <- g + ylab("Hazard Ratio")) 

# size and color of axis 
(g <- g + theme(axis.line = element_line(color='black', size=1))) 
(g <- g + theme(axis.ticks = element_line(color='black', size=1))) 

(g <- g + theme(plot.background = element_blank())) 
#(g <- g + theme(panel.grid.major = element_blank())) 
(g <- g + theme(panel.grid.minor = element_blank())) 
(g <- g + theme(panel.border = element_blank())) 

### 2nd PLOT: box whisker plot 
describe(df$age, digits=0) 
round(range(df$age)) 
(gg <- ggplot(data=df, aes(x=1, y=age)) + geom_boxplot(outlier.shape=NA, size=1) + coord_flip()) 
(gg <- gg + theme(axis.line=element_blank())) # 
(gg <- gg + theme(axis.text.x=element_blank())) 
(gg <- gg + theme(axis.text.y=element_blank())) 
(gg <- gg + theme(axis.ticks=element_blank())) 
(gg <- gg + theme(axis.title.x=element_blank())) 
(gg <- gg + theme(axis.title.y=element_blank())) 
(gg <- gg + theme(panel.background=element_blank())) 
(gg <- gg + theme(panel.border=element_blank())) # 
(gg <- gg + theme(legend.position="none")) # 
(gg <- gg + theme(panel.grid.major=element_blank())) # 
(gg <- gg + theme(panel.grid.minor=element_blank())) 
(gg <- gg + theme(plot.background=element_blank())) 
(gg <- gg + theme(plot.margin = unit(c(0,0,0,0), "in"))) 

(gg <- gg + scale_x_continuous(breaks=c(70,77,84), expand=c(0,0))) 

### FINAL PLOT: put box whisker plot in main plot 
(final.gg <- g + annotation_custom(ggplotGrob(gg), ymin=2.4, ymax=2.6)) 
  1. Cosa devo cambiare per la misura perfetta?
  2. Esiste un modo migliore per un allineamento automatizzato della posizione y del box-and-whisker ?

enter image description here

UPDATE # 1 Grazie per la risposta! Qui sotto puoi vedere il mio esempio con il tuo codice. Tuttavia, come puoi vedere, la cerniera inferiore, la mediana e la cerniera superiore non si adattano ancora. Cosa sta andando storto?

library(Hmisc) 
library(rms) 
library(ggplot2) 
library(gridExtra) 

data(pbc) 
d <- pbc 
rm(pbc, pbcseq) 
d$status <- ifelse(d$status != 0, 1, 0) 

dd = datadist(d) 
options(datadist='dd') 

f <- cph(Surv(time, status) ~ rcs(age, 4), data=d) 
p <- Predict(f, fun=exp) 
df <- data.frame(age=p$age, yhat=p$yhat, lower=p$lower, upper=p$upper) 

### 1st PLOT: main plot 
(breaks <- boxplot.stats(p[,"age"])$stats) 
g <- ggplot(data=df, aes(x=age, y=yhat)) + geom_line(size=1) + 
    geom_ribbon(data=df, aes(ymin=lower, ymax=upper), alpha=0.5, linetype=0, fill='#FFC000') + 
    theme_bw() + 
    scale_x_continuous(breaks=breaks) + 
    xlab("Age") + 
    ylab("Hazard Ratio") + 
    theme(axis.line = element_line(color='black', size=1), 
      axis.ticks = element_line(color='black', size=1), 
      plot.background = element_blank(), 
      # panel.border = element_blank(), 
      panel.grid.minor = element_blank()) 

### 2nd PLOT: box whisker plot 
gg <- ggplot(data=df, aes(x=1, y=age)) + 
    geom_boxplot(outlier.shape=NA, size=1) + 
    scale_y_continuous(breaks=breaks) + 
    ylab(NULL) + 
    coord_flip() + 
    # theme_bw() + 
    theme(axis.line=element_blank(), 
      # axis.text.x=element_blank(), 
      axis.text.y=element_blank(), 
      axis.ticks.y=element_blank(), 
      axis.title=element_blank(), 
      # panel.background=element_blank(), 
      panel.border=element_blank(), 
      # panel.grid.major=element_blank(), 
      panel.grid.minor=element_blank(), 
      # plot.background=element_blank(), 
      plot.margin = unit(c(0,0,0,0), "in"), 
      axis.ticks.margin = unit(0, "lines"), 
      axis.ticks.length = unit(0, "cm")) 

### FINAL PLOT: put box whisker plot in main plot 
(final.gg <- g + annotation_custom(ggplotGrob(gg), ymin=2.4, ymax=2.6)) 

enter image description here

+0

Grazie per l'aggiunta la foto! – Gurkenhals

risposta

4

Modifica minima: Aggiornamento a ggplot2 2.0.0 axis.ticks.margin è deprecato

Nel boxplot, anche se è stata impostata vari elementi a element_blank e dei margini a zero, di default gli spazi rimangono con conseguente disallineamento. Questi spazi appartengono a:

  • axis.ticks.length
  • XLAB

Nel codice qui sotto, ho ri-arrangiati in qualche modo il codice (spero che va bene), e commentata alcune linee di codice in modo che si possa vedere che i due grafici si allineano. Ho anche impostato le interruzioni nella trama due non arrotondate breaks (valori min e max, cerniere e mediana).

# X-axis 
(breaks <- boxplot.stats(p[,"age"])$stats) 

### 1st PLOT: main plot 
g <- ggplot(data=df, aes(x=age, y=yhat)) + geom_line(size=1) + 
    geom_ribbon(data=df, aes(ymin=lower, ymax=upper), alpha=0.5, linetype=0, fill='#FFC000') + 
    theme_bw() + 
    scale_x_continuous(breaks=breaks) + 
    xlab("Age") + 
    ylab("Hazard Ratio") + 
    theme(axis.line = element_line(color='black', size=1), 
     axis.ticks = element_line(color='black', size=1), 
     plot.background = element_blank(), 
     # panel.border = element_blank(), 
     panel.grid.minor = element_blank()) 

### 2nd PLOT: box whisker plot 
gg <- ggplot(data=df, aes(x=1, y=age)) + 
     geom_boxplot(outlier.shape=NA, size=1) + 
     scale_y_continuous(breaks=breaks) + 
     xlab(NULL) + 
     coord_flip() + 
    # theme_bw() + 
     theme(axis.line=element_blank(), 
     # axis.text.x=element_blank(), 
      axis.text.y=element_blank(), 
      axis.ticks.y=element_blank(), 
      axis.title=element_blank(), 
     # panel.background=element_blank(), 
      panel.border=element_blank(), 
     # panel.grid.major=element_blank(), 
      panel.grid.minor=element_blank(), 
     # plot.background=element_blank(), 
      plot.margin = unit(c(0,0,0,0), "in"), 
     # axis.ticks.margin = unit(0, "lines"), 
      axis.ticks.length = unit(0, "cm")) 

### FINAL PLOT: put box whisker plot in main plot 
(final.gg <- g + annotation_custom(ggplotGrob(gg), ymin=2.4, ymax=2.6)) 

enter image description here

Si dovrebbe notare che il metodo di ggplot per calcolare le cerniere differisce leggermente dal metodo usato da boxplot.stats.

# ggplot's hinges 
bp = ggplot(data=df, aes(x=1, y=age)) + 
     geom_boxplot(outlier.shape=NA, size=1) 
bpData = ggplot_build(bp) 
bpData$data[[1]][1:5] 
+0

Grazie per la tua risposta! Ho appena aggiornato la mia domanda. – Gurkenhals

+0

Grazie mille per il tuo aiuto! Hai idea di come sia possibile tracciare il plot box-e-whisker con un'altezza fissa e in una posizione fissa rispetto alla trama principale? Come è noto, avrei bisogno di adattare manualmente l'altezza e la posizione y a seconda del predittore sull'asse x. – Gurkenhals

+0

@Gurkenhals Sì, ma non utilizzando 'annotation_custom'. Vedi la risposta alternativa. Posizionerei il boxplot al di fuori del riquadro del grafico principale. Quindi, la sua posizione è fissa.Io uso le funzioni Gtable per farlo. L'altezza del pannello di boxplot può essere regolata in relazione all'altezza del pannello di trama principale. Un effetto simile può essere ottenuto usando le finestre (dal pacchetto della griglia). –

3

risposta alternativa utilizzando gtable funzioni per posizionare il grafico a scatole al di fuori della trama principale. L'altezza della trama scatola può essere regolata utilizzando il "h" parametro

library(rms) 

# Data 
data(pbc) 
d <- pbc 
rm(pbc) 
d$status <- ifelse(d$status != 0, 1, 0) 

dd = datadist(d) 
options(datadist='dd') 

f <- cph(Surv(time, status) ~ rcs(age, 4), data=d) 
p <- Predict(f, fun=exp) 
df <- data.frame(age=p$age, yhat=p$yhat, lower=p$lower, upper=p$upper) 

# X-axis 
breaks <- boxplot.stats(p[,"age"])$stats 

# Main plot 
MP <- ggplot(data=df, aes(x=age, y=yhat)) + geom_line(size=1) + 
    geom_ribbon(data=df, aes(ymin=lower, ymax=upper), alpha=0.5, linetype=0, fill='#FFC000') + 
    theme_bw() + 
    scale_x_continuous(breaks=breaks) + 
    xlab("Age") + 
    ylab("Hazard Ratio") + 
    theme(axis.line = element_line(color='black', size=1), 
     axis.ticks = element_line(color='black', size=1), 
     panel.grid.minor = element_blank()) 

# Boxplot 
BP <- ggplot(data=df, aes(x=factor(1), y=age)) + 
     geom_boxplot(width = 1, outlier.shape=NA, size=1) + 
     geom_jitter(position = position_jitter(width = .3), size = 1) + 
     scale_y_continuous(breaks=breaks) + 
     coord_flip() + 
     theme_bw() + 
     theme(panel.border=element_blank(), 
      panel.grid=element_blank()) 

#### Set up the grobs and gtables here 
library(gtable) 
library(grid) 

h = 1/15 # height of boxplot panel relative to main plot panel 

# Get ggplot grobs 
gMP = ggplotGrob(MP) 
BPg = ggplotGrob(BP) 
BPg = gtable_filter(BPg, "panel") # from the boxplot, extract the panel only 

# In the main plot, get position of panel in the layout 
pos = gMP$layout[gMP$layout$name == "panel", c('t', 'l')] 

# In main plot, set height for boxplot 
gMP$heights[pos$t-2] = unit(h, "null") 

# Add boxplot to main plot 
gMP = gtable_add_grob(gMP, BPg, t=pos$t-2, l=pos$l) 

# Add small space 
gMP$heights[pos$t-1] = unit(5, "pt") 

# Draw it 
grid.newpage() 
grid.draw(gMP) 

enter image description here