geom_point size and scale_radius as absolute values

I am struggling with adjusting the point size in geom_point(). I have read through several questions on SO, but haven't found an answer that solved my issue.

I would like the size of the points generated by geom_point() to always have the same size, in terms of units on the x and y-axis, regardless of the size of plot or limits of the x and y-axis.

an example:

library(tidyverse)

tribble(
  ~object,    ~x,    ~y,   ~group, ~size,
  "head",      100,   180,      1,    10, 
  "neck",      100,   160,      1,    3, 
  "pelvis",    100,   100,      1,    3, 
  "lhip",       90,    90,      2,    3, 
  "rhip",      110,    90,      3,    3, 
  "lfoot",      50,     0,      2,    3, 
  "rfoot",      150,    0,      3,    3,
  "lshoulder",   85,    155,    4,    3, 
  "rshoulder",  115,    155,    5,    3, 
  "lhand",      75,     100,    4,    3, 
  "rhand",      125,    100,    5,    3) %>% 
  ggplot(aes(x, y, group = group, color = as.factor(group)))+
  geom_point(aes(size=size))+
  geom_path()+
  coord_equal()+
  xlab("cm")+
  ylab("cm")+
  theme(legend.position = "none")+
  scale_radius(range = c(4, 15))

This looks fine, but if I change e.g. the limits of the axis the point size becomes distorted, e.g., we can add another person standing 10 m away.

tribble(
  ~object,    ~x,    ~y,   ~group, ~size,
  "head",      100,   180,      1,    10, 
  "neck",      100,   160,      1,    3, 
  "pelvis",    100,   100,      1,    3, 
  "lhip",       90,    90,      2,    3, 
  "rhip",      110,    90,      3,    3, 
  "lfoot",      50,     0,      2,    3, 
  "rfoot",      150,    0,      3,    3,
  "lshoulder",   85,  155,      4,    3, 
  "rshoulder",  115,  155,      5,    3, 
  "lhand",      75,   100,      4,    3, 
  "rhand",      125,  100,      5,    3,
  #Person two
  "head",       1500,  180,      6,    10, 
  "neck",       1500,  160,      6,    3, 
  "pelvis",     1500,  100,      6,    3, 
  "lhip",       1490,   90,      7,    3, 
  "rhip",       1510,   90,      8,    3, 
  "lfoot",      1450,    0,      7,    3, 
  "rfoot",      1550,    0,      8,    3,
  "lshoulder",  1485,  155,      9,    3, 
  "rshoulder",  1515,  155,      10,   3, 
  "lhand",      1475,  100,      9,    3, 
  "rhand",      1525,  100,      10,   3) %>% 
  ggplot(aes(x, y, group = group, color = as.factor(group)))+
  geom_point(aes(size=size))+
  geom_path()+
  coord_equal()+
  xlab("cm")+
  ylab("cm")+
  theme(legend.position = "none")+
  scale_radius(range = c(4, 15))

Is there some way I can set the point size to an absolute value measured in either the x or y-axis or both of them? Let's say I would like the head always to have a radius of 10 cm, and the remaining points to have a radius of 3 cm.

I understand that this is not the common usage of ggplot, but I imagine the same issue can arise if someone is plotting a map and size of the points needs reflect an actual area of the map. Is there perhaps some other trick I can use to get around this issue?

Any advice or help is much appreciated.

You could use geom_circle from the ggforce package to plot the points. geom_circle plots circles with an absolute size (in x/y axis units) that you specify as an aesthetic mapping from the data (or the radius can also be hard-coded).

In the code below, dat1 is your first data frame and dat2 is your second data frame. geom_circle has aesthetics x0 and y0 (the center of the circle), and r (the radius). In your data examples, the center of each circle is given by columns x and y and the radius is size/2.

library(tidyverse)
library(ggforce)

p1 = dat1 %>% 
  ggplot(aes(x0=x, y0=y, r=size/2, 
             group=group, fill=factor(group), color=factor(group))) +
    geom_circle() +
    geom_path(aes(x,y)) +
    coord_equal() + 
    labs(x="cm", y="cm") + 
    theme_classic() +
    theme(legend.position = "none") 

p2 = dat2 %>% 
  ggplot(aes(x0=x, y0=y, r=size/2, 
             group=group, fill=factor(group), color=factor(group))) +
  geom_circle() +
  geom_path(aes(x,y)) +
  coord_equal() + 
  labs(x="cm", y="cm") + 
  theme_classic() +
  theme(legend.position = "none") 

gridExtra::grid.arrange(p1, p2, ncol=2)

4 Likes

Thanks alot Joels, this is perfect :slight_smile:
I had no idea the geom_circle existed(), another gem by Thomas Lin Pedersen

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.