FKneip
FKneip

Reputation: 378

plot_ly with transforms and color mapping

I've been trying to use plot_ly with transforms (ultimately to subset data from a dropdown menu) and a color mapping.

Following is a reproducible example based on mtcars:

transforms without color mapping

If I exclude the color mapping, everything works as expected:

library(plotly)
library(tidyverse)

plot_ly(
    data = mtcars %>% rownames_to_column("id"),
    x = ~mpg, y = ~disp, text = ~id,
    type = "scatter", mode = "markers+text",
    marker = list(size = 10, opacity = 0.8),
    transforms = list(
        list(
            type = "filter",
            target = ~cyl,
            operation = "=",
            value = sort(unique(mtcars$cyl))[1])))

enter image description here

Data is filtered for mtcars$cyl values matching sort(unique(mtcars$cyl))[1] = 4. So far so good.

transforms with color mapping

I now like to color-code markers based on the number of carburetors from mtcars$carb; if I add a color mapping, I end up with

library(plotly)
library(tidyverse)

plot_ly(
    data = mtcars %>% rownames_to_column("id"),
    x = ~mpg, y = ~disp, text = ~id, color = ~as.factor(carb),
    type = "scatter", mode = "markers+text",
    marker = list(size = 10, opacity = 0.8),
    transforms = list(
        list(
            type = "filter",
            target = ~cyl,
            operation = "=",
            value = sort(unique(mtcars$cyl))[1])))

enter image description here

Somehow, the transforms filter is ignored, as the plot shows entries from mtcars where cyl != 4. I actually don't understand which element plot_ly decides to show here.

Would somebody be able to provide any insight? Many thanks.

Upvotes: 5

Views: 2164

Answers (1)

RolandASc
RolandASc

Reputation: 3923

It doesn't seem to like the factor in the color in combination with the filter. It looks like stuff is getting shuffled and screwed up, e.g. color = ~factor(carb, levels = unique(carb)) will give you yet another selection and plot.

Below is working with a factor (including the color in the marker). Specifying just color = ~carb would work as well. I understand none of these really give you the same plot / legend though.

library(plotly)
library(magrittr)

plot_ly(
  data = mtcars %>% tibble::rownames_to_column("id"),
  x = ~mpg, y = ~disp, text = ~id,
  type = "scatter", mode = "markers+text",
  marker = list(color = ~as.factor(carb), size = 10, opacity = 0.8),
  transforms = list(
    list(
      type = "filter",
      target = ~cyl,
      operation = "=",
      value = sort(unique(mtcars$cyl))[1])))

Edit

You can apparently work around the re-shuffling problem by sorting your data first according to the factor you desire to use. The code below works fine in this respect, although I still don't like having all the levels in the legend. Hope it's of some help nonetheless:

library(plotly)
library(magrittr)

xx <- mtcars[order(mtcars$carb), ]

plot_ly(
  data = xx %>% tibble::rownames_to_column("id"),
  x = ~mpg, y = ~disp, text = ~id, color = ~factor(carb),
  type = "scatter", mode = "markers+text",
  marker = list(size = 10, opacity = 0.8),
  transforms = list(
    list(
      type = "filter",
      target = ~xx$cyl,
      operation = "=",
      value = sort(unique(xx$cyl))[1])))

Explanation

Looking at plotly:::plotly_build.plotly, we can see that sorting is applied in certain cases, e.g. when working with groups, or discrete colors or symbols. This sorting however does not include the transforms/filter, therefore the two things get out of sync.

If you want to look this up, you can also find arrange_safe() within https://github.com/ropensci/plotly/blob/master/R/plotly_build.R.

It should obviously be considered a bug, but a good fix is maybe not exactly trivial.

Another way of confirming that else it's fine, is if you just mask arrange_safe. E.g. like below:

library(godmode)
arrange_safe_new <- function(x, y) x
godmode:::assignAnywhere("arrange_safe", arrange_safe_new)

Afterwards, your case will be fine (but of course you might need the original arrange_safe functionality for other cases, so this is really just to prove a point).

Upvotes: 6

Related Questions