在lapply中为分组dataframe嵌套for循环返回NULL的解决方法
问题描述
有一个简化的DataFrame df:
> df group value 1 A 1 2 A 4 3 B 2 4 B 3 5 C 2 6 C 1
df 的dput代码:
df<-structure(list(group = c("A", "A", "B", "B", "C", "C"), value = c(1, 4, 2, 3, 2, 1)), class = "data.frame", row.names = c(NA, -6L))
尝试通过 lapply 为每个分组应用for循环:先按group拆分数据为列表,再在每个子DataFrame中执行循环,但运行后返回NULL,可复现代码如下:
df = data.frame(group = c("A", "A", "B", "B", "C", "C"), value = c(1, 4, 2, 3, 2, 1)) l = split(df, df$group) lapply(l, \(x) { for(i in 1:nrow(x)) { if(x$value[i] == 1) { x$value[i] = x$value[i] + 1 } else x$value[i] = x$value[i] + 2 } }) #> $A #> NULL #> #> $B #> NULL #> #> $C #> NULL
预期输出应为:
$A group value 1 A 2 2 A 6 $B group value 3 B 3 4 B 5 $C group value 5 C 4 6 C 2
为什么返回NULL?
lapply会返回传入函数中最后一行代码的执行结果。你的匿名函数内只有for循环,而R中的for循环默认不会返回任何值(执行完毕后返回NULL),因此lapply最终输出的就是每个分组对应的NULL。
解决方案
方案1:修改匿名函数,返回修改后的子DataFrame
在for循环结束后,添加一行返回修改后的x(R函数默认返回最后一行的结果):
df = data.frame(group = c("A", "A", "B", "B", "C", "C"), value = c(1, 4, 2, 3, 2, 1)) l = split(df, df$group) result <- lapply(l, \(x) { for(i in 1:nrow(x)) { if(x$value[i] == 1) { x$value[i] = x$value[i] + 1 } else { x$value[i] = x$value[i] + 2 } } x # 返回修改后的子DataFrame }) print(result)
运行后即可得到预期输出。
方案2:用向量化操作替代for循环(更高效简洁)
在R中,向量化操作通常比逐行遍历的for循环更高效,代码也更简洁:
result <- lapply(l, \(x) { x$value <- ifelse(x$value == 1, x$value + 1, x$value + 2) x })
此方法同样能得到符合预期的结果。
内容的提问来源于stack exchange,提问作者Quinten
相关产品推荐
相关产品推荐

