R有条件地向前和向后结转
R carry forward and backward conditionally
我有一组变量,其中包含关于一个人是否 曾经 有某些健康状况的数据。例如,"have you ever had a heart attack?"
如果他们在观察 2 中说 "yes",那么在观察 3 和 4 中答案仍然是肯定的。但是,在观察 1 中不一定是肯定的。心脏病发作可能发生在观察 1 和观察之间2.
如果他们在观察 2 中说 "no",那么在观察 1 中答案是否定的。但是,在观察 3 或 4 中不一定是否。
这是一个可重现的例子:
df <- tibble(
id = rep(1:3, each = 4),
obs = rep(1:4, times = 3),
mi_ever = c(NA, 0, 1, NA, NA, 0, NA, NA, NA, 1, NA, NA)
)
df
id obs mi_ever
1 1 1 NA
2 1 2 0
3 1 3 1
4 1 4 NA
5 2 1 NA
6 2 2 0
7 2 3 NA
8 2 4 NA
9 3 1 NA
10 3 2 1
11 3 3 NA
12 3 4 NA
使用 zoo::na.locf 将我的 0(否)向后传送 或 将我的 1(是)向前传送是微不足道的。但是,我不确定如何将 0 向后 和 1 向前。理想情况下,我想要以下结果:
id obs mi_ever mi_ever_2
1 1 1 NA 0
2 1 2 0 0
3 1 3 1 1
4 1 4 NA 1
5 2 1 NA 0
6 2 2 0 0
7 2 3 NA NA
8 2 4 NA NA
9 3 1 NA NA
10 3 2 1 1
11 3 3 NA 1
12 3 4 NA 1
我查看了以下帖子,但 none 似乎完全涵盖了我在这里要问的内容。
making a "dropdown" function in R
感谢任何帮助。
基本上我是按顺序标记第一个 1 之后的项目成为 1,最后一个 0 之前的项目成为 0。
ever <- function (x) min( which( x == 1))
NA_1 <- function(x) seq_along(x) > ever(x) #could have done in one function
# check to see if working
ave(df$mi_ever, df$id, FUN= function(x){ x[NA_1(x) ] <- 1; x})
[1] NA 0 1 1 NA 0 NA NA NA 1 1 1
NA_0 <- function(x) seq_along(x) < not_yet(x)
not_yet <- function(x){ max( which( x==0)) }
# make temporary version of 1-modified column
temp1 <- ave(df$mi_ever, df$id, FUN= function(x){ x[NA_1(x) ] <- 1; x})
df$ever2 <- ave(temp1, df$id, FUN= function(x){ x[NA_0(x) ] <- 0; x})
# then make final version; could have done it "in place" I suppose.
df
# A tibble: 12 x 4
id obs mi_ever ever2
<int> <int> <dbl> <dbl>
1 1 1 NA 0
2 1 2 0 0
3 1 3 1 1
4 1 4 NA 1
5 2 1 NA 0
6 2 2 0 0
7 2 3 NA NA
8 2 4 NA NA
9 3 1 NA NA
10 3 2 1 1
11 3 3 NA 1
12 3 4 NA 1
如果你需要抑制应该可以的警告。
我从上面的@42- 那里得到了答案(谢谢!),并对其进行了一些调整以进一步满足我的需求。具体来说,我:
- 注意警告 "no non-missing arguments to min; returning Infno non-missing arguments to max; returning -Inf"。
- 将单独的函数合并为一个函数(尽管单独的函数对学习非常有用)。
- 添加了一个可选的
check_logic
参数。当 TRUE 时,如果 0 出现在 1 之后,函数将 return 9。这表示需要进一步调查的数据错误或逻辑缺陷。
- 添加了一个将函数与 data.table 一起使用的示例,并且一次用于多个变量。这更准确地代表了我在现实生活中如何使用该功能,我认为它可能对其他人有用。
函数:
distribute_ever <- function(x, check_logic = TRUE, ...) {
if (check_logic) {
if (length(which(x == 1)) > 0 & length(which(x == 0)) > 0) {
if (min(which(x == 1)) < max(which(x == 0))) {
x <- 9 # Set x to 9 if zero comes after 1
}
}
}
ones <- which(x == 1) # Get indices for 1's
if (length(ones) > 0) { # Prevents warning
first_1_by_group <- min(which(x == 1)) # Index first 1 by group
x[seq_along(x) > first_1_by_group] <- 1 # Set x at subsequent indices to 1
}
zeros <- which(x == 0) # Get indices for 0's
if (length(zeros) > 0) { # Prevents warning
last_0_by_group <- max(which(x == 0)) # Index last 0 by group
x[seq_along(x) < last_0_by_group] <- 0 # Set x at previous indices to 0
}
x
}
具有多个 "ever" 变量的新的可重现示例和一些在 1 之后有 0 的情况:
dt <- data.table(
id = rep(1:3, each = 4),
obs = rep(1:4, times = 3),
mi_ever = c(NA, 0, 1, NA, NA, 0, NA, NA, NA, 1, NA, NA),
diab_ever = c(0, NA, NA, 1, 1, NA, NA, 0, 0, NA, NA, NA)
)
使用data.table(按组处理)快速迭代多个变量:
ever_vars <- c("mi_ever", "diab_ever")
dt[, paste0(ever_vars, "_2") := lapply(.SD, distribute_ever),
.SDcols = ever_vars,
by = id][]
结果:
id obs mi_ever diab_ever mi_ever_2 diab_ever_2
1: 1 1 NA 0 0 0
2: 1 2 0 NA 0 NA
3: 1 3 1 NA 1 NA
4: 1 4 NA 1 1 1
5: 2 1 NA 1 0 9
6: 2 2 0 NA 0 9
7: 2 3 NA NA NA 9
8: 2 4 NA 0 NA 9
9: 3 1 NA 0 NA 0
10: 3 2 1 NA 1 NA
11: 3 3 NA NA 1 NA
12: 3 4 NA NA 1 NA
对于每个输入 "ever" 变量,我们有:
- 创建了一个新变量,在输入变量名称的末尾附加了“_2”。您也可以按照 42- 指出的那样编辑 "in place",但我喜欢能够仔细检查我的数据。
- 0 向后进位,1 向后进位。
- NA 在零之后和一之前(在 id 内)returned 不变。
- 如果在 1(是的,我有过...)之后有 0(没有,我从来没有...),就像第 2 个人对糖尿病的反应一样,那么函数 returns 9.
- 如果我们将
check_logic
设置为 FALSE,则 1 将胜出并取代 0
我有一组变量,其中包含关于一个人是否 曾经 有某些健康状况的数据。例如,"have you ever had a heart attack?"
如果他们在观察 2 中说 "yes",那么在观察 3 和 4 中答案仍然是肯定的。但是,在观察 1 中不一定是肯定的。心脏病发作可能发生在观察 1 和观察之间2.
如果他们在观察 2 中说 "no",那么在观察 1 中答案是否定的。但是,在观察 3 或 4 中不一定是否。
这是一个可重现的例子:
df <- tibble(
id = rep(1:3, each = 4),
obs = rep(1:4, times = 3),
mi_ever = c(NA, 0, 1, NA, NA, 0, NA, NA, NA, 1, NA, NA)
)
df
id obs mi_ever
1 1 1 NA
2 1 2 0
3 1 3 1
4 1 4 NA
5 2 1 NA
6 2 2 0
7 2 3 NA
8 2 4 NA
9 3 1 NA
10 3 2 1
11 3 3 NA
12 3 4 NA
使用 zoo::na.locf 将我的 0(否)向后传送 或 将我的 1(是)向前传送是微不足道的。但是,我不确定如何将 0 向后 和 1 向前。理想情况下,我想要以下结果:
id obs mi_ever mi_ever_2
1 1 1 NA 0
2 1 2 0 0
3 1 3 1 1
4 1 4 NA 1
5 2 1 NA 0
6 2 2 0 0
7 2 3 NA NA
8 2 4 NA NA
9 3 1 NA NA
10 3 2 1 1
11 3 3 NA 1
12 3 4 NA 1
我查看了以下帖子,但 none 似乎完全涵盖了我在这里要问的内容。
making a "dropdown" function in R
感谢任何帮助。
基本上我是按顺序标记第一个 1 之后的项目成为 1,最后一个 0 之前的项目成为 0。
ever <- function (x) min( which( x == 1))
NA_1 <- function(x) seq_along(x) > ever(x) #could have done in one function
# check to see if working
ave(df$mi_ever, df$id, FUN= function(x){ x[NA_1(x) ] <- 1; x})
[1] NA 0 1 1 NA 0 NA NA NA 1 1 1
NA_0 <- function(x) seq_along(x) < not_yet(x)
not_yet <- function(x){ max( which( x==0)) }
# make temporary version of 1-modified column
temp1 <- ave(df$mi_ever, df$id, FUN= function(x){ x[NA_1(x) ] <- 1; x})
df$ever2 <- ave(temp1, df$id, FUN= function(x){ x[NA_0(x) ] <- 0; x})
# then make final version; could have done it "in place" I suppose.
df
# A tibble: 12 x 4
id obs mi_ever ever2
<int> <int> <dbl> <dbl>
1 1 1 NA 0
2 1 2 0 0
3 1 3 1 1
4 1 4 NA 1
5 2 1 NA 0
6 2 2 0 0
7 2 3 NA NA
8 2 4 NA NA
9 3 1 NA NA
10 3 2 1 1
11 3 3 NA 1
12 3 4 NA 1
如果你需要抑制应该可以的警告。
我从上面的@42- 那里得到了答案(谢谢!),并对其进行了一些调整以进一步满足我的需求。具体来说,我:
- 注意警告 "no non-missing arguments to min; returning Infno non-missing arguments to max; returning -Inf"。
- 将单独的函数合并为一个函数(尽管单独的函数对学习非常有用)。
- 添加了一个可选的
check_logic
参数。当 TRUE 时,如果 0 出现在 1 之后,函数将 return 9。这表示需要进一步调查的数据错误或逻辑缺陷。 - 添加了一个将函数与 data.table 一起使用的示例,并且一次用于多个变量。这更准确地代表了我在现实生活中如何使用该功能,我认为它可能对其他人有用。
函数:
distribute_ever <- function(x, check_logic = TRUE, ...) {
if (check_logic) {
if (length(which(x == 1)) > 0 & length(which(x == 0)) > 0) {
if (min(which(x == 1)) < max(which(x == 0))) {
x <- 9 # Set x to 9 if zero comes after 1
}
}
}
ones <- which(x == 1) # Get indices for 1's
if (length(ones) > 0) { # Prevents warning
first_1_by_group <- min(which(x == 1)) # Index first 1 by group
x[seq_along(x) > first_1_by_group] <- 1 # Set x at subsequent indices to 1
}
zeros <- which(x == 0) # Get indices for 0's
if (length(zeros) > 0) { # Prevents warning
last_0_by_group <- max(which(x == 0)) # Index last 0 by group
x[seq_along(x) < last_0_by_group] <- 0 # Set x at previous indices to 0
}
x
}
具有多个 "ever" 变量的新的可重现示例和一些在 1 之后有 0 的情况:
dt <- data.table(
id = rep(1:3, each = 4),
obs = rep(1:4, times = 3),
mi_ever = c(NA, 0, 1, NA, NA, 0, NA, NA, NA, 1, NA, NA),
diab_ever = c(0, NA, NA, 1, 1, NA, NA, 0, 0, NA, NA, NA)
)
使用data.table(按组处理)快速迭代多个变量:
ever_vars <- c("mi_ever", "diab_ever")
dt[, paste0(ever_vars, "_2") := lapply(.SD, distribute_ever),
.SDcols = ever_vars,
by = id][]
结果:
id obs mi_ever diab_ever mi_ever_2 diab_ever_2
1: 1 1 NA 0 0 0
2: 1 2 0 NA 0 NA
3: 1 3 1 NA 1 NA
4: 1 4 NA 1 1 1
5: 2 1 NA 1 0 9
6: 2 2 0 NA 0 9
7: 2 3 NA NA NA 9
8: 2 4 NA 0 NA 9
9: 3 1 NA 0 NA 0
10: 3 2 1 NA 1 NA
11: 3 3 NA NA 1 NA
12: 3 4 NA NA 1 NA
对于每个输入 "ever" 变量,我们有:
- 创建了一个新变量,在输入变量名称的末尾附加了“_2”。您也可以按照 42- 指出的那样编辑 "in place",但我喜欢能够仔细检查我的数据。
- 0 向后进位,1 向后进位。
- NA 在零之后和一之前(在 id 内)returned 不变。
- 如果在 1(是的,我有过...)之后有 0(没有,我从来没有...),就像第 2 个人对糖尿病的反应一样,那么函数 returns 9.
- 如果我们将
check_logic
设置为 FALSE,则 1 将胜出并取代 0