Using custom shapes in ggplot

66 views Asked by At

I want to use different degrees of fill in the shape circle to represent different variables in my data set. I have a column in my data set called 'shape' that has numbers 0,1,2,3 that correspond to certain combinations of variables that I would like to depict in my graph. I want:

  • 0 = empty circle (outline only)
  • 1 = half filled circle (left side)
  • 2 = half filled circle (right side)
  • 3 = full circle

I can set 4 different shapes using the shape codes built into R, but unfortunately I cannot find any codes for a half filled circle. Because of this I have tried to build my own shapes, however I cannot seem to get ggplot2 to recognize them and either they just appear as squares or I get a warning message removing these rows.

# 4 different shapes using shape values from R   ###  
# define dodge position   
pd <- position_dodge(.6)  

newdata_small %%  
  ggplot(aes(x=as.numeric(F6_GR),  
             y=emmean,  
             shape=shape)) + # shape = condition   
  geom_errorbar(aes(ymin=emmean-SE,   
                    ymax=emmean+SE), width=.2, position = pd) +  
  geom_line(position = pd)+  
  geom_point(position = pd, size=5) +
  theme_classic() +
  ggtitle("Small Populations \n")+
  scale_shape_manual(name= "Heat Waves",
                     labels=c("No heat waves", "Initial heat wave only",
                              "Final heat wave only ", "Initial & final heat wave"), 
                     values = c(1, 5, 6, 19))
# Looks good but I want half filled circles.

Graph1

Then I tried to create my own custom shapes, using this code:

library(grid)

# Define custom point shapes
half_filled_left <- function(size) {
  polygonGrob(x = c(-0.5, 0.5, 0.5, -0.5), 
              y = c(-0.5, -0.5, 0.5, 0.5),
              gp = gpar(col = "black", fill = "yellow"))
  arc <- seq(0, pi, length.out = 100)
  linesGrob(x = cos(arc) * 0.5, 
            y = sin(arc) * 0.5, 
            gp = gpar(col = "black", lwd = size / 5))
}

half_filled_right <- function(size) {
  polygonGrob(x = c(-0.5, 0.5, 0.5, -0.5), 
              y = c(-0.5, -0.5, 0.5, 0.5), 
              gp = gpar(col = "black", fill = "red"))
  arc <- seq(pi, 2*pi, length.out = 100)
  linesGrob(x = cos(arc) * 0.5, 
            y = sin(arc) * 0.5, 
            gp = gpar(col = "black", lwd = size / 5))
}

This is how I tried to add to my plot:

newdata_small %>%
  ggplot(aes(x=as.numeric(F6_GR),
             y=emmean,
             shape=shape)) + #shape = condition 
  geom_errorbar(aes(ymin=emmean-SE, 
                    ymax=emmean+SE), 
                width=.2, position = pd) +
  geom_line(position = pd)+
  geom_point(position = pd, size=5) +
  theme_classic() +
  ggtitle("Small Populations \n")+
  scale_shape_manual(name= "Heat Waves",
                     labels=c("No heat waves", "Initial heat wave only",
                              "Final heat wave only ", "Initial & final heat wave"), 
                     values = c("0" = 1,  # Circle outline  only
                                "1" = half_filled_left(5),  # Half filled circle left side
                                "2" = half_filled_right(5),  # Half filled circle right side
                                "3" = 16))   # Default filled circle

Graph2

But it just returns an error message that it has removed the 4 rows that I want depicted as the custom shapes. What is the best way to use these custom shapes in my code, or is there a way to specify a half filled circle?

1

There are 1 answers

0
stefan On

One lightweight option to achieve your desired result would be to use unicode shapes, i.e. "\u25d6" and "\u25d7" will give you a left/right half circle. Note however, that not all fonts support these symbols. Additionally, you can't mix unicode with the default shapes, so you have to use unicode symbols for the other shapes as well:

library(ggplot2)

pd <- position_dodge(.6)

newdata_small <- data.frame(
  F6_GR = factor(rep(LETTERS[1:2], 4)),
  shape = as.character(rep(0:3, each = 2)),
  emmean = 1:8,
  SE = .25
)

newdata_small |> 
  ggplot(aes(
    x = as.numeric(F6_GR),
    y = emmean,
    shape = shape
  )) +
  geom_errorbar(aes(
    ymin = emmean - SE,
    ymax = emmean + SE
  ), width = .2, position = pd) +
  geom_line(position = pd) +
  geom_point(position = pd, size = 6) +
  scale_shape_manual(
    name = "Heat Waves",
    labels = c(
      "No heat waves", "Initial heat wave only",
      "Final heat wave only ", "Initial & final heat wave"
    ),
    values = c("\u25cb", "\u25d6", "\u25d7", "\u25cf")
  ) +
  theme_classic() +
  theme(plot.title = element_text(
    margin = margin(b = 20)
  )) +
  ggtitle("Small Populations")

enter image description here