plotly.R icon indicating copy to clipboard operation
plotly.R copied to clipboard

Missing traces when supplying a dual-axis plot to subplot()

Open dfernan opened this issue 8 years ago • 23 comments

If I do:

library(plotly)
ay <- list(
tickfont = list(color = "red"),
overlaying = "y",
side = "right",
title = "second y axis"
)
p <- plot_ly() %>%
add_lines(x = ~1:3, y = ~10*(1:3), name = "slope of 10") %>%
add_lines(x = ~2:4, y = ~1:3, name = "slope of 1", yaxis = "y2") %>%
layout(
title = "Double Y Axis", yaxis2 = ay,
xaxis = list(title="x")
)

It plots both axis. image

but if I do: subplot(p) I loose the secondary axis. image

This doesn't seem like the intended behavior of subplot unless I am missing something. Thanks!

dfernan avatar Apr 20 '17 15:04 dfernan

Try subplot(p, titleY = T)

cpsievert avatar Apr 20 '17 15:04 cpsievert

Thanks. I tried, I see this: image so not real change from not putting that option in. Maybe it's a version issue? I am running R on windows, R version 3.3.3 (2017-03-06), and plotly appears to be version 4.5.6. Does 4.5.6 refers to the Rplotly version or to the plotly.js version?

dfernan avatar Apr 20 '17 16:04 dfernan

Sorry, I mis-read the problem. This is, in fact, a bug.

cpsievert avatar Apr 20 '17 17:04 cpsievert

such legit tile: Missing traces when supplying a dual-axis plot to subplot() :-)

dfernan avatar Apr 20 '17 17:04 dfernan

Hi, I have been trying to use subplot() with dual axis, and face a similar (I think related) problem. I had built an example of my problem, and seeing that there is this open issue, I provide it here in case it is helpful.

I build two double axis plots with economics dataset:

# using "economics" dataset

# Create two 2-axis plots
# Plot 1: pop in y1 and psavert in y2
p1 <- economics %>% plot_ly() %>%  
  add_lines(x = ~date, y = ~pop,name="pop") %>%
  add_lines(x = ~date, y = ~psavert,name="psavert",yaxis = "y2") %>%
  layout(
    title ="Plot1",
    legend=list(x=1.1,y=1.1),
    yaxis = list(title="pop"),
    yaxis2 = list(side = "right",  overlaying = "y",title = "psavert")
  )

# Plot 2: pop in y1 and uempmed in y2
p2 <- economics %>% plot_ly() %>%  
  add_lines(x = ~date, y = ~pop,name="pop2") %>%
  add_lines(x = ~date, y = ~uempmed,name="uempmed2",yaxis = "y2") %>%
  layout(
    title ="Plot2",
    legend=list(x=1.1,y=1.1),
    yaxis = list(title="pop"),
    yaxis2 = list(side = "right",  overlaying = "y",title = "uempmed")
  )

If I compose a subplot with independen X axes

# Render the two together with subplot(), 2 X axes
p3 <- subplot(p1,p2,nrows=2)

imagen

If I set the shareX to TRUE:

# Render the two together with subplot(), 1 X axes
p3 <- subplot(p1,p2,nrows=2,shareX=T)

imagen

Seems like all operation on the second axis go to the same plot inside subplot. Hope this helps.

pepe-enriquez avatar May 26 '17 08:05 pepe-enriquez

@cpsievert any updated with respect to this issue? Thanks!

dfernan avatar Jul 21 '17 19:07 dfernan

Having the same issue, have a hunch the fix might be some additions to the axis handling in in subplots.R.

For example, when gathering layout attributes, yaxis2, yaxis3...yaxisN are not included in the existing list, c("mapbox", "geo", "xaxis", "yaxis"). Seems like adding the additional axis objects to this line and all others handling axes might solve this issue (utilizing generated sequences of names and regexes where possible to avoid manually typing all possible iterations of yaxis2, yaxis3...yaxisN, and y2, y3... yN).

@cpsievert Does this seem like the right path to head down? If so I'll open a PR and start working sometime this week.

lines 90-117 of subplots.R:

