rr-plotlymultiple-axes

How to address broken dual y-axis plot output from r plot_ly subplotting?


I am plotting 4 plot_ly plots on the same "page" with the subplot function in the plot_ly library. The data here is a quick mockup to represent the dataset with which I am working. I have 4 families of explanatory variables (i.e. actuals 1-10, bias 25,50,70,100), their coefficients, and the normalized amount of times they appeared in a fictitious model, and NA values to pad the vectors in order to make data-frames:

require(tidyverse)
require(data.table)
require(stringr)
require(plotly)
require(processx)

Actuals <-c(1,2,3,4,5,6,7,8,9,10)       
set.seed(10)
UniqCoef1 <- runif(10, min=-1, max=1)    
NrmPolct1 <- c(.015,.005,.33,.32,.225,.025,.03,.05,NA ,NA)
dataframe1 <- data.frame(NrmPolct1,UniqCoef1,Actuals)

bias <-c(25,50,100,200)       
set.seed(4)
UniqCoef2 <- runif(4, min=.08, max=.4)    
NrmPolct2 <- c(.25,.5,NA,NA)
dataframe2 <- data.frame(NrmPolct2,UniqCoef2,bias)

cat <-c(1)       
set.seed(1)
UniqCoef3 <- runif(1, min=-2, max=2)    
NrmPolct3 <- c(.67)
dataframe3 <- data.frame(NrmPolct3,UniqCoef3,cat)

cvglvl <-c(55,60,65,70,75,80,85)       
set.seed(7)
UniqCoef4 <- runif(7, min = -.5, max = .5)    
NrmPolct4 <- c(.22,.33,.15,.05,.12,NA,NA)
dataframe4 <- data.frame(NrmPolct4,UniqCoef4,cvglvl)

In an effort to fix the ranges of their respective y and y2 axis, I built scale factors based on the max value of the coefficients:

sclfctr1    <- ((.33*1.02)/.20)
sclfctr2    <- ((.5*1.02)/.20)
sclfctr3    <- ((.67*1.02)/.20)
sclfctr4    <- ((.33*1.02)/.20)

Then stored each variables plot:

plot1 <- plot_ly(dataframe1) %>%
            add_trace(x=~Actuals, y=~UniqCoef1, type="scatter", mode="markers", name="Coef Values") %>%
            add_bars(x=~Actuals, y=~NrmPolct1, yaxis="y2", name="% Polcy Count") %>%
            layout(plot_bgcolor='#D0CFC9',
                    yaxis=list(scaleanchor="x", scaleratio=5,title="Coefficients", zeroline = FALSE,
                                range=c((((max(UniqCoef1)*1.02)-(max(UniqCoef1)*1.02)+(min(UniqCoef1)*.98))/.80),max(UniqCoef1)*1.02), 
                                tickvals=UniqCoef1,standoff=30),
                    yaxis2=list(scaleacnchor="y", scaleratio=.15,overlaying="y",side="right", tickvals=NrmPolct1,range=c(0,sclfctr1),title="% Polcy Count", standoff=30),
                    xaxis=list(tickvals=Actuals,title=paste("actuals"),showgrid=T, standoff=30),
                    title=paste0("# Actuals for State 23, Crop 41."))
                    
plot2 <- plot_ly(dataframe2) %>%
            add_trace(x=~bias, y=~UniqCoef2, type="scatter", mode="markers", name="Coef Values") %>%
            add_bars(x=~bias, y=~NrmPolct2, yaxis="y2", name="% Polcy Count") %>%
            layout(plot_bgcolor='#D0CFC9',
                    yaxis=list(scaleanchor="x", scaleratio=5,title="Coefficients", zeroline = FALSE,
                                range=c((((max(UniqCoef2)*1.02)-(max(UniqCoef2)*1.02)+(min(UniqCoef2)*.98))/.80),max(UniqCoef2)*1.02), 
                                tickvals=UniqCoef2,standoff=30),
                    yaxis2=list(scaleacnchor="y", scaleratio=.15,overlaying="y",side="right", tickvals=NrmPolct2,range=c(0,sclfctr2),title="% Polcy Count", standoff=30),
                    xaxis=list(tickvals=bias,title=paste("bias"),showgrid=T, standoff=30),
                    title=paste0("# bias for State 23, Crop 41."))
                    
