3

I would like to show in the same plot interpolated data and a histogram of the raw data of each predictor. I have seen in other threads like this one, people explain how to do marginal histograms of the same data shown in a scatter plot, in this case, the histogram is however based on other data (the raw data).

Suppose we see how price is related to carat and table in the diamonds dataset:

library(ggplot2)
p = ggplot(diamonds, aes(x = carat, y = table, color = price)) + geom_point()

We can add a marginal frequency plot e.g. with ggMarginal

library(ggExtra)    
ggMarginal(p)

enter image description here

How do we add something similar to a tile plot of predicted diamond prices?

library(mgcv)
model = gam(price ~ s(table, carat), data = diamonds)
newdat = expand.grid(seq(55,75, 5), c(1:4))
names(newdat) = c("table", "carat")
newdat$predicted_price = predict(model, newdat)

ggplot(newdat,aes(x = carat, y = table, fill = predicted_price)) + 
    geom_tile()

enter image description here

Ideally, the histograms go even beyond the margins of the tileplot, as these data points also influence the predictions. I would, however, be already very happy to know how to plot a histogram for the range that is shown in the tileplot. (Maybe the values that are outside the range could just be added to the extreme values in different color.)

PS. I managed to more or less align histograms to the margins of the sides of a tile plot, using the method of the accepted answer in the linked thread, but only if I removed all kind of labels. It would be particularly good to keep the color legend, if possible.

EDIT: eipi10 provided an excellent solution. I tried to modify it slightly to add the sample size in numbers and to graphically show values outside the plotted range since they also affect the interpolated values. I intended to include them in a different color in the histograms at the side. I hereby attempted to count them towards the lower and upper end of the plotted range. I also attempted to plot the sample size in numbers somewhere on the plot. However, I failed with both.

This was my attempt to graphically illustrate the sample size beyond the plotted area:

plot_data = diamonds
plot_data <- transform(plot_data, carat_range = ifelse(carat < 1 | carat > 4, "outside", "within"))
plot_data <- within(plot_data, carat[carat < 1] <- 1)
plot_data <- within(plot_data, carat[carat > 4] <- 4)
plot_data$carat_range = as.factor(plot_data$carat_range)

p2 = ggplot(plot_data, aes(carat, fill = carat_range)) +
    geom_histogram() +
    thm +
    coord_cartesian(xlim=xrng)

I tried to add the sample size in numbers with geom_text. I tried fitting it in the far right panel but it was difficult (/impossible for me) to adjust. I tried to put it on the main graph (which would anyway probably not be the best solution), but it didn’t work either (it removed the histogram and legend, on the right side and it did not plot all geom_texts). I also tried to add a third row of plots and writing it there. My attempt:

n_table_above = nrow(subset(diamonds, table > 75))
n_table_below = nrow(subset(diamonds, table < 55))
n_table_within = nrow(subset(diamonds, table >= 55 & table <= 75))

text_p = ggplot()+ 
    geom_text(aes(x = 0.9, y = 2, label = paste0("N(>75) = ", n_table_above)))+
    geom_text(aes(x = 1, y = 2, label = paste0("N = ", n_table_within)))+
    geom_text(aes(x = 1.1, y = 2, label = paste0("N(<55) = ", n_table_below)))+ 
    thm

library(egg) 
pobj = ggarrange(p2, ggplot(), p1, p3,
                 ncol=2, widths=c(4,1), heights=c(1,4))

grid.arrange(pobj, leg, text_p, ggplot(), widths=c(6,1), heights =c(6,1))

I would be very happy to receive help on either or both tasks (adding sample size as text & adding values outside plotted range in a different color).

Community
  • 1
  • 1
bee guy
  • 429
  • 7
  • 20

1 Answers1

2

Based on your comment, maybe the best approach is to roll your own layout. Below is an example. We create the marginal plots as separate ggplot objects and lay them out with the main plot. We also extract the legend and put it outside the marginal plots.

Set-up

library(ggplot2)
library(cowplot)

# Function to extract legend
#https://github.com/hadley/ggplot2/wiki/Share-a-legend-between-two-ggplot2-graphs
g_legend<-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]]
  return(legend) }

thm = list(theme_void(),
           guides(fill=FALSE),
           theme(plot.margin=unit(rep(0,4), "lines")))

xrng = c(0.6,4.4)
yrng = c(53,77)

Plots

p1 = ggplot(newdat, aes(x = carat, y = table, fill = predicted_price)) + 
  geom_tile() +
  theme_classic() +
  coord_cartesian(xlim=xrng, ylim=yrng)

leg = g_legend(p1)

p1 = p1 + thm[-1]

p2 = ggplot(diamonds, aes(carat)) +
  geom_line(stat="density") +
  thm +
  coord_cartesian(xlim=xrng)

p3 = ggplot(diamonds, aes(table)) +
  geom_line(stat="density") +
  thm + 
  coord_flip(xlim=yrng)

plot_grid(
  plot_grid(plotlist=list(p2, ggplot(), p1, p3), ncol=2, 
            rel_widths=c(4,1), rel_heights=c(1,4), align="hv", scale=1.1),
  leg, rel_widths=c(5,1))

enter image description here

UPDATE: Regarding your comment about the space between the plots: This is an Achilles heel of plot_grid and I don't know if there's a way to fix it. Another option is ggarrange from the experimental egg package, which doesn't add so much space between plots. Also, you need to save the output of ggarrange first and then lay out the saved object with the legend. If you run ggarrange inside grid.arrange you get two overlapping copies of the plot:

# devtools::install_github('baptiste/egg')
library(egg) 

pobj = ggarrange(p2, ggplot(), p1, p3, 
                 ncol=2, widths=c(4,1), heights=c(1,4))

grid.arrange(pobj, leg, widths=c(6,1))

enter image description here

eipi10
  • 91,525
  • 24
  • 209
  • 285
  • Thanks for the answer. They are however still not properly aligned as carat and table have different ranges in newdat and diamonds. – bee guy Feb 18 '17 at 19:52
  • Thank you very much! Great answer :-) Do you also know how to reduce the white space between the line plots and the main plot? – bee guy Feb 19 '17 at 02:00
  • Superb. This looks very nice! – bee guy Feb 19 '17 at 08:51
  • I tried to slightly modify your plot, but I have clearly not well enough understood what you did. Could you have a look at my attempts above? Sorry that I am so lost with this. – bee guy Mar 01 '17 at 10:18