plots <- list()
  for (i in seq_along(plotz)) {
    p <- plots[[i]] <- plotz[[i]]
    layoutAttrs <- c(names(p$layout), c("mapbox", "geo", "xaxis", "yaxis"))
    xTraceAttrs <- sub("^x", "xaxis", sapply(p$data, function(tr) tr[["subplot"]] %||% tr[["geo"]] %||% tr[["xaxis"]] %||% "x"))
    yTraceAttrs <- sub("^y", "yaxis", sapply(p$data, function(tr) tr[["subplot"]] %||% tr[["geo"]] %||% tr[["yaxis"]] %||% "y"))
    missingAttrs <- setdiff(c(xTraceAttrs, yTraceAttrs), layoutAttrs)
    # move to next iteration if trace references are complete
    if (!length(missingAttrs)) next
    # remove each "missing" trace from this plot
    missingTraces <- xTraceAttrs %in% missingAttrs | yTraceAttrs %in% missingAttrs
    plots[[i]]$data[missingTraces] <- NULL
    # move traces with "similar missingness" to a new plot
    for (j in missingAttrs) {
      newPlot <- list(
        data = p$data[xTraceAttrs %in% j | yTraceAttrs %in% j],
        layout = p$layout
      )
      # reset the anchors
      newPlot$data <- lapply(newPlot$data, function(tr) {
        for (k in c("mapbox", "geo", "xaxis", "yaxis")) {
          tr[[k]] <- sub("[0-9]+", "", tr[[k]]) %||% NULL
        }
        tr
      })
      plots <- c(plots, list(newPlot))
    }
  }

msummersgill avatar Aug 07 '17 14:08 msummersgill

Another reprex:

library(plotly)

## Create a data frame with data to be plotted
S <- seq(1,100)
DF <- data.frame(S = S, A = S, B = sin(S), C = S^2)

## Create first plot, 'P_A' with trace 'A'
A_Axis <- list(side = "left", title = "Axis A")

DF %>% 
plot_ly() %>% 
  add_lines(x = ~S , y = ~ A, yaxis = "y", name = "A",color = I("red")) %>%
  layout(yaxis = A_Axis) -> P_A

## Create second plot, 'P_BC', with traces 'B' and 'C'
B_Axis <- list(side = "left", title = "Axis B")
C_Axis <- list(side = "right", title = "Axis C", overlaying = "y")

DF %>% 
plot_ly() %>% 
  add_lines(x = ~S , y = ~ B, yaxis = "y", name = "B", color = I("blue")) %>%
  add_lines(x = ~S , y = ~ C, yaxis = "y2", name = "C", color = I("green")) %>%
  layout(yaxis = B_Axis,
         yaxis2 = C_Axis) -> P_BC

## Create a subplot- Trace 'C' is added to the first plot instead of the second
subplot(list(P_A,P_BC),nrows = 2,titleY = TRUE, shareX = TRUE)

image

msummersgill avatar Aug 07 '17 14:08 msummersgill

@msummersgill for some reason, subplot() is incorrectly changing the trace anchor numbering. To see that, take the first example and do:

diffObj::diffObj(plotly_build(p)$x$data, subplot(p)$x$data)

I'm not sure why this is happening, but if you think you have a solution, I'd love to see a pull request!

cpsievert avatar Aug 08 '17 19:08 cpsievert

Hi, fairly new to github, coding and plotly.

Faced with a similar issue replacing overlaying="y" with overlaying="y2" in the axis definition solved my issue.

I hope it helps

nc-savoy avatar Nov 08 '17 11:11 nc-savoy

Thank you very much @nc-savoy ! I feel like I still have a tenuous grasp of exactly how this is working, but that method also works for more than two plots in a subplots. (After some trial and error)

library(plotly)

## Create a data frame with data to be plotted
S <- seq(1,100)
DF <- data.frame(S = S, A = S, B = sin(S), C = S^2, D = 1/S, E = S^3 + S^3*cos(S))

## Create first plot, 'P_A' with trace 'A'
A_Axis <- list(side = "left", title = "Axis A")

DF %>% 
plot_ly() %>% 
  add_lines(x = ~S , y = ~ A, yaxis = "y", name = "A",color = I("red")) %>%
  layout(yaxis = A_Axis) -> P_A

## Create second plot, 'P_BC', with traces 'B' and 'C'
B_Axis <- list(side = "left", title = "Axis B")
C_Axis <- list(side = "right", title = "Axis C", overlaying = "y2")


DF %>% 
plot_ly() %>% 
  add_lines(x = ~S , y = ~ B, yaxis = "y", name = "B", color = I("blue")) %>%
  add_lines(x = ~S , y = ~ C, yaxis = "y2", name = "C", color = I("green")) %>%
  layout(yaxis = B_Axis,
         yaxis2 = C_Axis) -> P_BC


## Create third plot, 'P_DE', with traces 'D' and 'E'
D_Axis <- list(side = "left", title = "Axis D")
E_Axis <- list(side = "right", title = "Axis E", overlaying = "y4")

