1

Similar to this question and also this, my preferred output is to have the legend for geom_text to be the text label, instead of 'aaa'. (ie the 'aaa' inside the circle of the mchild legend should be 0,1,2,3,4. Please see the attached picture)

Here is my data:

 data.frame(
            ID = c(1L, 3L, 5L, 11L, 13L, 18L, 20L, 24L, 33L, 34L, 36L),
       sum_hrs = c(12, 8, 68, 44, 16, 12, 36, 16, 4, 20, 4),
  avg_workload = c(263.1615,275.312,269.462444444444,
                   268.867666666667,276.686,257.3605,267.8695,268.3355,
                   239.409,260.230333333333,330.061),
      avg_achv = c(92.5,98,94.2222222222222,
                   94.8333333333333,92,98,94.25,93.5,98,95.3333333333333,100),
        mchild = c(1L, 0L, 1L, 2L, 3L, 0L, 4L, 0L, 2L, 0L, 1L),
       drinker = as.factor(c("No","Yes","Yes",
                             "Yes","Yes","No","Yes","Yes","No","No",
                             "Yes"))
)

I have tried the answer suggestions in the related SO questions, but could not get it to work. Most probably my grobs indexing is incorrect. So I have read the documentation but still could not quite get it. Here is what I have done so far:

p2 <- ggplot(data=sum_ua, aes(x=avg_achv, y=sum_hrs, size=mchild, color=drinker, alpha=sum_hrs)) +
    geom_point() +
    geom_text(data=sum_ua, aes(x=avg_achv, y=sum_hrs, label=mchild), color='#A9A9A9') +
    scale_size(range=c(4,20)) +
    scale_alpha_binned(range = c(0.01, 1), guide = 'none') +    
    new_scale_color()

g <- ggplotGrob(p2)
lbls <- unique(sort(sum_ua$mchild))
g$grobs[[15]][[1]][[1]]$grobs[[1]]$label <- lbls[1]
g$grobs[[15]][[1]][[1]]$grobs[[6]]$label <- lbls[2]
g$grobs[[15]][[1]][[1]]$grobs[[8]]$label <- lbls[3]

g$grobs[[grep("guide", g$layout$name)]]

grid.draw(g)

Here is my output: p2 plot output

Sorry for the basic question and thank you for your help! :)

sarah am
  • 25
  • 1
  • 7

2 Answers2

3

As an alternative to messing around in the gtable of the plot, you can make a custom scale_discrete_identity() for the label aesthetic. You just need to make sure that it has a legend and that all information is similar to the size legend (title, breaks, labels etc.).

library(ggplot2)
library(ggnewscale)

sum_ua <- data.frame(
  ID = c(1L, 3L, 5L, 11L, 13L, 18L, 20L, 24L, 33L, 34L, 36L),
  sum_hrs = c(12, 8, 68, 44, 16, 12, 36, 16, 4, 20, 4),
  avg_workload = c(263.1615,275.312,269.462444444444,
                   268.867666666667,276.686,257.3605,267.8695,268.3355,
                   239.409,260.230333333333,330.061),
  avg_achv = c(92.5,98,94.2222222222222,
               94.8333333333333,92,98,94.25,93.5,98,95.3333333333333,100),
  mchild = c(1L, 0L, 1L, 2L, 3L, 0L, 4L, 0L, 2L, 0L, 1L),
  drinker = as.factor(c("No","Yes","Yes",
                        "Yes","Yes","No","Yes","Yes","No","No",
                        "Yes"))
)

ggplot(data=sum_ua, aes(x=avg_achv, y=sum_hrs, size=mchild, color=drinker, alpha=sum_hrs)) +
  geom_point() +
  geom_text(data=sum_ua, aes(x=avg_achv, y=sum_hrs, label=as.character(mchild)), color='#A9A9A9') +
  scale_size(range=c(4,20)) +
  scale_alpha_binned(range = c(0.01, 1), guide = 'none') +   
  scale_discrete_identity(guide = "legend", aesthetics = "label",
                          name = "mchild") +
  new_scale_color()

Created on 2021-02-01 by the reprex package (v0.3.0)

teunbrand
  • 33,645
  • 4
  • 37
  • 63
  • Thank you so much teunbrand. This works! :) What if I wanted to remove/hide the text next to the circles, what should I do? I tried to use scale_fill_identity but couldn't make it work.. – sarah am Feb 01 '21 at 10:23
  • I think you can do that by either setting `labels = NULL` in both the size and discrete identity scale, ór by setting `guide = guide_legend(label = FALSE)` in both those scales. – teunbrand Feb 01 '21 at 10:29
0

For your case this seems to work

g <- ggplotGrob(p2)
g$grobs[[15]][[1]][[1]]$grobs[[5]]$label <- 0
g$grobs[[15]][[1]][[1]]$grobs[[8]]$label <- 1
g$grobs[[15]][[1]][[1]]$grobs[[11]]$label <- 2
g$grobs[[15]][[1]][[1]]$grobs[[14]]$label <- 3
g$grobs[[15]][[1]][[1]]$grobs[[17]]$label <- 4
grid::grid.draw(g)

Or to avoid hardcoding and repeating the same code again we can use :

labels <- 0:4
inds <- which(colSums(sapply(g$grobs[[15]][[1]][[1]]$grobs,class) == 'text') > 0)
g$grobs[[15]][[1]][[1]]$grobs[inds] <- Map(function(x, y) {x$label <- y;x}, 
                        g$grobs[[15]][[1]][[1]]$grobs[inds], labels)
grid::grid.draw(g)

enter image description here

Ronak Shah
  • 377,200
  • 20
  • 156
  • 213
  • Thank you Ronak, your output is what I am trying to achieve :) But when I tried to use your solution, I got this error.. Error in `*tmp*`[[11]]: subscript out of bounds... Could you help me out? – sarah am Feb 01 '21 at 09:37
  • That's strange it works fine for me for the data you have shared. Are you applying this on the same dataset? Which line gives you the error? Did you run `g <- ggplotGrob(p2)`? I haven't added `new_scale_color()` in `p2` since I don't know from which package it is, not sure if that makes any difference. – Ronak Shah Feb 01 '21 at 09:43
  • Yup, using the same dataset. And yes, I ran the g <- ggplotGrob(p2). Got error starting from this line: g$grobs[[15]][[1]][[1]]$grobs[[11]]$label <- 2. So then I tried to change the grobs index, and it seems like index more than 8 gives an error.. I have also removed new_scale_color() to mirror your answer.. I think the indexing is wrong for my plot.. Can you show me how you get the index for each of the label? (your 5, 8, 11, 14, 17 grob index) – sarah am Feb 01 '21 at 09:51