按多列对data.frame进行分组

pas*_*cal 3 r

说我有这个data.frame

data <- data.frame(foo = c(1, 1, 2, 2 ),
                   bar = c(10,10,10,20),
                   baz = c(1, 2, 3, 4 ),
                   qux = c(5, 6, 7, 8 ))
Run Code Online (Sandbox Code Playgroud)

我想通过foobar列对它进行分组以达到此目的:

expected <- list(
  data.frame(foo = c(1, 1),
             bar = c(10, 10),
             baz = c(1, 2),
             qux = c(5, 6)),
  data.frame(foo = 2,
             bar = 10,
             baz = 3,
             qux = 7),
  data.frame(foo = 2,
             bar = 20,
             baz = 4,
             qux = 8)
)
Run Code Online (Sandbox Code Playgroud)

我可以为每个组生成一个行,但是我找不到一个MATCH函数; 当给定带有列的输入框架和带有列foo,bar,baz,qux的过滤器框架时,foo,bar返回foo,bar单元格内容匹配的行.

groups <- unique(data[c("foo","bar")])
MATCH(data, groups[1,]) == expected[[1]]
MATCH(data, groups[2,]) == expected[[2]]
MATCH(data, groups[3,]) == expected[[3]]
Run Code Online (Sandbox Code Playgroud)

或者更高级别的GROUP函数,它只返回一个框架列表,给定的列匹配:

GROUP(data, by=c("foo","bar")) == expected
Run Code Online (Sandbox Code Playgroud)

我最接近的是

out <- aggregate(. ~ foo + bar, data, list)
Run Code Online (Sandbox Code Playgroud)

当细胞baz,qux是列表:

> out
  foo bar  baz  qux
1   1  10 1, 2 5, 6
2   2  10    3    7
3   2  20    4    8
> class(out[,"baz"])
[1] "list"
Run Code Online (Sandbox Code Playgroud)

所以每个组都是一行out,但是我如何再次展开它,这样out[1,]就成了一个有两行的data.frame,比如expected[[1]]

A5C*_*2T1 7

看起来你只需要split.

选项1:保留"foo"和"bar"组合的所有"级别",即使它导致空data.frame.

> split(data, list(data$foo, data$bar))
$`1.10`
  foo bar baz qux
1   1  10   1   5
2   1  10   2   6

$`2.10`
  foo bar baz qux
3   2  10   3   7

$`1.20`
[1] foo bar baz qux
<0 rows> (or 0-length row.names)

$`2.20`
  foo bar baz qux
4   2  20   4   8
Run Code Online (Sandbox Code Playgroud)

选项2:删除"foo"和"bar"组合的空"级别" - 就像您在预期输出中所做的那样.

> split(data, list(data$foo, data$bar), drop=TRUE)
$`1.10`
  foo bar baz qux
1   1  10   1   5
2   1  10   2   6

$`2.10`
  foo bar baz qux
3   2  10   3   7

$`2.20`
  foo bar baz qux
4   2  20   4   8
Run Code Online (Sandbox Code Playgroud)