如何在R中不使用循环,连续在对应列旁添加新列?
批量添加分位数列并紧邻原变量列的实现方法
问题背景
我有如下DataFrame:
set.seed(42) ## 保证结果可复现 df <- data.frame("time"=c(1:40), "Var1"=sample(1:100, size=40), "Var2"=sample(1:100, size=40), "Var3"=sample(1:100, size=40)) head(df) # time Var1 Var2 Var3 # 1 1 49 3 38 # 2 2 65 21 1 # 3 3 25 2 13 # 4 4 74 58 78 # 5 5 18 10 5 # 6 6 100 40 73
先计算各变量的分位数:
(quantiles <- as.data.frame(apply(df[2:4] , 2 , quantile, probs=seq(0, 1, 1/10), na.rm=TRUE))) # Var1 Var2 Var3 # 0% 2.0 2.0 1.0 # 10% 5.9 8.9 11.4 # 20% 19.6 17.6 15.8 # 30% 25.7 31.1 28.1 # 40% 35.2 41.2 35.8 # 50% 42.5 51.0 42.5 # 60% 53.2 57.4 56.4 # 70% 67.3 70.2 66.0 # 80% 80.8 80.4 78.6 # 90% 89.4 90.5 90.1 # 100% 100.0 99.0 100.0
接着为每个变量添加对应的分位数列,使用.bincode处理断点不唯一的情况:
df$QuantVar1 <- .bincode(x=df$Var1, breaks=quantiles$Var1, include.lowest=T, right=T) df$QuantVar2 <- .bincode(x=df$Var2, breaks=quantiles$Var2, include.lowest=T, right=T) df$QuantVar3 <- .bincode(x=df$Var3, breaks=quantiles$Var3, include.lowest=T, right=T) head(df) # time Var1 Var2 Var3 QuantVar1 QuantVar2 QuantVar3 # 1 1 49 3 38 6 1 5 # 2 2 65 21 1 7 3 1 # 3 3 25 2 13 3 1 2 # 4 4 74 58 78 8 7 8 # 5 5 18 10 5 2 2 1 # 6 6 100 40 73 10 4 8
之后手动调整列顺序,让新列紧邻原变量:
library(dplyr); library(tidyft) df <- df %>% relocate(QuantVar1, .after = Var1) df <- df %>% relocate(QuantVar2, .after = Var2) df <- df %>% relocate(QuantVar3, .after = Var3) head(df) # time Var1 QuantVar1 Var2 QuantVar2 Var3 QuantVar3 # 1 1 49 6 3 1 38 5 # 2 2 65 7 21 3 1 1 # 3 3 25 3 2 1 13 2 # 4 4 74 8 58 7 78 8 # 5 5 18 2 10 2 5 1 # 6 6 100 10 40 4 73 8
但变量数量多的时候手动操作不可行,希望用lapply系列函数实现,无需循环,同时让新列自动紧邻原变量列。
解决方案
方法1:使用dplyr的across+relocate批量处理
结合across批量生成分位数列,再通过构造列顺序规则自动重排,无需逐个调用relocate:
library(dplyr) # 定义需要处理的变量列(排除time列) var_cols <- names(df)[-1] # 批量添加分位数列并调整顺序 df <- df %>% mutate(across(all_of(var_cols), ~.bincode(x = ., breaks = quantiles[[cur_column()]], include.lowest = T, right = T), .names = "Quant{.col}")) %>% # 构造目标列顺序:time + 每个变量紧跟对应分位数列 relocate(!!!unlist(map(var_cols, ~c(.x, paste0("Quant", .x)))), .after = time) head(df)
方法2:使用purrr的map_dfc生成新列并合并
通过map_dfc对每个变量生成原列+分位数列的组合,再和time列合并:
library(purrr) library(dplyr) var_cols <- names(df)[-1] # 对每个变量生成原列和分位数列的组合 var_quant_pairs <- map_dfc(var_cols, function(col) { tibble( !!col := df[[col]], !!paste0("Quant", col) := .bincode(df[[col]], breaks = quantiles[[col]], include.lowest = T, right = T) ) }) # 合并time列和生成的列对 df <- bind_cols(df[, "time", drop = F], var_quant_pairs) head(df)
方法3:Base R实现(无需tidyverse)
利用lapply生成分位数列,再通过构造列索引重排:
var_cols <- names(df)[-1] # 批量生成分位数列 quant_cols <- lapply(var_cols, function(col) { .bincode(df[[col]], breaks = quantiles[[col]], include.lowest = T, right = T) }) names(quant_cols) <- paste0("Quant", var_cols) # 添加到原数据框 df <- cbind(df, quant_cols) # 构造目标列顺序:time + 每个变量和对应分位数列交替 new_col_order <- c("time", unlist(mapply(c, var_cols, paste0("Quant", var_cols)))) df <- df[, new_col_order] head(df)
内容的提问来源于stack exchange,提问作者emr2
相关产品推荐
相关产品推荐

