1

Below is a code that works using the plot() function to run a 2D scatterplot of Height vs Weight where points are classed as “Good”, “Fair”, “Poor” based on whether the Class value is 1, 2 or 3, respectively. Points for "Good" are bright green, "Fair", olive green and Poor, red. All points are the same size (pch=19). Is it possible to have different sizes and transparency for each data point depending on what each point is assigned in the “Group” column: either an Opaque and Small size point, Semi-transparent and Medium sized, or 100% Transparent and Large size point. Thanks for your ideas!

  df
  #           Group        Class      Height      Weight
  #  1       Opaque small     1 0.831777874 0.859223152
  #  2 Semi-transprnt med     2 0.751019511 0.807521752
  #  3 Semi-transprnt med     1 0.751019511 0.807521752
  #  4    Transprnt large     3 0.527390539 0.599957241
  #  5    Transprnt large     3 0.527390539 0.599957241

    color <- c(rgb(0, 1, 0, 1), rgb(0.5, 0.5, 0), rgb(1, 0, 0))
    plot(x=c(0.0, 0.5, 0.5, 0.0, 0.0), y=c(0.0, 0.0, 0.5, 0.5, 0.0), 
    type='l', col='gray', lwd=2,xlab='Height', ylab='Weight', 
    xlim=c(1,0), ylim=c(1, 0))
    par(new=T)
    plot(x=c(0.0, 0.5, 0.5, 0.0, 0.0), y=c(0.5, 0.5, 1, 1, 0.5), 
    type='l', col='gray', lwd=2, xlab='', ylab='', 
    xlim=c(1, 0.0), ylim=c(1, 0.0), axes=F)
    par(new=T)
    plot(x=c(0.5, 1, 1, 0.5, 0.5), y=c(0.0, 0.0, 0.5, 0.5, 0.0), 
    type='l', col='gray', lwd=2,
    xlab='', ylab='', xlim=c(1, 0.0), ylim=c(1, 0.0), axes=F)
    par(new=T)
    plot(x=c(0.5, 1, 1, 0.5, 0.5), y=c(0.5, 0.5, 1, 1, 0.5), type='l', 
    col='gray', lwd=2, xlab='', ylab='', 
    xlim=c(1, 0.0), ylim=c(1, 0.0), axes=F)
    par(new=T)
    for (i in 1:3) {
    plot(Height[Class==i], Weight[Class==i], xlim=c(0, 1), ylim=c(0, 1), 
    col=color[i], pch=19, xlab='', ylab='', axes=F)
    par(new=T)
    }
    legend(0.8, 0.586,legend=c('Good', 'Fair', 'Poor'), pch=19, 
    col=color, title='Class')
