根据某个值的累计总数创建组

时间:2019-06-24 12:20:24

标签: r

我在一个变量Y处具有唯一的数据。另一个变量Z告诉我每个Y中有多少人。我的问题是,我想从这些Y和Z中创建45人一组。我的意思是,每当总共进行了Z次触摸45次,进行了一组操作,然后代码继续创建下一组。

我的数据看起来像这样

ID  X   Y   Z
1   A   A   1
2   A   B   5
3   A   C   2
4   A   D   42
5   A   E   10
6   A   F   2
7   A   G   0
8   A   H   3
9   A   I   0
10  A   J   8
11  A   K   19
12  A   L   3
13  A   M   1
14  A   N   1
15  A   O   2
16  A   P   0
17  A   Q   1
18  A   R   2

想要的就是这样

ID  X   Y   Z   CumSum  Group
1   A   A   1   1   1
2   A   B   5   6   1
3   A   C   2   8   1
4   A   D   42  50  1
5   A   E   10  10  2
6   A   F   2   12  2
7   A   G   0   12  2
8   A   H   3   15  2
9   A   I   0   15  2
10  A   J   8   23  2
11  A   K   19  42  2
12  A   L   3   45  2
13  A   M   1   1   3
14  A   N   1   2   3
15  A   O   2   4   3   
16  A   P   0   4   3
17  A   Q   1   5   3
18  A   R   2   7   3

请让我知道如何使用R实现这一目标。

编辑:为了更清晰起见,我扩展了最小可重现的示例

编辑2:关于这个主题,我还有一个问题。如果现在仅X的变量A也在变化。例如,它可以B一段时间,然后可以变成C。如何防止代码生成不在X的两个类别中的组。例如,如果Group = 3,那么如何确保3不在类别AB中?

5 个答案:

答案 0 :(得分:4)

在MESS软件包中可以使用此功能...

library(MESS)
library(data.table)

DT[, Group := MESS::cumsumbinning(Z, 50)][, Cumsum := cumsum(Z), by = .(Group)][]

输出

    ID X Y  Z Group Cumsum
 1:  1 A A  1     1      1
 2:  2 A B  5     1      6
 3:  3 A C  2     1      8
 4:  4 A D 42     1     50
 5:  5 A E 10     2     10
 6:  6 A F  2     2     12
 7:  7 A G  0     2     12
 8:  8 A H  3     2     15
 9:  9 A I  0     2     15
10: 10 A J  8     2     23
11: 11 A K 19     2     42
12: 12 A L  3     2     45

样本数据