plot3 <- plot_ly(dataframe3) %>%
            add_trace(x=~cat, y=~UniqCoef3, type="scatter", mode="markers", name="Coef Values") %>%
            add_bars(x=~cat, y=~NrmPolct3, yaxis="y2", name="% Polcy Count") %>%
            layout(plot_bgcolor='#D0CFC9',
                    yaxis=list(scaleanchor="x", scaleratio=5,title="Coefficients", zeroline = FALSE,
                                range=c((((max(UniqCoef3)*1.02)-(max(UniqCoef3)*1.02)+(min(UniqCoef3)*.98))/.80),max(UniqCoef3)*1.02), 
                                tickvals=UniqCoef3,standoff=30),
                    yaxis2=list(scaleacnchor="y", scaleratio=.15,overlaying="y",side="right", tickvals=NrmPolct3,range=c(0,sclfctr3),title="% Polcy Count", standoff=30),
                    xaxis=list(tickvals=cat,title=paste("cat"),showgrid=T, standoff=30),
                    title=paste0("# cat for State 23, Crop 41."))
                    
plot4 <- plot_ly(dataframe4) %>%
            add_trace(x=~cvglvl, y=~UniqCoef4, type="scatter", mode="markers", name="Coef Values") %>%
            add_bars(x=~cvglvl, y=~NrmPolct4, yaxis="y2", name="% Polcy Count") %>%
            layout(plot_bgcolor='#D0CFC9',
                    yaxis=list(scaleanchor="x", scaleratio=5,title="Coefficients", zeroline = FALSE,
                                range=c((((max(UniqCoef4)*1.02)-(max(UniqCoef4)*1.02)+(min(UniqCoef4)*.98))/.80),max(UniqCoef4)*1.02), 
                                tickvals=UniqCoef4,standoff=30),
                    yaxis2=list(scaleacnchor="y", scaleratio=.15,overlaying="y",side="right", tickvals=NrmPolct4,range=c(0,sclfctr4),title="% Polcy Count", standoff=30),
                    xaxis=list(tickvals=cvglvl,title=paste("cvglvl"),showgrid=T, standoff=30),
                    title=paste0("# cvglvl for State 23, Crop 41."))

whose outputs, while not ideal or the focus of this question (though any input about how to constrain y2 to take the bottom 15% of the plot and y the top 85% would be very appreciated), but workable:

htmlwidgets::saveWidget(plot1, paste0('Number of actuals for State 23, Crop 41.html'))

plot1

htmlwidgets::saveWidget(plot2, paste0('Number of bias for State 23, Crop 41.html'))

plot2

htmlwidgets::saveWidget(plot3, paste0('Number of cat for State 23, Crop 41.html'))

plot3

htmlwidgets::saveWidget(plot4, paste0('Number of cvglvl for State 23, Crop 41.html'))

plot4

collapse into a mess with no titles, missing bars or scatterplots, and squished ranges that occlude the data that is present:

Plot <- subplot(plot1, plot2, plot3, plot4, nrows=2) %>%
        layout(plot_bgcolor='#e5ecf6', 
                xaxis = list( 
                zerolinecolor = '#ffff', 
                zerolinewidth = 2, 
                gridcolor = 'ffff'), 
                yaxis = list( 
                zerolinecolor = '#ffff', 
                zerolinewidth = 2, 
                gridcolor = 'ffff'))
    
htmlwidgets::saveWidget(Plot, paste0('Number of actuals-cvglvl for State 23, Crop 41.html'))

gridplot

What parameters do I need to include and where do I need to include them in order for the 4 singular plots to show up in the 2x2 grid as near identical copies of their individual instances?

Edit 1: Using the annotations utility allows the inclusion and orientation of graph and axis labels, though the formatting is still useless.

Edit 2: Known bug with workaround posted on GitHub. Link in answer below. Leaving the question up so others can quickly find the workaround if they come in to contact with this problem.


Solution

  • Turns out this is a known bug with a workaround posted on GitHub here:

    https://github.com/plotly/plotly.R/issues/954#issuecomment-453872899