R中实现data.table逐行提取非NA值与对应列名,支持字符型数据
问题描述
我有一个和“如何识别数据框中每行非NA列”的问题高度相似的需求。
我需要将结果输出为多列格式,同时保留非NA的实际取值。特别需要注意方案需支持非数值型的字符类数据:我还有另一个数据框存储的是字符串而非数值!
输入示例
AFA AFI AII AMA AMI AMU BFA BFI BFU BII 1: 0.79 NA NA 0.58 NA NA NA NA 0.75 NA 2: NA NA NA NA NA 0.78 NA -0.5 NA NA 3: NA NA NA NA NA NA 0.79 -0.5 NA NA 4: NA NA NA NA NA NA NA -0.5 NA NA 5: NA NA NA NA 0.63 NA NA NA NA NA 6: NA NA NA NA NA NA 0.83 NA NA NA 7: 0.63 NA NA NA NA NA NA NA NA 0.82 8: NA NA NA NA 0.63 NA NA NA NA NA 9: NA NA 0.54 0.59 NA NA NA NA NA NA 10: NA 0.51 NA NA NA NA NA NA NA NA
预期输出
V1_Code V1_Val V2_Code V2_Val V3_Code V3_Val 1: AFA 0.79 AMA 0.58 BFU 0.75 2: AMU 0.78 BFI -0.5 NA NA 3: BFA 0.79 BFI -0.5 NA NA and so on....
示例数据结构
structure(list(AFA = c(0.79, NA, NA, NA, NA, NA, 0.63, NA, NA, NA), AFI = c(NA, NA, NA, NA, NA, NA, NA, NA, NA, 0.51), AII = c(NA, NA, NA, NA, NA, NA, NA, NA, 0.54, NA), AMA = c(0.58, NA, NA, NA, NA, NA, NA, NA, 0.59, NA), AMI = c(NA, NA, NA, NA, 0.63, NA, NA, 0.63, NA, NA), AMU = c(NA, 0.78, NA, NA, NA, NA, NA, NA, NA, NA), BFA = c(NA, NA, 0.79, NA, NA, 0.83, NA, NA, NA, NA), BFI = c(NA, -0.5, -0.5, -0.5, NA, NA, NA, NA, NA, NA), BFU = c(0.75, NA, NA, NA, NA, NA, NA, NA, NA, NA), BII = c(NA, NA, NA, NA, NA, NA, 0.82, NA, NA, NA)), row.names = c(NA, -10L), class = c("data.table", "data.frame"))
解决方案
两种方案均天然支持任意数据类型(数值、字符串、因子等),不会对非NA值做强制类型转换。
方案1:data.table实现(适配你的数据结构,效率更高)
library(data.table) # 假设你的数据框变量名为dt dt[, row_id := .I] # 转为长表过滤NA值 long_dt <- melt(dt, id.vars = "row_id", variable.name = "Code", value.name = "Val", na.rm = T) # 按原始行号生成非NA值的顺序编号 long_dt[, pos := rowid(row_id)] # 转回预期的宽表格式 wide_dt <- dcast(long_dt, row_id ~ pos, value.var = c("Code", "Val")) # 调整列顺序符合输出要求 setcolorder(wide_dt, c("row_id", as.vector(t(outer(c("Code", "Val"), 1:max(long_dt$pos), paste, sep="_"))))) # 可选:删除辅助用的行号列 wide_dt[, row_id := NULL]
方案2:tidyverse实现(语法更易读)
library(tidyverse) dt %>% mutate(row_id = row_number()) %>% pivot_longer(-row_id, names_to = "Code", values_to = "Val", values_drop_na = T) %>% group_by(row_id) %>% mutate(pos = row_number()) %>% ungroup() %>% pivot_wider(id_cols = row_id, names_from = pos, values_from = c(Code, Val), names_vary = "slowest") %>% select(-row_id)
内容的提问来源于stack exchange,提问作者Kimster
相关产品推荐
相关产品推荐