AlexP
  • 147
  • 2
  • 9
  • hey, you might take a look at [this](https://stackoverflow.com/questions/12995683/any-way-to-make-plot-points-in-scatterplot-more-transparent-in-r) and [this](https://stackoverflow.com/questions/2579995/control-the-size-of-points-in-an-r-scatterplot) – mischva11 Jul 01 '19 at 18:50
  • The `Group` and `Class` columns in the example `df` aren't consistent. Which one is correct? Also, should the `plot` function have `Weight` instead of `Length`? – A. S. K. Jul 01 '19 at 19:37
  • Weight is on the x axis and Length is y axis on scatterplot. What do you mean by plot function have Weight instead of Length?Group and Class columns in the example df aren't consistent? In the above code, I have plotted values in the "Class" column. I could enter a column for size and a separate column for transparency if that makes it easier? – AlexP Jul 01 '19 at 20:38
  • From your question, it sounds like the `Class` of each row should uniquely determine its `Group` (`1` -> `Opaque small`, etc.). But lines 1 and 3 in the example have the same `Class` but different `Group`s, and lines 2 and 3 have the same `Group` but different `Class`es. Also, the last call to `plot` has `Length[class==i]` as its second argument, but there's no `Length` column in `df`. – A. S. K. Jul 01 '19 at 21:02
  • Apologies, I have edited Length to Weight. Class and Group are not related Class is a biological condition. Group is based on the number of replicates for a particular site. If I plot all 900 points (some of which are replicates) the data points can overlap and obscure points underneath. To get round this, I wanted to plot an opaque small point that would represent 1 rep, semi-transparent medium sized point for 2-4 reps and transparent large point for reps > 5. I have classified those replicate groupings which are shown under column Group. Any ideas how to approach this? Thanks. – AlexP Jul 01 '19 at 21:23
  • I understand now; thanks. – A. S. K. Jul 01 '19 at 22:01

1 Answers1

0

Here's an approach that adds transparency using the method described in this answer (as suggested by @mischva11). First, add columns that contain the desired plotting properties:

library(dplyr)
library(tidyr)
library(scales)
df = df %>%
  separate(Group, into = c("Transparency", "Size"), sep = " ") %>%
  mutate(Color = case_when(Class == 1 ~ "Chartreuse",
                           Class == 2 ~ "Olive Drab",
                           Class == 3 ~ "Red"),
         Alpha = case_when(Transparency == "Opaque" ~ 0.9,
                           Transparency == "Semi-transprnt" ~ 0.9,
                           Transparency == "Transprnt" ~ 0.3),
         Size = case_when(Size == "small" ~ 0.8,
                          Size == "med" ~ 1,
                          Size == "large" ~ 1.2))

The replace the for loop in the original code with the following:

plot(df$Height, df$Weight, xlim = c(0, 1), ylim = c(0, 1),
     col = df$Color, pch = 21, xlab = "", ylab = "", axes = F,
     cex = df$Size, bg = alpha(df$Color, df$Alpha))

Edit: Using pch = 21 allows us to control the fill and border of the point separately (col for the border, bg for the fill). This example applied transparency to the fill but not the border.

On my machine, the semi-transparent point doesn't actually appear to be very transparent at all, so this might not quite accomplish what you're looking for. You could play around with the alpha values, or you could try ggplot instead. Here's how:

library(dplyr)
library(tidyr)
library(ggplot2)
df = df %>%
  separate(Group, into = c("Transparency", "Size"), sep = " ") %>%
  mutate(Color = case_when(Class == 1 ~ rgb(0, 1, 0),
                           Class == 2 ~ rgb(0.5, 0.5, 0),
                           Class == 3 ~ rgb(1, 0, 0)),
         Alpha = case_when(Transparency == "Opaque" ~ 1,
                           Transparency == "Semi-transprnt" ~ 0.4,
                           Transparency == "Transprnt" ~ 0.2),
         Size = case_when(Size == "small" ~ 1,
                          Size == "med" ~ 2,
                          Size == "large" ~ 4))
ggplot(df, aes(x = Height, y = Weight, col = Color, alpha = Alpha, size = Size)) +
  geom_point() +
  scale_color_identity() +
  scale_alpha_identity()

This is maybe a little better, although to me, as a naive viewer, it's difficult to distinguish between ordinary points (with expected colors) and overlapping points (with colors that are the sum of other colors). But this may be easier to see with the full dataset. Again, you could try playing around with the specific sizes and alpha values to see what works.

A. S. K.
  • 2,504
  • 13
  • 22
  • This is great. I can't thank you enough for taking the time to work on this! I've played around with the alpha values and sizes and it's better. I see the Color = rgb(Red, Green, Blue - which is different to the rgb colors I used. The code won't allow me to put in my rgb values that I had in the original code. So, I'm trying to change the rgb color code to Chartreuse (which is class 1), Olive Drab (Class 2) and Red (Class 3) which are the colors I had (as seen here: http://www.tayloredmktg.com/rgb/#OR). The new code won't run those color names.How do I change the colors back to the original? – AlexP Jul 01 '19 at 23:40
  • Good point; I was trying to make the color-assignment part shorter, but I think I ended up just making it confusing. I'll edit the post with a more transparent way! – A. S. K. Jul 02 '19 at 01:05
  • This works great! Thank you so much for doing this!! It's really appreciated. – AlexP Jul 02 '19 at 19:41
  • Do you know if its possible to put a border around data points? – AlexP Jul 02 '19 at 22:43
  • Hmm - what kind of border? One around each point, or a rectangle that encloses all the points? – A. S. K. Jul 03 '19 at 01:38
  • Appreciate you asking. What looks the least noisy and gets around the data point layering issue is to assign Chartreuse, Olive D and Red a Transprncy of 0.9, 0.9, 0.3 and Size, 0.8, 1.0, 1.2, respectively. This way you can just see the outline of a point underneath a point. I used your grouping code to do this. Ideally I'd like Red points to be 100% transp with just a red circle outline but the more I make red transp. the fainter the outline until it completely disappears at 100% transp. Is there a way to make Red hollow with a red circular outline, and add a circular outline to Olive Drab? – AlexP Jul 03 '19 at 18:00
  • Got it. With `pch = 21`, we can specify the fill and border independently. Post edited accordingly! – A. S. K. Jul 04 '19 at 13:58
  • Fantastic! Thank you So much for all your input! – AlexP Jul 05 '19 at 16:44