rplotlyinsets

Add a plotly figure as an inset to another plotly figure


I think this is a plotting situation that probably commonly occurs in data science.

I have some data on which I'm conducting a statistical test and I want to plot the data together with the outcomes of the statistical test in a single plot.

Here's an example:

The data are two distributions (from two groups):

library(dplyr)
set.seed(1)

data.df <- data.frame(val = c(rnorm(100,0,1),rnorm(100,1,1)),
                 group = c(rep("A",100),rep("B",100)))

I graph these data using a density plot:

density.df <- do.call(rbind,lapply(levels(data.df$group),function(g){
  dens <- density(dplyr::filter(data.df,group == g)$val)
  data.frame(x = dens$x, y = dens$y, group = g)
}))

library(plotly)

density.plot <- plot_ly(x = density.df$x, y = density.df$y, type = 'scatter', mode = 'lines',color = density.df$group) %>%
  layout(xaxis = list(title= "Value", zeroline = F), yaxis = list(title = "Density", zeroline = F))

Which gives: enter image description here

And let's assume I fitted some statistical model to them, with two factors of interest, and obtained these results:

effects.df <- data.frame(effect = c(0.5, 0.75), effect.error = c(0.05,0.1), factor = c("Factor-A","Factor-B"))

I graph these data in what's known as a caterpillar plot:

effects.plot <- plot_ly(type = 'scatter', mode = "markers", marker = list(size = 13, color = "black"),
                        x = effects.df$effect, y = effects.df$factor, showlegend = F) %>%
  layout(xaxis = list(range=c(-1,1), title = "Effect Size", zeroline = T, showticklabels = T, font = list(size=15)),
         yaxis = list(title = NA, zeroline = F, showticklabels = T, tickvals = effects.df$factor, ticktext = as.character(effects.df$factor)), font = list(size = 15)) %>%
  add_trace(error_x = list(array = effects.df$effect.error, width = 5, color = "black"), showlegend = F)

Which gives:

enter image description here

Now I would like to add effects.plot as an inset to the top right quadrant of density.plot. I'm specific about adding it as an inset rather than grouping them using plotly's subplot function.

So my question is how do I do this?

plotly does have an example of adding insets but my attempts to fit my case into that have failed.

Any idea?


Solution

  • You need to define a new couple of axes (xaxis2 and yaxis2) using layout and then to refer the second plot to this coordinate system.

    library(dplyr)
    library(plotly)
    set.seed(1)
    data.df <- data.frame(val = c(rnorm(100,0,1),rnorm(100,1,1)),
                     group = c(rep("A",100),rep("B",100)))
    
    density.df <- do.call(rbind, lapply(levels(data.df$group),
         function(g) {
          dens <- density(dplyr::filter(data.df,group == g)$val)
          data.frame(x = dens$x, y = dens$y, group = g)
         }) )
    
    effects.df <- data.frame(effect = c(0.5, 0.75), 
                             effect.error = c(0.05,0.1), 
                             factor = c("Factor-A","Factor-B"))
    
    density.plot <- density.df %>%
    plot_ly(x = ~x, y = ~y, color = ~group,
                type = 'scatter', mode = 'lines') %>%
    add_markers(x = ~effect, y = ~factor, showlegend = F, 
                error_x=list(array=~effect.error, width=5, color="black"),
                marker = list(size = 13, color = "black"), 
                xaxis = 'x2', yaxis = 'y2', data=effects.df, inherit=F) %>%
    layout(xaxis = list(title= "Value", zeroline = F), 
           yaxis = list(title = "Density", zeroline = F),
           xaxis2 = list(domain = c(0.7, 0.95), anchor='y2', range=c(-1,1), title = "Effect Size", 
                         zeroline = T, showticklabels = T, font = list(size=15)),
           yaxis2 = list(domain = c(0.5, 0.95), anchor='x2', title = NA, zeroline = F, 
                         showticklabels = T, tickvals = effects.df$factor, 
                         ticktext = as.character(effects.df$factor)))
    

    enter image description here