首页 文章

按多列对data.frame进行分组

提问于
浏览
3

说我有这个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 回答

  • 7

    看起来你只需要 split .

    Option 1 :保留"foo"和"bar"组合的所有"levels",即使它导致空 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
    

    Option 2 :删除"foo"和"bar"组合的空"levels" - 就像在预期输出中一样 .

    > 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
    
  • 3

    来自 plyrdlply 就是为此目的而设计的:

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

相关问题