dplyr with subgroup join

5.6k Views Asked by At

The following problem can be seen as a "two-column reshape to wide", and there are several methods available to solve it the classical way, from base::reshape (horror) to reshape2. For the two-group case, a simple subgroup join works best.

Can I reformulate the join within the piping framework of dplyr? The example below is a bit silly, but I needed the join in a longer pipe-chain which I do not want to break.

library(dplyr)
d = data.frame(subject= rep(1:5,each=2),treatment=letters[1:2],bp = rnorm(10))

d %>%
  # Assume piped manipulations here
  # Make wide
  # Assume additional piped manipulations here

# Make wide (old style)
with(d,left_join(d[treatment=="a",],
          d[treatment=="b",],by="subject" ))
2

There are 2 best solutions below

2
On BEST ANSWER

How about

d %>% 
  filter(treatment == "a") %>%
  left_join(., filter(d, treatment == "b"), by = "subject")

#  subject treatment.x       bp.x treatment.y      bp.y
#1       1           a  0.4392647           b 0.6741559
#2       2           a -0.6010311           b 1.9845774
#3       3           a  0.1749082           b 1.7678771
#4       4           a -0.3089731           b 0.4427471
#5       5           a -0.8346091           b 1.7156319

You could continue the pipe right after the left join.

Or if you don't require the separate treatment columns, you could use tidyr to do:

library(tidyr)
d %>% spread(treatment, bp)
#  subject          a         b
#1       1  0.4392647 0.6741559
#2       2 -0.6010311 1.9845774
#3       3  0.1749082 1.7678771
#4       4 -0.3089731 0.4427471
#5       5 -0.8346091 1.7156319

(which is the same as using d %>% dcast(subject ~ treatment, value.var = "bp") from reshape2 package as noted by Henrik in the comments)

0
On

Solution with group_by instead of join.

d %>% 
group_by(subject) %>% 
summarize(bp_a = bp[match("a",treatment)], 
          bp_b = bp[match("b",treatment)])