首页 > 解决方案 > 根据配对列替换值

问题描述

我有一个数据框,每个样本有两列(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 的更简单的方法?

标签: rtidyversepairwise

解决方案


我们可以将 data.frame 拆分为 data.frames 列表并在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通过重塑为“长”pivot_longer格式NApivot_wider

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

推荐阅读