将值从一列复制到给定条件的下一列

nat*_*tys 17 r

我有如下二进制数据:

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)

我怎样才能做到这一点?也许使用applyfor循环?

tmf*_*mnk 9

另一个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)


sin*_*dur 9

用循环让事情变得简单:

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)


akr*_*run 8

一个选项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)


Tar*_*Jae 8

我又发现了一个带有旋转功能的:

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)


Jul*_*ien 5

您可以在同一个中创建所有变量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)

但我不知道是否可以以更编程的方式做到这一点


Pau*_*ulS 5

另一种可能的解决方案:

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)


Yur*_*kin 5

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)