R中使用lapply调用依赖全局常量的函数出错,求解决方案
R中lapply批量处理数据框列时的错误与修复
问题背景
尝试在R中使用lapply将依赖全局向量BASELINE_CLASSIFICATION_THRESHOLDS的value_to_classification函数应用到数据框列,实现数值到Mild/Moderate/Severe/Extreme四个等级的转换。原代码如下:
BASELINE_CLASSIFICATION_THRESHOLDS <- c(0, 3.5, 6.5, 10.0000001) value_to_classification <- function(x){ if((x >= BASELINE_CLASSIFICATION_THRESHOLDS[1]) && (x < BASELINE_CLASSIFICATION_THRESHOLDS[2])){ classification <- "Mild" } else if((x >= BASELINE_CLASSIFICATION_THRESHOLDS[2]) && (x < BASELINE_CLASSIFICATION_THRESHOLDS[3])){ classification <- "Moderate" } else if((x >= BASELINE_CLASSIFICATION_THRESHOLDS[3]) && (x < round(BASELINE_CLASSIFICATION_THRESHOLDS[4])){ classification <- "Severe" } else { classification <- "Extreme" } return(classification) } df <- data.frame(x = runif(10, min = 0, max = 10), y = runif(10, min = 0, max = 10), z = runif(10, min = 0, max = 10))
执行lapply(df["x"], value_to_classification)时出现错误警告,仅返回一个"Mild"结果;但lapply(df[["x"]], value_to_classification)可正常运行。需要实现类似df[c("x1", "x2")] <- lapply(df[c("x", "y")], value_to_classification)的批量列转换。
错误原因
- 数据类型差异:
df["x"]返回的是单列数据框,lapply处理数据框时会将整列(向量)作为单个元素传入函数;df[["x"]]返回的是向量,lapply会将向量的每个元素逐个传入函数。原函数是为单个数值设计的,当传入整列向量时,if语句的条件会返回长度大于1的逻辑向量,而if仅接受长度为1的逻辑值,触发警告后仅取第一个元素的判断结果,导致只返回"Mild"。 - 语法错误:
第三个else if的条件表达式缺少闭合括号,正确写法应为:else if((x >= BASELINE_CLASSIFICATION_THRESHOLDS[3]) && (x < round(BASELINE_CLASSIFICATION_THRESHOLDS[4]))){
修复方案
方案1:使用向量化函数(推荐)
利用R的向量化特性,用cut函数实现分类,代码更简洁高效:
BASELINE_CLASSIFICATION_THRESHOLDS <- c(0, 3.5, 6.5, 10.0000001) classification_labels <- c("Mild", "Moderate", "Severe", "Extreme") value_to_classification <- function(x) { # include.lowest = TRUE 包含左边界,right = FALSE 区间为左闭右开 cut(x, breaks = BASELINE_CLASSIFICATION_THRESHOLDS, labels = classification_labels, include.lowest = TRUE, right = FALSE) } # 批量处理多列并赋值 df[c("x1", "x2")] <- lapply(df[c("x", "y")], value_to_classification)
方案2:修改原函数为向量化版本
用嵌套ifelse实现向量化判断,兼容向量输入:
BASELINE_CLASSIFICATION_THRESHOLDS <- c(0, 3.5, 6.5, 10.0000001) value_to_classification <- function(x){ ifelse(x >= BASELINE_CLASSIFICATION_THRESHOLDS[1] & x < BASELINE_CLASSIFICATION_THRESHOLDS[2], "Mild", ifelse(x >= BASELINE_CLASSIFICATION_THRESHOLDS[2] & x < BASELINE_CLASSIFICATION_THRESHOLDS[3], "Moderate", ifelse(x >= BASELINE_CLASSIFICATION_THRESHOLDS[3] & x < round(BASELINE_CLASSIFICATION_THRESHOLDS[4]), "Severe", "Extreme"))) } # 批量处理多列 df[c("x1", "x2")] <- lapply(df[c("x", "y")], value_to_classification)
方案3:用Vectorize包装原函数
如果不想修改原函数逻辑,可通过Vectorize将其转为支持向量输入的函数:
# 先修复原函数的语法错误 value_to_classification <- function(x){ if((x >= BASELINE_CLASSIFICATION_THRESHOLDS[1]) && (x < BASELINE_CLASSIFICATION_THRESHOLDS[2])){ classification <- "Mild" } else if((x >= BASELINE_CLASSIFICATION_THRESHOLDS[2]) && (x < BASELINE_CLASSIFICATION_THRESHOLDS[3])){ classification <- "Moderate" } else if((x >= BASELINE_CLASSIFICATION_THRESHOLDS[3]) && (x < round(BASELINE_CLASSIFICATION_THRESHOLDS[4]))){ classification <- "Severe" } else { classification <- "Extreme" } return(classification) } # 转为向量化函数 vec_classify <- Vectorize(value_to_classification) # 批量处理 df[c("x1", "x2")] <- lapply(df[c("x", "y")], vec_classify)
内容的提问来源于stack exchange,提问作者Thomas Philips
相关产品推荐
相关产品推荐

