如何获取data.frame指定列每行(排除NA)的第k小值?
问题描述
给定一个data.frame,需要从命名规则固定(以i开头)且彼此相邻的指定列中,提取每行排除NA后的第k小值,最终生成包含原数据列和各k值结果的表格。示例输入与期望输出如下:
输入表格
| ID | i1 | i2 | i3 |
|---|---|---|---|
| 1 | NA | NA | NA |
| 1 | 0 | NA | NA |
| 1 | 1 | 0 | NA |
| 1 | 1 | 1 | NA |
| 1 | 0 | 1 | NA |
| 1 | 0 | 1 | 2 |
期望输出表格
| ID | i1 | i2 | i3 | k=1 | k=2 | k=3 |
|---|---|---|---|---|---|---|
| 1 | NA | NA | NA | NA | NA | NA |
| 1 | 0 | NA | NA | 0 | NA | NA |
| 1 | 1 | 0 | NA | 0 | 1 | NA |
| 1 | 1 | 1 | NA | 1 | 1 | NA |
| 1 | 0 | 1 | NA | 0 | 1 | NA |
| 1 | 2 | 1 | 0 | 0 | 1 | 2 |
要求方案适配3列场景,同时能无缝扩展至更多相邻列(如i1至i6)。
解决方案
以下提供两种通用的R语言实现方案,均支持自动识别所有i开头的列,无需手动调整列数:
方案1:基础R实现
利用apply逐行处理,结合排序和索引提取目标值:
# 构造示例数据 df <- data.frame( ID = rep(1, 6), i1 = c(NA, 0, 1, 1, 0, 0), i2 = c(NA, NA, 0, 1, 1, 1), i3 = c(NA, NA, NA, NA, NA, 2) ) # 自动匹配所有i开头的列 target_cols <- grep("^i", names(df), value = TRUE) k_values <- 1:length(target_cols) # k值范围对应列数 # 逐行计算各k对应的第k小值 result_matrix <- t(apply(df[target_cols], 1, function(row) { # 排除NA后排序 sorted_vals <- sort(row[!is.na(row)], na.last = NA) # 提取对应k值,不足则返回NA sapply(k_values, function(k) if(k <= length(sorted_vals)) sorted_vals[k] else NA) })) # 合并结果到原数据框 colnames(result_matrix) <- paste0("k=", k_values) final_df <- cbind(df, result_matrix) # 查看最终结果 print(final_df)
方案2:tidyverse风格实现
适合熟悉dplyr/purrr的用户,代码更具可读性:
library(dplyr) library(purrr) # 构造示例数据 df <- data.frame( ID = rep(1, 6), i1 = c(NA, 0, 1, 1, 0, 0), i2 = c(NA, NA, 0, 1, 1, 1), i3 = c(NA, NA, NA, NA, NA, 2) ) # 定义目标列和k值范围 target_cols <- starts_with("i") k_values <- 1:length(select(df, target_cols)) # 逐行处理并生成结果列 final_df <- df %>% rowwise() %>% mutate( # 动态生成k=1、k=2...列 !!!set_names( map(k_values, ~{ sorted_vals <- sort(c_across(target_cols), na.last = NA) if(.x <= length(sorted_vals)) sorted_vals[.x] else NA }), paste0("k=", k_values) ) ) %>% ungroup() # 查看最终结果 print(final_df)
两种方案都能自动适配任意数量的i开头相邻列,比如新增i4-i6后,无需修改核心逻辑即可生成对应k=1至k=6的结果列。
内容的提问来源于stack exchange,提问作者Alex Braksator
相关产品推荐
相关产品推荐

