根据配对列替换值

Replacing value depending on paired column

我有一个数据框,每个样本有两列(n > 1000 个样本):

df <- data.frame(
    "sample1.a" = 1:5, "sample1.b" = 2,
    "sample2.a" = 2:6, "sample2.b" = c(1, 3, 3, 3, 3),
    "sample3.a" = 3:7, "sample3.b" = 2)

如果.b列中有零,则.a列中的相应值应设置为NA。

我想在 colnames(没有后缀)上写一个函数来过滤每对列和条件交换值。有没有更简单的基于tidyverse的方法?

我们可以将 data.frame 拆分为 data.frame 的列表,然后在 base R

中进行替换
df1 <- do.call(cbind, lapply(split.default(df,  
   sub("\..*", "", names(df))), function(x) {
             x[,1][x[2] == 0] <- NA
      x}))

或者另一种选择是 Map

acols <- endsWith(names(df), "a")
bcols <- endsWith(names(df), "b")
df[acols] <- Map(function(x, y) replace(x, y == 0, NA), df[acols], df[bcols])

或者如果列与'a'、'b'列交替出现,则使用逻辑索引进行循环,创建'b'列的逻辑矩阵,并在'a' 列到 NA

df[c(TRUE, FALSE)][df[c(FALSE, TRUE)] == 0] <- NA

或带有 tidyverse 的选项,方法是重塑为 'long' 格式 (pivot_longer),如果有相应的格式,则将 'a' 列更改为 NA 'a' 中的 0,并使用 pivot_wider

重塑回 'wide' 格式
library(dplyr)
library(tidyr)
df %>%
    mutate(rn = row_number()) %>%
   pivot_longer(cols = -rn, names_sep="\.",
        names_to = c('group', '.value')) %>% 
   mutate(a = na_if(b, a == 0)) %>%
   pivot_wider(names_from = group, values_from = c(a, b)) %>% 
   select(-rn)
# A tibble: 5 x 6
#  a_sample1 a_sample2 a_sample3 b_sample1 b_sample2 b_sample3
#      <dbl>     <dbl>     <dbl>     <dbl>     <dbl>     <dbl>
#1         2         1         2         2         1         2
#2         2         3         2         2         3         2
#3         2         3         2         2         3         2
#4         2         3         2         2         3         2
#5         2         3         2         2         3         2