How do I combine these into a single table?

Viewed 40

I'm working with some survey data and I want to summarize responses from everyone, and responses from members in a single table.

The best way I can think of to translate this to Starwars is that I want to know how many characters total have any one eye color, and how many female characters have that eye color. For simplicity, I limited the population to blue and brown eyes.

I can run to separate queries, one to show just the females:

starwars %>% 
  filter(eye_color %in% c("brown","blue")) %>% 
  count(eye_color, gender) %>% 
  filter(gender == "female") %>% 
  mutate(percent = n / sum(n) * 100,
         percent = sprintf("%.0f%%", percent))

And one to show all characters regardless of gender:

starwars %>% 
  filter(eye_color %in% c("brown","blue")) %>% 
  count(eye_color) %>% 
  mutate(percent = n / sum(n) * 100,
         percent = sprintf("%.0f%%", percent))

But I'd like to spit these out as a single table. Is there a better approach to that than just pasting the two resulting tibbles together?

1 Answers

I still don't know of a good way to group by data where groups overlap in dplyr without repeating data. So I think combing the data data from two different pipelines is fine. If you want to elimiated code duplication, you could write a helper function. Here's one such example

plus_margin <- function(data, filters, fun=identity, .id="id") {
  stopifnot(is.list(filters))
  stopifnot(!is.null(names(filters)))
  stopifnot(all(sapply(filters, is.function)))
  stopifnot(is.function(fun))
  bind_rows(
    map_dfr(filters, ~data %>% .x %>% fun, .id=.id),
    data %>% fun %>% mutate(.id:="all")
  )
}

Then you could call it with something like

starwars %>% 
  filter(eye_color %in% c("brown","blue")) %>% 
  plus_margin(list(
    feminine = . %>% filter(gender == "feminine")
  ),
  . %>% count(eye_color) %>% 
    mutate(percent = n / sum(n) * 100,
           percent = sprintf("%.0f%%", percent))
  )

Which returns

  id       eye_color     n percent
  <chr>    <chr>     <int> <chr>  
1 feminine blue          6 55%    
2 feminine brown         5 45%    
3 all      blue         19 48%    
4 all      brown        21 52%  

The idea is that you pass in a list of filters to subset the data by. These filters, should be functions that take data and subset it in some way. The list should be named and the names will be used as values in the resulting "id" column. Here we use the magrittr syntax . %>% {} to create an anonymous function. We the need to pass in a function apply to each of the subsets.

But at the end of the day, the joining is still happening with bind_rows. Maybe someone else will suggest a better way.

Related