Skip to content

plotly::subplot with >= 3 rows/cols results in different sized plots for inner/boundary plots #2057

Open

Description

@avsdev-cw

As per title, when making a subplot with >= 3 rows or columns with a margin (default is 0.02 but the issue is easier to see with more plots/bigger margins)

library(magrittr)
p <- plotly::plot_ly(mtcars, x = ~cyl, y = ~mpg, color = ~rownames(.data), type = "bar") %>% plotly::layout(showlegend = FALSE)
plotly::subplot(p, p, p, p, p, p, p, p, p, nrows = 3, margin = 0.1)

image

There is a PR (#622) which aims to fix it, but the PR also does some extra things like make the margins fixed rather than scaled. I think it's possible to provide a much quicker & simpler fix as an alternative, given that this has been known about since 2016.

I think in a nutshell its due to the function get_domains & it's for loops:

plotly.R/R/subplots.R

Lines 385 to 401 in 4bb1e44

xs <- vector("list", ncols)
for (i in seq_len(ncols)) {
xs[[i]] <- c(
xstart = widths[i] + if (i == 1) 0 else margins[1],
xend = widths[i + 1] - if (i == ncols) 0 else margins[2]
)
}
xz <- rep_len(xs, nplots)
ys <- vector("list", nrows)
for (i in seq_len(nplots)) {
j <- ceiling(i / ncols)
ys[[i]] <- c(
ystart = 1 - (heights[j]) - if (j == 1) 0 else margins[3],
yend = 1 - (heights[j + 1]) + if (j == nrows) 0 else margins[4]
)
}

It's those if's that are the problem, can they be removed without causing too much trouble?

Activity

  1. romanzenka commented on Feb 21, 2025

    @romanzenka

    @avsdev-cw Trying to look into this - you are right, this is not working great. I do no think that removing the if statements is a good idea, as the margins should be respected, but instead the margins need to contribute properly to all the subplots so that they are laid out evenly while honoring the margin constraints from the user.

    I can make it even worse with this code:

    library(magrittr)
    p <- plotly::plot_ly(mtcars, x = ~cyl, y = ~mpg, color = ~rownames(.data), type = "bar") %>% plotly::layout(showlegend = FALSE)
    plotly::subplot(p, p, p, p, p, p, p, p, p, nrows = 3, margin = c(0.1, 0.2, 0.1, 0.2))

    What I would expect instead is "collapsing" of margins similar to how HTML does it, so that the margins between plots would be 0.2, the margin on the left 0.1 and the very right one 0.2. And I would expect the plots to have identical sizes.

    That said, if margins collapse but left and right side lays flat against the edge of the picture, then it makes zero sense to specify separate left and right margin, because the gaps between the plots would be max(left, right) and nothing special happens on the very left or the very right... that does not sound right to me either. It is also unclear whether width of the plot should correspond to the entire area including margin, or just to the "plotted on" area without margins (I believe we are talking about distinction between margin and padding as in HTML). I can see many ways of solving this, I think we need to look at how other packages do this and adjust so that the function is useable and covers scenarios people care about the most in an easy-to-use way.

  2. avsdev-cw commented on Feb 24, 2025

    @avsdev-cw
    Author

    @romanzenka Thanks for the reply. It's been quite a long time so I can't actually remember if I adhered to the truthful or falseful side of the inline "if" statements. It may be worth replicating the tests with either side to see which one works best.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions