Создание групп на основе промежуточных итогов по значению - PullRequest
3 голосов
/ 24 июня 2019

У меня есть данные, которые уникальны для одной переменной Y. Другая переменная Z говорит мне, сколько человек в каждом из Y. Моя проблема в том, что я хочу создать группы из 45 из этих Y и Z. Я имею в виду, что всякий раз, когдаобщее количество 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 не относится к категории A и B?

Ответы [ 5 ]

4 голосов
/ 24 июня 2019

Функция для этого доступна в 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")
3 голосов
/ 24 июня 2019

Определите Accum, который добавляет x к acc, сбрасывая к x, если acc равно 45 или более. Используйте Reduce, чтобы применить это к Z, давая r (который является столбцом кумулятивной суммы). Значения, большие или равные 45, являются концами группы, поэтому присвойте им уникальный идентификатор группы в g, используя cumsum, начиная с конца и возвращаясь назад к началу, давая g, который имеет уникальные значения для каждого группа. Наконец, измените идентификаторы групп в g так, чтобы они начинались с 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 голосов
/ 24 июня 2019

Одна 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
1 голос
/ 24 июня 2019

Не симпатичное решение, но функциональное.

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
0 голосов
/ 27 июня 2019

Или используя 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")
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...