按多列对data.frame进行分组

时间:2013-07-24 15:31:49

标签: 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 ))

我希望按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)
)

我可以为每个组生成一个行,但是我找不到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]]

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

GROUP(data, by=c("foo","bar")) == expected

我最接近的是

out <- aggregate(. ~ foo + bar, data, list)

单元格bazqux是列表:

> 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"

因此,每个组都在out中排成一行,但是如何再次展开这一行,以便out[1,]成为一个包含两行的data.frame,例如expected[[1]]

3 个答案:

答案 0 :(得分: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

选项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

答案 1 :(得分:3)

来自dlply

plyr就是为此目的而设计的:

require(plyr)    
dlply( data , .(foo , bar) )

$`1.10`
  foo bar baz qux
1   1  10   1   5
2   1  10   2   6

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

$`2.20`
  foo bar baz qux
1   2  20   4   8

答案 2 :(得分:0)

试试这个,就像@Ananda的解决方案,但使用interaction

> split(data,interaction(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 rækker> (eller 0-længde row.names)

$`2.20`
  foo bar baz qux
4   2  20   4   8

> split(data,interaction(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