R为两列的所有排列引入新行并对它们进行分组

时间:2016-04-25 13:37:49

标签: r scripting group-by permutation

我有一个包含周数和日期数的表格,我想将其分组以创建所有可能的组合并使用它们创建一个新表格。

enter image description here

我试过了:

 w =(book2$week_no)
 d = (book2$day_no)

 b=expand.grid(w,d)
 b=b[c(TRUE,FALSE),] 

它给了我所有的排列。

    Var1 Var2
  1  week1 day1
  3  week2 day1
  5  week1 day2
  7  week2 day2
  9  week1 day1
  11 week2 day1
  13 week1 day2
  15 week2 day2

但我不知道如何使用这些结果创建新表。如果这是一个愚蠢的问题,我很抱歉。

2 个答案:

答案 0 :(得分:3)

我认为您没有完全清楚地描述您所需的输出。您说您希望“分组以创建所有可能的组合”,但分组通常意味着聚合到较小的表中。从屏幕截图中看,您似乎希望从输入表中复制行以生成更大的表。更具体地说,您希望在每个唯一week_no值的行集之间采用笛卡尔积,并且您希望产品行对按顺序堆叠在输出表中的附加皱纹,而不是并排加入。这是一个很好的方法:

df <- data.frame(raw_id=1:4,week_no=c('week1','week1','week2','week2'),day_no=c('day1','day2','day1','day2'),val=c(234,123,235,111),stringsAsFactors=F);
df[t(do.call(expand.grid,split(seq_len(nrow(df)),df$week_no))),];
##     raw_id week_no day_no val
## 1        1   week1   day1 234
## 3        3   week2   day1 235
## 2        2   week1   day2 123
## 3.1      3   week2   day1 235
## 1.1      1   week1   day1 234
## 4        4   week2   day2 111
## 2.1      2   week1   day2 123
## 4.1      4   week2   day2 111

让我们打破这一点:

seq_len(nrow(df));
## [1] 1 2 3 4

上面生成了输入表中存在的行索引向量。

split(seq_len(nrow(df)),df$week_no);
## $week1
## [1] 1 2
##
## $week2
## [1] 3 4
##

然后我们在df$week_no值上拆分向量以生成一个命名列表,其中每个组件保存输入表中week_no的唯一值的行索引,并捕获该值很好地作为组件名称(虽然我们不会在解决方案中使用该名称)。

do.call(expand.grid,split(seq_len(nrow(df)),df$week_no));
##   week1 week2
## 1     1     3
## 2     2     3
## 3     1     4
## 4     2     4

拆分列表的格式非常适合直接作为do.call()的arg列表传递,以便调用expand.grid()。这就是我们如何在两个week_no值之间得到行索引的笛卡尔积。

我们可以通过索引输入表中的上述行对来获得所需的输出,通过沿着行从左到右跟随索引表顺序堆叠它们,然后沿着列从上到下(通常只是调用“按行”)。但是有两个原因导致我们不能直接使用上面的对象作为行维度下标:(1)它是一个data.frame,我们需要一个简单的行索引向量,(2)即使我们可以传递它作为行下标的矩阵,矩阵数据自然地在内存中“按列”排列,这意味着索引将按照所需输出的错误顺序进行。

t(do.call(expand.grid,split(seq_len(nrow(df)),df$week_no)));
##       [,1] [,2] [,3] [,4]
## week1    1    2    1    2
## week2    3    3    4    4

我们可以通过单次调用t()来解决上述两个问题,它会自动将data.frame强制转换为矩阵并对其进行转置,因此自然的“by column”遍历将导致正确的索引顺序。

df[t(do.call(expand.grid,split(seq_len(nrow(df)),df$week_no))),];
##     raw_id week_no day_no val
## 1        1   week1   day1 234
## 3        3   week2   day1 235
## 2        2   week1   day2 123
## 3.1      3   week2   day1 235
## 1.1      1   week1   day1 234
## 4        4   week2   day2 111
## 2.1      2   week1   day2 123
## 4.1      4   week2   day2 111

瞧。

啊,几乎忘记了所需输出中的group列。由于输出表是以常规方式生成的,这意味着它是由输入行的笛卡尔积生成的,因此我们可以在事后推导出组值。假设您已将上述结果存储为res

,请按照以下步骤操作
res$group <- rep(seq_len(prod(table(df$week_no))),each=length(unique(df$week_no)));
res;
##     raw_id week_no day_no val group
## 1        1   week1   day1 234     1
## 3        3   week2   day1 235     1
## 2        2   week1   day2 123     2
## 3.1      3   week2   day1 235     2
## 1.1      1   week1   day1 234     3
## 4        4   week2   day2 111     3
## 2.1      2   week1   day2 123     4
## 4.1      4   week2   day2 111     4

