3

I have XY data which I'd like to graph using a scatter plot, with R's plotly package.

set.seed(1)
df <- data.frame(x=c(rnorm(30,1,1),rnorm(30,5,1),rnorm(30,9,1)),
                 y=c(rnorm(30,1,1),rnorm(30,5,1),rnorm(30,9,1)),
                 group=c(rep("A",30),rep("B",30),rep("C",30)),score=runif(90,0,1))

Each point is assigned to one of three groups (df$group) and has a score in the [0,1] range.

I'm looking for a way to graph the data such that each group is colored with a different color but the shade of that color (or intensity) reflects the score.

So I thought this would work:

library(dplyr)
library(plotly)

    plot_ly(marker=list(size=10),type='scatter',mode="markers",x=~df$x,y=~df$y,color=~df$score,colors=c("#66C2A5","#FC8D62","#8DA0CB")) %>%
  layout(xaxis=list(title="X",zeroline=F,showticklabels=F),yaxis=list(title="Y",zeroline=F,showticklabels=F))

But I get: enter image description here

If I just color code by group:

plot_ly(marker=list(size=10),type='scatter',mode="markers",x=~df$x,y=~df$y,color=~df$group,colors=c("#66C2A5","#FC8D62","#8DA0CB")) %>%
      layout(xaxis=list(title="X",zeroline=F,showticklabels=F),yaxis=list(title="Y",zeroline=F,showticklabels=F))

I get: enter image description here

So it looks like it is mixing the group colors and the score gradient.

What I'm looking for is to have the bottom left group colored in shades of green (say from gray to darkgreen) that correspond to score (low to high), and the same for the two other groups in orange and blue, respectively.

dan
  • 6,048
  • 10
  • 57
  • 125
  • They're not meant to be related to x and y. x and y simply separate the points by group (let's say "A" are dogs, "B" are cats, and "C" rats). The score is an orthogonal measurement (e.g. water consumption) – dan Jul 20 '18 at 01:12
  • If I understand you correctly you'd like 3 different colour gradients for the 3 different groups, where the gradient colour encodes the score. I don't think this is possible. There exist some hack-y ways to implement [two colour gradient scales in `ggplot2` (and then probably by extension in `plotly`)](https://stackoverflow.com/questions/18347147/using-two-scale-colour-gradients-ggplot2). Why not use different symbols for the group and then one common colour gradient for the scale? – Maurits Evers Jul 20 '18 at 01:18
  • Symbols will be the alternative if a gradient is not possible. – dan Jul 20 '18 at 01:22

2 Answers2

6

Using scales::colour_ramp you can create the colours yourself with a quick function. I'm not sure how else to get different gradients happening within each group. Note I'm using df$score = df$x + df$y here to make the mapping more obvious.

make_colour_gradient = function(x, brewer_palette = "Greens") {
    min_x = min(x)
    max_x = max(x)
    range_x = max_x - min_x
    x_scaled = (x - min_x) / range_x

    # Chopping out first colour as it's too light to work well as a
    #   point colour
    colours = scales::brewer_pal("seq", brewer_palette)(5)[2:5]

    colour_vals = scales::colour_ramp(colours)(x_scaled)
    colour_vals
}

df$score = df$x + df$y

df = df %>%
    # Assign a different gradient to each group, these are the names
    #   of different palettes in scales::brewer_pal
    mutate(group_colour = case_when(
        group == "A" ~ "Greens",
        group == "B" ~ "Oranges",
        group == "C" ~ "Purples"
    )) %>%
    group_by(group) %>%
    mutate(point_colour = make_colour_gradient(score, first(group_colour)))


plot_ly(marker=list(size=10),type='scatter',mode="markers",
        x=~df$x,y=~df$y,color=~ I(df$point_colour)) %>%
    hide_colorbar() %>%
    layout(xaxis=list(title="X",zeroline=F,showticklabels=F),
           yaxis=list(title="Y",zeroline=F,showticklabels=F))

Result:

Scatter plot with colours per group

This does bring up error messages but they don't seem to be important? Adding a legend to this would probably be tricky.

Marius
  • 58,213
  • 16
  • 107
  • 105
3

I'm not an expert in plotly, however, you could also use the alpha argument in ggplot and to achieve the same result and then convert the ggplot object to plotly. I know there is also an alpha argument in plotly, however, I found it a little difficult.

g <- ggplot(df, aes(x,y))+
    geom_point(aes(color = group, alpha = score), size = 3)+
    scale_color_manual(values = c("#66C2A5","#FC8D62","#8DA0CB"))+
    scale_alpha_continuous(range = c(0.3,1))+
    theme(panel.background = element_blank(),
        axis.text = element_blank(),
        axis.ticks = element_blank(),
        legend.position = "none")
gp <- ggplotly(g)
gp

enter image description here

I couldn't get the legend to format nicely with plotly, however in ggplot, we can keep the legend (omit the legend.position = "none") and we get the following: enter image description here

You can also increase the number of different shades using the breaks argument in scale_alpha_continuous.

AndS.
  • 7,748
  • 2
  • 12
  • 17