3

Say I have this 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 ))

I want to group it by the foo and bar columns to arrive at this:

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

I can generate a frame with a row for each group, but I couldn't find a MATCH function; something that when given an input frame with columns foo,bar,baz,qux and a filter frame with columns foo,bar returns the rows where the foo,bar cell's content matches.

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

Or a higher level GROUP function which just returns a list of frames, where the columns given match:

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

The closest I came to that is

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

Where the cells baz, qux are lists:

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

So each group is a row in out, but how do I unfold this again, so that out[1,] becomes a data.frame with two rows, like expected[[1]]?

4

3 回答 3

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
于 2013-07-24T15:35:25.517 回答
3

dlplyfromplyr正是为此目的而设计的:

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
于 2013-07-24T15:37:02.443 回答
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
于 2013-07-24T15:37:57.907 回答