I want to plot a restricted cubic spline as main plot and add a box-and-whisker plot to show the variation of the X variable. However, the lower hinge (x=42), the median (x=51), and the upper hinge(x=61) did not fit perfectly with the corresponding grid line of the main plot.
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))
UPDATE #1 Thanks for your answer! Below you can see my example with your code. However, as you can see, the lower hinge, the median, and the upper hinge still do not fit. What is going wrong?
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))
Minor edit: Updating to ggplot2 2.0.0
axis.ticks.margin
is deprecated
In the boxplot, even though you have set various elements to element_blank
and margins to zero, default spaces remain resulting in the misalignment. These spaces belong to:
In the code below, I've re-arranged your code somewhat (I hope that's okay), and commented out some lines of code so that it can be seen that the two plots do align. I've also set the breaks in the two plot to un-rounded breaks
(min and max values, hinges, and the median).
# 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))
You should note that ggplot's method for calculating the hinges differs slightly from the method used by 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]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With