3
votes

facet_grid and facet_wrap have the scales parameter, which as far as I know allows each plot to adjust the scales of the x and/or y axis to the data being plotted. Since according to the grammar of ggplot x and y are just two among many aesthetics, and there's a scale for each aesthetic, I figured it would be reasonable to have the option of letting each aesthetic be free, but so far I didn't find a way to do it.

I was trying to set it in particular for the Size, since sometimes a variables lives in a different order of magnitude depending on the group I'm using for the facet, and having the same scale for every group blocks the possibility of seeing within-group variation.

A reproducible example:

set.seed(1)
x <- runif(20,0,1)
y <- runif(20,0,1)
groups <- c(rep('small', 10), rep('big', 10))
size_small <- runif(10,0,1)
size_big   <- runif(10,0,1) * 1000

df <- data.frame(x, y, groups, sizes = c(size_small, size_big))

And an auxiliary function for plotting:

basic_plot <- function(df) ggplot(df) + 
  geom_point(aes(x, y, size = sizes, color = groups)) + 
  scale_color_manual(values = c('big' = 'red', 'small' = 'blue')) +
  coord_cartesian(xlim=c(0,1), ylim=c(0,1))

If I we plot the data as is, we get the following:

basic_plot(df)

Non faceted plot

The blue dots are relatively small, but there is nothing we can do. If we add the facet:

basic_plot(df) +
  facet_grid(~groups, scales = 'free')

Faceted plot

The blue dots continue being small. But I would like to take advantage of the fact that I'm dividing the data in two, and allow the size scale to adjust to the data of each plot. I would like to have something like the following:

plot_big <- basic_plot(df[df$groups == 'big',])
plot_small <- basic_plot(df[df$groups == 'small',])

grid.arrange(plot_big, plot_small, ncol = 2)

What I want

Can it be done without resorting to this kind of micromanaging, or a manual rescaling of the sizes like the following?

df %>% 
  group_by(groups) %>% 
  mutate(maximo = max(sizes),
         sizes = scale(sizes, center = F)) %>% 
  basic_plot() +
  facet_grid(~groups)

I can manage to do those things, I'm just trying to see if I'm not missing another option, or if I'm misunderstanding the grammar of graphics.

Thank you for your time!

1
Not really, without creating separate plots with separate legends, as you've done. - joran
When you facet a plot, you only get one legend. With scales like size, you wouldn't be able to raw just one legend to represent the values for all facets if they each used a different range. With the x and y scales, it's just easier because those scales are rendered as axis rather than put in legends. You could add a column to your name which normalizes the size scale variable within each group so they are directly comparable perhaps. - MrFlick
That makes a lot of sense! Thank you very much. - mgiormenti

1 Answers

2
votes

As mentioned, original plot aesthetics are maintained when calling facet_wrap. Since you need grouped graphs, consider base::by (the subsetting data frame function) wrapped in do.call:

do.call(grid.arrange, 
        args=list(grobs=by(df, df$groups, basic_plot), 
                  ncol=2,
                  top="Grouped Point Plots"))

Grouped Plot Output


Should you need to share a legend, I always use this wrapper from @Steven Lockton's answer

do.call(grid_arrange_shared_legend, by(df, df$groups, basic_plot))

Shared Legend Plot Output