修改数据框中低于样本5%分位数阈值的值报错,求原因与解决方法
数据框分位数阈值处理的报错修复
问题描述
有一个300行63列的数据框(各列代表不同样本),希望以5%分位数为阈值,将低于该阈值的值置为0。编写的R代码如下:
thresholdForRemoval <- function(x){ quantile(x[x> 0], probs = 0.05) } thresholds <- apply(df, MARGIN = 2, FUN = thresholdForRemoval) x <- df y <- thresholds for(i in 1:ncol(x)){ x[,i] <- ifelse(x[,i] < y[i], 0, x[,i]) }
运行时出现报错:
Error in
vectbl_as_new_col_index():
! Can't recycleifelse(x[, i] < y[i], 0, x[, i])(size 300) to size 1。
错误原因
核心问题是数据框索引的类型差异:如果你的df是tibble(tidyverse风格的数据框),使用x[,i]提取列时,返回的是一个仅含1列的tibble对象,而非长度为300的向量。但ifelse返回的是长度300的向量,尝试将向量赋值给tibble的列(数据框类型)时,R会判定两者尺寸不匹配,触发回收错误。
另外需要注意:如果某列所有值都≤0,x[x>0]会变成空向量,quantile会返回NA,后续比较也会出现异常,需要提前处理这种边界情况。
修复方案
方案1:修改循环索引方式
把x[,i]替换为x[[i]],后者会直接提取列对应的向量,赋值时就不会有尺寸不匹配问题:
thresholdForRemoval <- function(x){ # 处理全为非正值的列,避免返回NA pos_vals <- x[x > 0] if(length(pos_vals) == 0){ return(0) } else { quantile(pos_vals, probs = 0.05) } } thresholds <- apply(df, MARGIN = 2, FUN = thresholdForRemoval) x <- df y <- thresholds for(i in 1:ncol(x)){ x[[i]] <- ifelse(x[[i]] < y[i], 0, x[[i]]) }
方案2:用dplyr简化批量处理
如果使用tidyverse工具,用across可以更简洁地批量处理所有列:
library(dplyr) thresholdForRemoval <- function(x){ pos_vals <- x[x > 0] if(length(pos_vals) == 0) 0 else quantile(pos_vals, 0.05) } x <- df %>% mutate(across(everything(), ~ifelse(.x < thresholdForRemoval(.x), 0, .x)))
方案3:基础R的apply批量处理
直接用apply按列处理,一步生成结果数据框:
thresholdForRemoval <- function(x){ pos_vals <- x[x > 0] thresh <- if(length(pos_vals) == 0) 0 else quantile(pos_vals, 0.05) ifelse(x < thresh, 0, x) } x <- apply(df, MARGIN = 2, FUN = thresholdForRemoval) # 若需要转回数据框类型 x <- as.data.frame(x)
内容的提问来源于stack exchange,提问作者Jasmine Ong
相关产品推荐
相关产品推荐

