0

I have a sample dataframe in R.

df <- data.frame(product_id= c("A","A","A","B","B","B","C","C"), year = c("2019", "2020", "2021", "2019", "2020", "2021", "2019", "2020"), profit=c(1.5, 2.2, 2.3, 1.4, 1, 16, 1.9, 25), sale = c(1.2, 1.8, 1.9, 2.0, 1.6, 20.0, 3.0, 33.2))

I am trying to draw three separate graphs in ggplot where y='year', x='profit' and data will be filtered by product_id. All the geom_point will be circular shape. This can be achieved by following code for Product A:

library(ggplot2)
library(dplyr)
ggplot(df) +
  geom_point(data= df %>% filter(product_id =='A'), aes(x=year, y=profit, size=sale), shape = 16, color='Red') +
  scale_size_continuous(range = c(5, 10), guide = FALSE) +
  labs(x = "Year", y = "Profit")

Generated Plot

But for customization, I am willing to change the fill-color of the circle if the y-axis value (profit) is less than 2.0. The border should be Red as the original color but the fill-color should be yellow. For the above graph, the point at 2019 will be red border and yellow filled color. I have tried that following way but it's not working:

library(ggplot2)
library(dplyr)
ggplot(test_df) +
  geom_point(data= test_df %>% filter(product_id =='A'), aes(x=year, y=profit, size=sale), shape = 16, color='Red') +
  scale_size_continuous(range = c(5, 10), guide = FALSE) +
  scale_colour_manual(name = 'profit < 2', values = setNames(c('Yellow','Red'),c(T, F)), guide = FALSE) +
  labs(x = "Year", y = "Profit")

Another problem is for all three graphs the size of the circular shapes aren't maintaining a standard relative size. I am trying to develop a standard size scale like

for 'sale' column value <=1.9; size will be 5,

for 1.9 < sale_value <= 10; size range will be 10 to 20,

for 10 < sale_value <= 20; size will be 25

and for sale_value > 20; size will be 30.

I can't figure out how this can be implemented or even it's possible or not.

solo
  • 71
  • 7

2 Answers2

1

You could specify sizes explicitly:

df %>%
  mutate(size = case_when(
    sale <= 1.9 ~ 5,
    sale <= 10 ~ sale * 2,
    sale <= 20 ~ 25,
    sale > 20 ~ 30
  )) -> df2

And then map profit < 2 to fill, using your color definitions:

ggplot(df2) +
  geom_point(data= df2 %>% filter(product_id =='A'), color = "red",
             aes(x=year, y=profit, size=size, fill = profit < 2), shape = 21) +
  scale_size_identity() +
  scale_fill_manual(name = 'profit < 2', values = setNames(c('Yellow','Red'),c(T, F)), guide = FALSE)  +
  labs(x = "Year", y = "Profit")

enter image description here

Note, by default the size aesthetic is mapped to point radius, which means the perceptual area will increase with that value ^ 2. If that's not what you want, you could map the square root of your value, times a constant for taste, to get sizes proportional to area.

Jon Spring
  • 55,165
  • 4
  • 35
  • 53
  • I don't want the whole circle to be yellow for profit < 2; I want it to be red border and yellow fill color as I mentioned earlier in the question. Any suggestions? – solo Mar 21 '21 at 09:09
  • 1
    updated. shape 21 lets us assign color to the border and fill to the center. – Jon Spring Mar 21 '21 at 15:54
1

This code might be helpful to you:

df2 <- data.frame(
  product_id = c("D", "D", "D", "D"), 
  year = c("2020", "2020", "2020", "2020"), 
  profit = c(1.5, 15, 25, 35), 
  sale = c(1.5, 15, 25, 35) 
)

df3 <- rbind(df, df2)

ggplot(df3) +
  aes(x = year, y = profit, fill = profit < 2) + 
  facet_wrap(~ product_id) +
  geom_point(
    aes(size = sale), 
    color = "red", 
    pch = 21
  ) +
  scale_fill_manual(values = c("red", "yellow"))

To fill by a different color based on the value, you need a shape that takes both a fill and a color (pch 21-25). Then, you can map the fill aesthetic to profit < 2 and set the color to be constantly red. I use scale_fill_manual to define the colors that I want.

Will Oldham
  • 704
  • 3
  • 13