我有如下二进制数据:
ID <- c("A", "B", "C", "D", "E", "F")
Q0 <- c(0, 0, 0, 0, 0, 0)
Q1 <- c(0, 1, 0, 0, NA, 1)
Q2 <- c(0, NA, 1, 0, NA, 1)
Q3 <- c(0, NA, NA, 1, NA, 1)
Q4 <- c(0, NA, NA, 1, NA, 1)
dta <- data.frame(ID, Q0, Q1, Q2, Q3, Q4)
Run Code Online (Sandbox Code Playgroud)
如果其中一列中的行为 1,则所有后续列也应为 1。如果有 0 或 NA,则下一列应保持原样。
换句话说,如何根据相对位置中列的值有条件地更改多个列的值?
上述数据框的预期输出是:
ID Q0 Q1 Q2 Q3 Q4
A 0 0 0 0 0
B 0 1 1 1 1
C 0 0 1 1 1
D 0 0 0 1 1
E 0 NA NA NA NA
F 0 1 1 1 1
Run Code Online (Sandbox Code Playgroud)
我怎样才能做到这一点?也许使用apply或for循环?
另一个dplyr+purrr选项可能是:
dta %>%
mutate(pmap_dfr(across(-ID), ~ `[<-`(c(...), seq_along(c(...)) > match(1, c(...)), 1)))
ID Q0 Q1 Q2 Q3 Q4
1 A 0 0 0 0 0
2 B 0 1 1 1 1
3 C 0 0 1 1 1
4 D 0 0 0 1 1
5 E 0 NA NA NA NA
6 F 0 1 1 1 1
Run Code Online (Sandbox Code Playgroud)
用循环让事情变得简单:
for (i in 3:ncol(dta)) dta[[i]][dta[[i-1]] == 1] <- 1
Run Code Online (Sandbox Code Playgroud)
# ID Q0 Q1 Q2 Q3 Q4
# 1 A 0 0 0 0 0
# 2 B 0 1 1 1 1
# 3 C 0 0 1 1 1
# 4 D 0 0 0 1 1
# 5 E 0 NA NA NA NA
# 6 F 0 1 1 1 1
Run Code Online (Sandbox Code Playgroud)
dplyr+受到data.table尤里 (Yuriy) 的启发:
library(dplyr)
library(data.table)
setDT(dta)
dta[, (names(dta)[-1]) := as.list(cumany(.SD == 1)), by = ID]
Run Code Online (Sandbox Code Playgroud)
一个选项na.locf
library(zoo)
i1 <- do.call(pmax, c(dta[-1], na.rm = TRUE))!= 0
dta[-1][i1,] <- t(na.locf(as.data.frame(t(dta[-1][i1,]))))
Run Code Online (Sandbox Code Playgroud)
-输出
> dta
ID Q0 Q1 Q2 Q3 Q4
1 A 0 0 0 0 0
2 B 0 1 1 1 1
3 C 0 0 1 1 1
4 D 0 0 0 1 1
5 E 0 NA NA NA NA
6 F 0 1 1 1 1
Run Code Online (Sandbox Code Playgroud)
我又发现了一个带有旋转功能的:
library(tidyr)
library(dplyr)
dta %>%
pivot_longer(-ID) %>%
group_by(ID) %>%
mutate(value2 = value) %>%
fill(value2) %>%
mutate(value = ifelse(value2 == 0, value, value2)) %>%
select(-value2) %>%
pivot_wider(names_from = name, values_from = value)
Run Code Online (Sandbox Code Playgroud)
ID Q0 Q1 Q2 Q3 Q4
<chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 A 0 0 0 0 0
2 B 0 1 1 1 1
3 C 0 0 1 1 1
4 D 0 0 0 1 1
5 E 0 NA NA NA NA
6 F 0 1 1 1 1
Run Code Online (Sandbox Code Playgroud)
您可以在同一个中创建所有变量mutate
dta %>%
mutate(
Q2 = case_when(Q1 == 1 ~ 1, TRUE ~ Q2),
Q3 = case_when(Q2 == 1 ~ 1, TRUE ~ Q3),
Q4 = case_when(Q3 == 1 ~ 1, TRUE ~ Q4))
ID Q0 Q1 Q2 Q3 Q4
1 A 0 0 0 0 0
2 B 0 1 1 1 1
3 C 0 0 1 1 1
4 D 0 0 0 1 1
5 E 0 NA NA NA NA
6 F 0 1 1 1 1
Run Code Online (Sandbox Code Playgroud)
但我不知道是否可以以更编程的方式做到这一点
另一种可能的解决方案:
library(dplyr)
dta %>%
mutate(t(apply(.[-1], 1, \(x) {if (max(x, na.rm = T) == 1)
x[which.max(x):length(x)] <- 1 else x; x})) %>% as_tibble)
#> ID Q0 Q1 Q2 Q3 Q4
#> 1 A 0 0 0 0 0
#> 2 B 0 1 1 1 1
#> 3 C 0 0 1 1 1
#> 4 D 0 0 0 1 1
#> 5 E 0 NA NA NA NA
#> 6 F 0 1 1 1 1
Run Code Online (Sandbox Code Playgroud)
ID <- c("A", "B", "C", "D", "E", "F")
Q0 <- c(0, 0, 0, 0, 0, 0)
Q1 <- c(0, 1, 0, 0, NA, 1)
Q2 <- c(0, NA, 1, 0, NA, 1)
Q3 <- c(0, NA, NA, 1, NA, 1)
Q4 <- c(0, NA, NA, 1, NA, 1)
df <- data.frame(ID, Q0, Q1, Q2, Q3, Q4)
df[-1] <- t(apply(df[-1], 1, function(x) +(dplyr::cumany(x == 1))))
df
#> ID Q0 Q1 Q2 Q3 Q4
#> 1 A 0 0 0 0 0
#> 2 B 0 1 1 1 1
#> 3 C 0 0 1 1 1
#> 4 D 0 0 0 1 1
#> 5 E 0 NA NA NA NA
#> 6 F 0 1 1 1 1
Run Code Online (Sandbox Code Playgroud)
由reprex 包于 2022 年 7 月 4 日创建(v2.0.1)
| 归档时间: |
|
| 查看次数: |
3267 次 |
| 最近记录: |