如何用dplyr按行统计指定列两两组合的非NA值数量?
按行统计指定列两两组合的非NA值数量(dplyr简洁实现)
问题背景
现有如下数据框:
id cog com emo AUD-002 12 34 24 PAR-044 NA 28 38 BRE-019 0 NA 51 2-1-GRE NA 31 68
需要按行统计cog, com, emo所有列两两组合中两列均为非NA的情况,满足则标记为1,否则为0,期望输出:
id cog com emo cog-com cog-emo com-emo AUD-002 12 34 24 1 1 1 PAR-044 NA 28 38 0 0 1 BRE-019 0 NA 51 0 1 0 2-1-GRE NA 31 68 0 0 1
目前通过多次mutate语句可实现需求,但当列数较多时操作繁琐,需要更简洁的dplyr实现方式。
数据的dput如下:
structure(list(id = structure(c(2L, 4L, 3L, 1L), .Label = c("2-1-GRE", "AUD-002", "BRE-019", "PAR-044"), class = "factor"), cog = c(12L, NA, 0L, NA), com = c(34L, 28L, NA, 31L), emo = c(24L, 38L, 51L, 68L)), row.names = c(NA, -4L), class = "data.frame")
解决方案
可以通过批量生成列组合+dplyr批量创建新列的方式实现,无需手动编写多个mutate语句,代码可扩展性强(列数增多时仅需修改目标列列表):
方法1:结合rowwise()与c_across()
library(dplyr) # 定义需要处理的目标列 target_cols <- c("cog", "com", "emo") # 生成所有两两列组合 col_pairs <- combn(target_cols, 2, simplify = FALSE) # 批量生成统计列 df_result <- df %>% rowwise() %>% mutate( !!!purrr::map(col_pairs, ~ { # 生成新列名(用"-"连接两个列名) new_col <- paste(.x, collapse = "-") # 判断当前行的两个列是否均为非NA,转成整数1/0 val <- as.integer(all(!is.na(c_across(all_of(.x))))) # 绑定列名与对应值的表达式 purrr::set_names(list(val), new_col) }) ) %>% ungroup() # 查看结果 print(df_result)
方法2:直接构造逻辑判断表达式(无需rowwise)
library(dplyr) library(purrr) target_cols <- c("cog", "com", "emo") col_pairs <- combn(target_cols, 2, simplify = FALSE) df_result <- df %>% mutate( !!!map(col_pairs, ~ { new_col <- paste(.x, collapse = "-") # 构造逻辑判断表达式:!is.na(col1) & !is.na(col2) expr <- parse_expr(paste0("as.integer(!is.na(", .x[1], ") & !is.na(", .x[2], "))")) set_names(list(expr), new_col) }) ) print(df_result)
说明
- 两种方法均能自动生成所有两两列组合的统计列,当目标列数量增加时,仅需修改
target_cols向量即可,无需额外修改代码。 - 核心逻辑是判断每行中两个列是否都不为NA,满足则返回1,否则返回0,与你之前的
rowSums(!is.na(...)) -1逻辑一致(当两列均非NA时rowSums为2,减1得1;否则为0或-1,若数据存在两列均NA的场景,直接判断逻辑与的方式更准确)。
内容的提问来源于stack exchange,提问作者Sandy
相关产品推荐
相关产品推荐

