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”变量。
请注意:
- 每隔一年测试一次。
- “penalized.compliance.cum.sum”变量不断添加之前的分数。
- 但仅当个人在测试年份错过测试时才开始扣除(在“needs.testing”变量中表示)。
- 例如,个人“C”在 1990 年符合要求。在 1991 年她不需要接受测试,因此保持她的分数为 1。然后,她错过了 1992 年的测试,从她的成绩中减去 1累积分数,1992 年得 0 分。然后她一直缺考,在研究结束时得了 -1。
- 此外,我需要分配不同的惩罚(即不同的数字)。在此示例中,它只是 1。但是,我需要能够使用其他数字(例如 0.5、0.1 等)进行惩罚。
谢谢!
基础 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)))
我需要计算一个惩罚累计和
个人“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”变量。
请注意:
- 每隔一年测试一次。
- “penalized.compliance.cum.sum”变量不断添加之前的分数。
- 但仅当个人在测试年份错过测试时才开始扣除(在“needs.testing”变量中表示)。
- 例如,个人“C”在 1990 年符合要求。在 1991 年她不需要接受测试,因此保持她的分数为 1。然后,她错过了 1992 年的测试,从她的成绩中减去 1累积分数,1992 年得 0 分。然后她一直缺考,在研究结束时得了 -1。
- 此外,我需要分配不同的惩罚(即不同的数字)。在此示例中,它只是 1。但是,我需要能够使用其他数字(例如 0.5、0.1 等)进行惩罚。
谢谢!
基础 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
.
这有可能改变行顺序的副作用。例如,如果先按 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)))