r 中的惩罚累积和

Penalized cumulative sum in r

我需要计算一个惩罚累计和

个人“A”、“B”和“C”应该每隔一年接受一次测试。每次测试,他们都会累积 1 分。但是,当他们错过考试时,他们的累计分数将被扣除 1。

我有以下代码:

data.frame(year = rep(1990:1995, 3), person.id = c(rep("A", 6), rep("B", 6), rep("C", 6)),   needs.testing = rep(c("Yes", "No"), 9), test.compliance = c(c(1,0,1,0,1,0), c(1,0,1,0,0,0), c(1,0,0,0,0,0)), penalized.compliance.cum.sum = c(c(1,1,2,2,3,3), c(1,1,2,2,1,1), c(1,1,0,0,-1,-1)))

...给出以下内容:

  year person.id needs.testing test.compliance penalized.compliance.cum.sum
1  1990         A           Yes               1                            1
2  1991         A            No               0                            1
3  1992         A           Yes               1                            2
4  1993         A            No               0                            2
5  1994         A           Yes               1                            3
6  1995         A            No               0                            3
7  1990         B           Yes               1                            1
8  1991         B            No               0                            1
9  1992         B           Yes               1                            2
10 1993         B            No               0                            2
11 1994         B           Yes               0                            1
12 1995         B            No               0                            1
13 1990         C           Yes               1                            1
14 1991         C            No               0                            1
15 1992         C           Yes               0                            0
16 1993         C            No               0                            0
17 1994         C           Yes               0                           -1
18 1995         C            No               0                           -1

很明显,“A”完全符合要求。 “B”有点遵守(1994年他应该接受测试,但他错过了测试,因此他的累计总和从2减为1)。最后,“C”只符合一次(1990年,每次她需要测试时,她都错过了测试)。

我需要一些代码来获取“penalized.compliance.cum.sum”变量。

请注意:

  1. 每隔一年测试一次。
  2. “penalized.compliance.cum.sum”变量不断添加之前的分数。
  3. 但仅当个人在测试年份错过测试时才开始扣除(在“needs.testing”变量中表示)。

谢谢!

基础 R

do.call(rbind, by(dat, dat$person.id,
                  function(z) transform(z, res = cumsum(ifelse(needs.testing == "Yes", 1-2*(test.compliance < 1), 0))) 
))
#      year person.id needs.testing test.compliance penalized.compliance.cum.sum res
# A.1  1990         A           Yes               1                            1   1
# A.2  1991         A            No               0                            1   1
# A.3  1992         A           Yes               1                            2   2
# A.4  1993         A            No               0                            2   2
# A.5  1994         A           Yes               1                            3   3
# A.6  1995         A            No               0                            3   3
# B.7  1990         B           Yes               1                            1   1
# B.8  1991         B            No               0                            1   1
# B.9  1992         B           Yes               1                            2   2
# B.10 1993         B            No               0                            2   2
# B.11 1994         B           Yes               0                            1   1
# B.12 1995         B            No               0                            1   1
# C.13 1990         C           Yes               1                            1   1
# C.14 1991         C            No               0                            1   1
# C.15 1992         C           Yes               0                            0   0
# C.16 1993         C            No               0                            0   0
# C.17 1994         C           Yes               0                           -1  -1
# C.18 1995         C            No               0                           -1  -1

by 通过 INDICES(此处为 dat$person.id)拆分一个帧,其中在函数中 z 只是该组的数据。这使我们可以对数据进行操作,而不必担心向量中的人会发生变化。

by returns a list,将列表组合成一个框架的规范 base-R 方法是 rbind(a, b) 当只有两个框架时,或者 do.call(rbind, list(...)) 当列表中可能有两个以上的框架时。

1-2*(.) 只是基于 test.compliance.

在 +1 和 -1 之间胡扯的把戏

这有可能改变行顺序的副作用。例如,如果先按 year 然后按 person.id 排序,则 by 组计算仍然有效,但输出将按 person.id 分组(并且由组内 year 订购)。未成年人,但如果您需要秩序,请注意。

dplyr

library(dplyr)
dat %>%
  group_by(person.id) %>%
  mutate(res = cumsum(if_else(needs.testing == "Yes", 1-2*(test.compliance < 1), 0))) %>%
  ungroup()

data.table

library(data.table)
datDT <- as.data.table(dat)
datDT[, res := cumsum(fifelse(needs.testing == "Yes", 1-2*(test.compliance < 1), 0)), by = .(person.id)]

这对你有用吗?

df <- data.frame(year = rep(1990:1995, 3), person.id = c(rep("A", 6), rep("B", 6), rep("C", 6)),   needs.testing = rep(c("Yes", "No"), 9), test.compliance = c(c(1,0,1,0,1,0), c(1,0,1,0,0,0), c(1,0,0,0,0,0)), penalized.compliance.cum.sum = c(c(1,1,2,2,3,3), c(1,1,2,2,1,1), c(1,1,0,0,-1,-1)))

library("dplyr")

penalty <- -1
df %>% 
  group_by(person.id) %>% 
  mutate(cumsum = cumsum(ifelse(needs.testing == "Yes" & test.compliance == 0, penalty, test.compliance)))
## A tibble: 18 x 6
## Groups:   person.id [3]
#    year person.id needs.testing test.compliance penalized.compliance.cum.sum cumsum
#   <int> <chr>     <chr>                   <dbl>                        <dbl>  <dbl>
# 1  1990 A         Yes                         1                            1      1
# 2  1991 A         No                          0                            1      1
# 3  1992 A         Yes                         1                            2      2
# 4  1993 A         No                          0                            2      2
# 5  1994 A         Yes                         1                            3      3
# 6  1995 A         No                          0                            3      3
# 7  1990 B         Yes                         1                            1      1
# 8  1991 B         No                          0                            1      1
# 9  1992 B         Yes                         1                            2      2
#10  1993 B         No                          0                            2      2
#11  1994 B         Yes                         0                            1      1
#12  1995 B         No                          0                            1      1
#13  1990 C         Yes                         1                            1      1
#14  1991 C         No                          0                            1      1
#15  1992 C         Yes                         0                            0      0
#16  1993 C         No                          0                            0      0
#17  1994 C         Yes                         0                           -1     -1
#18  1995 C         No                          0                           -1     -1

然后您可以轻松地将 penalty 变量调整为您想要的任何惩罚。

使用case_when

library(dplyr)
df1 %>%
   group_by(person.id) %>%
   mutate(res = cumsum(case_when(needs.testing == "Yes" ~ 1- 2 *(test.compliance < 1), TRUE ~ 0)))