我刚才意识到,你的问题和我的答案都没有解决week_no中三个或更多个唯一值的情况。我的解决方案恰好在所有week_no行集之间采用可变笛卡尔积,但我意识到您可能更喜欢在所有week_no行集之间仅使用二元笛卡尔积,这需要不同的解决方案。这是一个具有挑战性和有趣的问题,但我相信我明白了:

df <- data.frame(raw_id=1:6,week_no=c('week1','week1','week2','week2','week3','week3'),day_no=c('day1','day2','day1','day2','day3','day3'),val=c(234,123,235,111,300,400),stringsAsFactors=F);
df;
##   raw_id week_no day_no val
## 1      1   week1   day1 234
## 2      2   week1   day2 123
## 3      3   week2   day1 235
## 4      4   week2   day2 111
## 5      5   week3   day3 300
## 6      6   week3   day3 400
res <- df[t(do.call(rbind,apply(combn(seq_along(unique(df$week_no)),2L),2L,function(is,rs) expand.grid(rs[[is[1L]]],rs[[is[2L]]]),split(seq_len(nrow(df)),df$week_no)))),];
res$group <- rep(seq_len(nrow(res)%/%2L),each=2L);
res;
##     raw_id week_no day_no val group
## 1        1   week1   day1 234     1
## 3        3   week2   day1 235     1
## 2        2   week1   day2 123     2
## 3.1      3   week2   day1 235     2
## 1.1      1   week1   day1 234     3
## 4        4   week2   day2 111     3
## 2.1      2   week1   day2 123     4
## 4.1      4   week2   day2 111     4
## 1.2      1   week1   day1 234     5
## 5        5   week3   day3 300     5
## 2.2      2   week1   day2 123     6
## 5.1      5   week3   day3 300     6
## 1.3      1   week1   day1 234     7
## 6        6   week3   day3 400     7
## 2.3      2   week1   day2 123     8
## 6.1      6   week3   day3 400     8
## 3.2      3   week2   day1 235     9
## 5.2      5   week3   day3 300     9
## 4.2      4   week2   day2 111    10
## 5.3      5   week3   day3 300    10
## 3.3      3   week2   day1 235    11
## 6.2      6   week3   day3 400    11
## 4.3      4   week2   day2 111    12
## 6.3      6   week3   day3 400    12

我们可以将上述结果与我在第一个解决方案中得到的结果进行比较:

res <- df[t(do.call(expand.grid,split(seq_len(nrow(df)),df$week_no))),];
res$group <- rep(seq_len(prod(table(df$week_no))),each=length(unique(df$week_no)));
res;
##     raw_id week_no day_no val group
## 1        1   week1   day1 234     1
## 3        3   week2   day1 235     1
## 5        5   week3   day3 300     1
## 2        2   week1   day2 123     2
## 3.1      3   week2   day1 235     2
## 5.1      5   week3   day3 300     2
## 1.1      1   week1   day1 234     3
## 4        4   week2   day2 111     3
## 5.2      5   week3   day3 300     3
## 2.1      2   week1   day2 123     4
## 4.1      4   week2   day2 111     4
## 5.3      5   week3   day3 300     4
## 1.2      1   week1   day1 234     5
## 3.2      3   week2   day1 235     5
## 6        6   week3   day3 400     5
## 2.2      2   week1   day2 123     6
## 3.3      3   week2   day1 235     6
## 6.1      6   week3   day3 400     6
## 1.3      1   week1   day1 234     7
## 4.2      4   week2   day2 111     7
## 6.2      6   week3   day3 400     7
## 2.3      2   week1   day2 123     8
## 4.3      4   week2   day2 111     8
## 6.3      6   week3   day3 400     8

答案 1 :(得分:1)

1。您没有按两组创建所有排列

仔细观察,重复排列(前四行与最后四行相同)

expand.grid将为您提供两个向量的所有组合,而无需“分组”。

您的代码的更正版本将是:

编辑:

b<-rbind(expand.grid(week = unique(w),day = unique(d)),expand.grid(day = d,week = w))

2.添加新列

b$group<-rep(1:4,each = 2)
w<-sapply(1:nrow(b),FUN = function(z){
        which(book2$week_no==b$week[z] & book2$day_no==b$day[z])
       }
    )
b$pre_raw_id<-w
b$val<-book2$val[w]