在R语言中跨多个数据框使用ifelse生成新数据框
问题描述
我有两个R数据框:
- 数据框
Averages:包含1列Lat,以及约一千列命名为ASV+连续数字的列,数据示例如下:
> dput(averages[1:10, c(1: 16)]) structure(list(Lat = c(-59.99833333, -59.33383333, -58.66866667, -58.00066667, -57.33483333, -56.67116667, -53.00033333, -52.335, -51.67116667, -51.00016667), asv_3 = c(1838.5, 275.5, 175, 733.5, 1510.5, 2538.5, 3451.5, 1084.5, 811.5, 545.5), asv_4 = c(2758.5, 1101.5, 1679, 676, 844, 888.5, 738.5, 1051.5, 1057, 777.5), asv_5 = c(170.5, 245, 94, 81.5, 195, 80.5, 262, 335, 301, 161), asv_7 = c(4632, 1019.5, 1790, 27, 34.5, 19, 3, 0, 0, 1.5), asv_8 = c(743, 849, 1109.5, 450.5, 408.5, 404, 258, 212.5, 297.5, 212.5), asv_9 = c(625.5, 2234, 1726, 3031.5, 5130.5, 368, 21, 0, 0, 0), asv_10 = c(0, 0, 0, 0, 0, 0, 1198, 3006, 1740.5, 1198), asv_11 = c(0, 0, 0, 0, 0, 0, 1334.5, 3013, 2209.5, 1619), asv_12 = c(822, 485, 346, 221.5, 239.5, 191.5, 250, 232.5, 296, 169.5), asv_14 = c(183, 26.5, 16, 333.5, 1031, 1520.5, 409.5, 195, 51.5, 63), asv_15 = c(603.5, 1365.5, 1246.5, 1192.5, 1312.5, 765.5, 65, 56.5, 95.5, 80), asv_17 = c(2657.5, 1713, 1683, 75, 48.5, 45, 141.5, 100, 178, 60.5), asv_18 = c(0, 0, 0, 0, 0, 0, 0, 0, 0, 2.5), asv_19 = c(0, 0, 0, 0, 0, 0, 36.5, 25.5, 39.5, 51.5), asv_20 = c(0, 1.5, 0, 0, 0, 0, 292, 673, 512.5, 319)), row.names = c(NA, -10L), class = c("tbl_df", "tbl", "data.frame" ))
- 数据框
ranges:基于Averages生成,包含ASV和V1列,存储Averages中的ASV名称及其对应的范围值,数据示例如下:
> dput(ranges[1:15, c(1: 2)]) structure(list(ASV = c("asv_3", "asv_4", "asv_5", "asv_7", "asv_8", "asv_9", "asv_10", "asv_11", "asv_12", "asv_14", "asv_15", "asv_17", "asv_18", "asv_19", "asv_20"), V1 = c(30.00133333, 30.00133333, 30.00133333, 16.00083333, 30.00133333, 9.66466666, 22.00283333, 10.004, 30.00133333, 16.46566666, 17.002, 17.99783333, 20.00266667, 23.00333333, 29.33683333)), row.names = c(NA, 15L), class = "data.frame")
需求
生成第三个数据框,保留Averages的列名和Lat列的所有值,填充规则:
- 若
Averages中某ASV列的数值>0,则替换为ranges中对应ASV的V1值; - 若数值为0,则保留0。
例如:Averages中Lat为-59.99833333的asv_3值为1838.5>0,新数据框对应位置应填入ranges中asv_3的V1值30.00133333。
尝试用ifelse实现未成功,求解决方案。
解决方案
方法1:使用dplyr + tidyr(tidyverse风格)
适合处理大量列,代码简洁易读:
library(tidyverse) # 将ranges转换为命名向量,方便快速匹配ASV对应的V1值 range_vec <- deframe(ranges) # 批量处理所有ASV列 new_df <- averages %>% mutate(across(starts_with("asv_"), ~ifelse(.x > 0, range_vec[cur_column()], 0)))
方法2:使用base R
无需加载额外包,适合偏好原生R语法的场景:
# 提取所有ASV列的列名 asv_cols <- grep("^asv_", colnames(averages), value = TRUE) # 创建命名向量,ASV名称为向量名,对应V1值为向量元素 range_vec <- setNames(ranges$V1, ranges$ASV) # 循环处理每一列 new_df <- averages for(col in asv_cols){ new_df[[col]] <- ifelse(new_df[[col]] > 0, range_vec[col], 0) }
核心逻辑说明
两种方法的核心都是:
- 将
ranges转换为命名向量,让ASV名称与对应范围值形成一一映射; - 对每个ASV列做条件判断:值大于0则替换为对应范围值,否则保留0。
可以通过查看new_df的特定列验证结果,比如asv_7列,原数据中第8、9行是0,新数据框中这两行仍为0,其余行则替换为16.00083333。
内容的提问来源于stack exchange,提问作者CoccoLoco
相关产品推荐
相关产品推荐