DT <- fread("ID  X   Y   Z
            1   A   A   1
            2   A   B   5
            3   A   C   2
            4   A   D   42
            5   A   E   10
            6   A   F   2
            7   A   G   0
            8   A   H   3
            9   A   I   0
            10  A   J   8
            11  A   K   19
            12  A   L   3")

答案 1 :(得分:3)

定义Accum,如果x为45或更大,则将acc添加到x并重置为acc。使用Reduce将其应用于Z,得到r(这是累积总和列)。大于或等于45的值是组结尾,因此通过使用g从结尾开始并向后退到开头,在cumsum中将唯一的组ID附加到它们上,得到g每个组都有唯一的值。最后,修改g中的组ID,使它们从1开始。我们在末尾的“注释”中输入该内容,该行重复最后一行几次,从而可以显示3个组。不使用任何软件包。

Accum <- function(acc, x) if (acc < 45)  acc + x else x
applyAccum <- function(x) Reduce(Accum, x, accumulate = TRUE)
cumsumr <- function(x) rev(cumsum(rev(x))) # reverse cumsum
GroupNo <- function(x) {
  y <- cumsumr(x >= 45)
  max(y) - y + 1
}
transform(transform(DF, Cumsum = ave(Z, ID, FUN = applyAccum)), 
  Group = ave(Cumsum, ID, FUN = GroupNo))

给予:

   ID X Y  Z Cumsum Group
1   1 A A  1      1     1
2   2 A B  5      6     1
3   3 A C  2      8     1
4   4 A D 42     50     1
5   5 A E 10     10     2
6   6 A F  2     12     2
7   7 A G  0     12     2
8   8 A H  3     15     2
9   9 A I  0     15     2
10 10 A J  8     23     2
11 11 A K 19     42     2
12 12 A L  3     45     2
13 12 A L  3      3     3
14 12 A L  3      6     3

注意

可复制形式的输入:

Lines <- "ID  X   Y   Z
1   A   A   1
2   A   B   5
3   A   C   2
4   A   D   42
5   A   E   10
6   A   F   2
7   A   G   0
8   A   H   3
9   A   I   0
10  A   J   8
11  A   K   19
12  A   L   3
12  A   L   3
12  A   L   3"
DF <- read.table(text = Lines, as.is = TRUE, header = TRUE)

答案 2 :(得分:2)

一种tidyverse可能是:

df %>% 
 mutate(Cumsum = accumulate(Z, ~ if_else(.x >= 45, .y, .x + .y)),
        Group = cumsum(Cumsum >= 45),
        Group = if_else(Group > lag(Group, default = first(Group)), lag(Group), Group) + 1)

   ID X Y  Z Cumsum Group
1   1 A A  1      1     1
2   2 A B  5      6     1
3   3 A C  2      8     1
4   4 A D 42     50     1
5   5 A E 10     10     2
6   6 A F  2     12     2
7   7 A G  0     12     2
8   8 A H  3     15     2
9   9 A I  0     15     2
10 10 A J  8     23     2
11 11 A K 19     42     2
12 12 A L  3     45     2

答案 3 :(得分:1)

不是一个很好的解决方案,但是可以起作用。

df$Group<-0
group<-1
while (df$Group[nrow(df)]==0) {
  df$ww[df$Group==0]<-cumsum(df$Z[df$Group==0])
  df$Group[df$Group==0 & (lag(df$ww)<=45 | is.na(lag(df$ww)) | lag(df$Group!=0))]<-group
  group=group+1
}

df
   ID X Y  Z ww Group
1   1 A A  1  1  1
2   2 A B  5  6  1
3   3 A C  2  8  1
4   4 A D 42 50  1
5   5 A E 10 10  2
6   6 A F  2 12  2
7   7 A G  0 12  2
8   8 A H  3 15  2
9   9 A I  0 15  2
10 10 A J  8 23  2
11 11 A K 19 42  2
12 12 A L  3 45  2

好的,是的,@ tmfmnk的解决方案好得多:

Unit: milliseconds
 expr       min        lq     mean    median        uq      max neval
   tm  2.224536  2.805771  6.76661  3.221449  3.990778 303.7623   100
  iod 19.198391 22.294222 30.17730 25.765792 35.768616 110.2062   100

答案 4 :(得分:0)

或使用data.table

library(data.table)
n <- 45L
DT[, cs := Reduce(function(tot, z) if (tot+z > n) z else tot+z, Z, accumulate=TRUE)][, 
    Group := .GRP, by=cumsum(c(1L, diff(cs))<0L)]

输出:

    ID X Y  Z cs Group
 1:  1 A A  1  1     1
 2:  2 A B  5  6     1
 3:  3 A C  2  8     1
 4:  4 A D 42 42     1
 5:  5 A E 10 10     2
 6:  6 A F  2 12     2
 7:  7 A G  0 12     2
 8:  8 A H  3 15     2
 9:  9 A I  0 15     2
10: 10 A J  8 23     2
11: 11 A K 19 42     2
12: 12 A L  3 45     2
13: 13 A M  1  1     3
14: 14 A N  1  2     3
15: 15 A O  2  4     3
16: 16 A P  0  4     3
17: 17 A Q  1  5     3
18: 18 A R  2  7     3

数据:

library(data.table)
DT <- fread("ID  X   Y   Z
1   A   A   1
2   A   B   5
3   A   C   2
4   A   D   42
5   A   E   10
6   A   F   2
7   A   G   0
8   A   H   3
9   A   I   0
10  A   J   8
11  A   K   19
12  A   L   3
13  A   M   1
14  A   N   1
15  A   O   2
16  A   P   0
17  A   Q   1
18  A   R   2")