DF %>% 
plot_ly() %>% 
  add_lines(x = ~S , y = ~ D, yaxis = "y", name = "D", color = I("orange")) %>%
  add_lines(x = ~S , y = ~ E, yaxis = "y5", name = "E", color = I("purple")) %>% 
  layout(yaxis = D_Axis,
         yaxis5 = E_Axis) -> P_DE

## Create a subplot
subplot(list(P_A,P_BC,P_DE),nrows = 3,titleY = TRUE, shareX = TRUE)

image

msummersgill avatar Nov 29 '17 23:11 msummersgill

Trying to make sense of this example. Why are data and title for series E linked to y4 and y5, rather than both to the same y, or even both to y2? And related to that; why is series C linked to y2 (in contrast to E on y4/y5) and not for instance y3 or y5 for that matter. Are the axis counted on the left all y1 and on the right as y2 or is it a series of y1-y6?

In an approach I used, I create the panels automatically in a purrr::map structure so its hard to assign y'n' to each plots secondary axis. Coding it to append y2 to all secondary axes put all those traces and ticks on top of each other in the first row of panels in a 3*3 subplot page

ghost avatar Apr 19 '18 09:04 ghost

facing same bug

slfan2013 avatar May 09 '18 21:05 slfan2013

Any more insight on how to make this work in a generic fashion?

I'm doing a lapply on a plotting function that does this different number of subplots depending on the data. (with an upper end of 5 sub plots each with their own dual axes)

ghost avatar May 30 '18 05:05 ghost

I'm trying to make a similar plot related to this post and similar to the image below.

image

The interesting part is in the second-y-axis... it was split into two scales while the left y-axis is shared for all datasets.

  • The brown line corresponds to the left-y-axis.
  • The pink bars are linked to the top-right y-axis and bottom blue bars to the bottom right y-axis.
  • All plots share X-axis.

Is it possible in plotly to make the left y-axis and x-axis shared?

gponce-ars avatar Nov 13 '18 04:11 gponce-ars

For anyone facing this issue, this is the approach that worked for me

Setting all the yaxis arguments in add_trace() for the left y axis to "y". Setting all the yaxis arguments in add_trac() for the right y axis to "y2" Setting the "overlaying" argument in the layout for the second y axis to "y" for the first subplot, "y3" for the second, "y5" for the third and so on.

It's also important to assign the "left" attribute to the left y-axes, even though that's the default. I've found it won't work otherwise.

y <- rnorm(20,5,1)
y2 <- rnorm(20,10,2)

df <- data.frame(x,y,y2)

library(plotly)
p1 <- plot_ly() %>% add_trace(x=df$x,y=df$y,type="scatter",mode="lines",yaxis="y") %>%
                    add_trace(x=df$x,y=df$y2,type="scatter",mode="lines",yaxis="y2") %>%
                    layout(yaxis=list(side="left"),
                           yaxis2=list(side="right",overlaying="y"),
                           showlegend=FALSE)

p2 <-plot_ly() %>% add_trace(x=df$x,y=df$y+df$y2,type="scatter",mode="lines",yaxis="y") %>%
                 add_trace(x=df$x,y=df$y2*df$y,type="scatter",mode="lines",yaxis="y2") %>%
                layout(yaxis=list(side="left"),
                       yaxis2=list(side="right",overlaying="y3"),
                       showlegend=FALSE)

p <- subplot(p1,p2,nrows = 2)

image

mabreidi avatar Jan 13 '19 22:01 mabreidi

For one of my apps I had to use multiple y axis and discovered that using "anchor='free'" will lead to the issue that subplot will not display the plot at all. Hence, once can not manually rearrange the axis when you have more than 2.

JulianStopp avatar May 12 '20 18:05 JulianStopp

It seems this bug is still alive. I needed to use the workaround provided by mabreidi to address loss of data with 4 dual yaxis subplots.

PAllen0518 avatar Nov 30 '21 22:11 PAllen0518

I wish this would be in the official documentation because I would otherwise have never found out about this!

nitram9 avatar Jan 07 '22 11:01 nitram9

@mabreidi thank you so much for this! I've literally spent more than 2 hours trying to figure out what was going on with my charts 🙏🙏🙏

dishutin avatar Feb 22 '22 22:02 dishutin

Hey guys,

I was running into the same problem. Found a way to solve it (basically the same @mabreidi proposed) but the axis titles are not showing up. If you guys found a way to make them appear I'm up for the tip!

Cheers

Found it ==> subplot(........ titleY=T)

Padiol avatar Mar 08 '22 18:03 Padiol

Hey, @mabreidi ! Thanks so much! This is incredible! You saved us all so much time and frustration!

fabianjkrueger avatar Apr 03 '24 18:04 fabianjkrueger