如何用lapply或purrr函数优雅地为数据框指定列添加自定义类?
问题:用lapply/purrr为数据框指定列添加自定义类
背景与需求
现有如下数据框:
jnk <- mtcars[1:3, 1:3] jnk #> mpg cyl disp #> Mazda RX4 21.0 6 160 #> Mazda RX4 Wag 21.0 6 160 #> Datsun 710 22.8 4 108
各列初始类型:
lapply(jnk, class) #> $mpg #> [1] "numeric" #> #> $cyl #> [1] "numeric" #> #> $disp #> [1] "numeric"
需求:为第2、3列添加自定义类"formula"。
原for循环实现可正常生效:
myfunc <- function(df, cols){ for(i in cols){ class(df[[i]]) <- c(class(df[[i]]), "formula") } return(df) } jnk <- myfunc(jnk, 2:3) lapply(jnk, class) #> $mpg #> [1] "numeric" #> #> $cyl #> [1] "numeric" "formula" #> #> $disp #> [1] "numeric" "formula"
最初的lapply实现未生效,核心原因是:R中对象默认按值传递,lapply匿名函数里修改的是数据框的副本,修改操作不会影响外部的原数据框,最终返回的还是未修改的原始数据。
正确的lapply实现
通过将修改后的列重新赋值回数据框,即可实现需求:
newfunc <- function(df, colnums) { # 仅对指定列修改类属性,其余列保持原样 df[colnums] <- lapply(df[colnums], function(col) { class(col) <- c(class(col), "formula") col }) return(df) } # 测试 jnk <- mtcars[1:3, 1:3] jnk <- newfunc(jnk, 2:3) lapply(jnk, class) #> $mpg #> [1] "numeric" #> #> $cyl #> [1] "numeric" "formula" #> #> $disp #> [1] "numeric" "formula"
用purrr包实现更优雅的版本
purrr包的modify_at函数专门用于修改数据框的指定列,代码更简洁可读:
library(purrr) newfunc_purrr <- function(df, colnums) { modify_at(df, colnums, function(col) { class(col) <- c(class(col), "formula") col }) } # 测试 jnk <- mtcars[1:3, 1:3] jnk <- newfunc_purrr(jnk, 2:3) lapply(jnk, class) #> $mpg #> [1] "numeric" #> #> $cyl #> [1] "numeric" "formula" #> #> $disp #> [1] "numeric" "formula"
方案对比与最优选择
- 原for循环方案:无需依赖第三方包,性能稳定(R的for循环已优化,数据量不大时效率很高),逻辑清晰,是可靠的选择。
- 修正后的lapply方案:不依赖第三方包,代码更具函数式风格。
- purrr::modify_at方案:代码最简洁优雅,可读性强,但需要引入purrr包。
没有绝对的最优方案,可根据场景选择:如果项目已使用tidyverse生态,优先选purrr版本;如果要避免额外依赖,原for循环或修正的lapply版本都很合适。
内容的提问来源于stack exchange,提问作者FB001
相关产品推荐
相关产品推荐

