R语言用sapply为data.frame添加列报错,求原因与解决方法
解决R语言中sapply修改data.frame时的列缺失问题
咱们先捋清楚你遇到的报错根源:你用sapply的时候踩了一个函数式编程的典型误区——sapply的函数内部是局部环境,你在里面修改的mydata其实是外部原始数据框的副本,根本没影响到外面的真实数据。
举个具体例子,假设cols=2:
- 第一次循环x=1,你尝试给
mydata加第2列,但这时候修改的是局部副本,外部的mydata还是只有初始的1列(label列); - 第二次循环x=2,你要加第3列,可外部的
mydata依然只有1列,中间跳过了第2列,R就会抛出“new columns would leave holes after existing columns”的错误——它不允许你跳过现有列直接往后新增列。
你后来试的两种写法也没解决核心问题:
- 第二种写法里你最后返回了
mydata,但sapply只会把每次修改后的副本存成列表,外部的原始mydata还是没变化; - Map的写法同理,传入的
mydata1是副本,修改的只是副本,原始数据框纹丝不动。
给你两种解决思路:
思路1:强制修改外部环境(不推荐,但贴合你原本的写法逻辑)
如果一定要在sapply里修改外部的mydata,可以用<<-操作符强制修改全局环境的变量,但这种写法破坏了函数式编程的封装性,尽量少用:
cols <- sample(c(1:5), 1) mydata <- data.frame(label = paste0("label ", seq(1,10))) sapply(1:cols, function(x) { mydata[,(x+1)] <<- sample(1:10, 10) })
思路2:函数式编程的正确姿势(推荐)
更优雅的做法是先用sapply生成所有要添加的列数据,再一次性合并到原始数据框里,完全避免修改外部对象的问题:
cols <- sample(c(1:5), 1) # 初始化label列 mydata <- data.frame(label = paste0("label ", seq(1,10))) # 生成cols列随机数据,每列是10个1-10的随机数 new_columns <- sapply(1:cols, function(x) sample(1:10, 10)) # 给新列命名,然后合并 colnames(new_columns) <- paste0("col", 1:cols) mydata <- cbind(mydata, new_columns)
甚至可以一步到位构建整个数据框,更简洁:
cols <- sample(c(1:5), 1) mydata <- data.frame( label = paste0("label ", seq(1,10)), sapply(1:cols, function(x) sample(1:10, 10)) ) # 给新增的列重命名(可选) colnames(mydata)[-1] <- paste0("col", 1:cols)
核心逻辑就是:函数式编程更适合生成数据、组合数据,而不是在循环里偷偷修改外部变量,这样代码更清晰,也不容易出奇怪的bug~
内容的提问来源于stack exchange,提问作者wtrs
相关产品推荐
相关产品推荐

