You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 15:48:04