R语言赋值lapply输出时方括号[]的作用及更优实现方案
问题描述
我有一个数据框变量v,内容如下:
> head(v) C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 1 0.m2 0.m2 0.m2 0.m2 0.m2 0.m2 0.m2 0.m2 0.m2 0.m2 2 0.m1 0.m1 0.m1 0.m1 0.m1 0.m1 0.m1 0.m1 0.m1 0.m1 3 0.p1 0.p1 0.p1 0.p1 0.p1 0.p1 0.p1 0.p1 0.p1 0.p1 4 0.m1 0.m1 0.m1 0.m1 0.m1 0.m1 0.m1 0.m1 0.m1 0.m1 5 0.m2 0.m2 0.m2 0.m2 0.m2 0.m2 0.m2 0.m2 0.m2 0.m2 6 0.m2 0.m2 0.m2 0.m2 0.m2 0.m2 0.m2 0.m2 0.m2 0.m2
我想去掉每个元素的后缀(比如.m1)并转换为数值类型。用lapply处理后得到的是列表结构:
> lapply(v, function(x) as.numeric(gsub("\\..*", "", x))) %>% str List of 10 $ C1 : num [1:40000] 0 0 0 0 0 0 0 0 0 0 ... $ C2 : num [1:40000] 0 0 0 0 0 0 0 0 0 0 ... $ C3 : num [1:40000] 0 0 0 0 0 0 0 0 0 0 ... $ C4 : num [1:40000] 0 0 0 0 0 0 0 0 0 0 ... $ C5 : num [1:40000] 0 0 0 0 0 0 0 0 0 0 ... $ C6 : num [1:40000] 0 0 0 0 0 0 0 0 0 0 ... $ C7 : num [1:40000] 0 0 0 0 0 0 0 0 0 0 ... $ C8 : num [1:40000] 0 0 0 0 0 0 0 0 0 0 ... $ C9 : num [1:40000] 0 0 0 0 0 0 0 0 0 0 ... $ C10: num [1:40000] 0 0 0 0 0 0 0 0 0 0 ...
但我需要保留原数据框的维度,于是用v[]=lapply(v, function(x) as.numeric(gsub("\\..*", "", x)))实现了需求,处理后的数据框如下:
> head(v) C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 1 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 5 0 0 0 0 0 0 0 0 0 0 6 0 0 0 0 0 0 0 0 0 0
现在有两个疑问:
- 这里的方括号
[]是不是修改v中每个元素的必要操作? - 有没有更优的代码实现方式?
回答
关于方括号[]的必要性
是的,v[] = ...里的方括号是必要的。原因很直接:
- 直接写
v = lapply(...)的话,lapply返回的是列表,会把原数据框v直接覆盖成列表类型,彻底丢失数据框的结构。 - 而
v[] = ...是对数据框的列进行原地替换,相当于告诉R:把右边列表里的每一列对应赋值给原数据框的列,全程保留原数据框的属性(行数、列名、数据框类型),不会改变v的数据结构。
更优的实现方式
方式1:tidyverse风格(dplyr批量处理)
如果你常用tidyverse工具包,代码会更简洁易读:
library(dplyr) # 新版dplyr推荐写法,支持灵活选择列 v <- v %>% mutate(across(everything(), ~as.numeric(gsub("\\..*", "", .x)))) # 旧版dplyr可以用mutate_all v <- v %>% mutate_all(~as.numeric(gsub("\\..*", "", .x)))
这种写法不仅简洁,还能轻松扩展——比如只处理包含特定关键词的列,只需把everything()换成starts_with("C")这类选择器即可。
方式2:基础R直接转数据框
既然lapply返回的列表结构和数据框的列结构完全一致,直接把列表转成数据框也能达到目的:
v <- data.frame(lapply(v, function(x) as.numeric(gsub("\\..*", "", x))))
这种方式不需要原地修改,直接生成新的数据框,适合不想改动原对象的场景。
方式3:用parse_number简化字符串处理
如果你的后缀都是非数字字符,用readr包的parse_number可以直接提取数字部分,省去写正则的麻烦:
library(readr) # tidyverse写法 v <- v %>% mutate(across(everything(), parse_number)) # 基础R原地修改写法 v[] <- lapply(v, parse_number)
parse_number会自动忽略数字后面的所有非数字内容,代码更简洁且不易出错。
内容的提问来源于Stack Exchange,提问作者wkde
相关产品推荐
相关产品推荐

