r-为什么转换为"list"会提高"lapply"的性能



我很惊讶地看到,第一行的运行速度比第二行慢得多,第二行的性能与矢量化版本的性能相当。如果处理列表比处理numeric(n)向量快得多,为什么R不自动将其输入转换为列表?

> system.time(lapply(1:10^7, sqrt))
user  system elapsed
4.445   0.204   4.692
> system.time(lapply(list(1:10^7), sqrt))
user  system elapsed
0.048   0.015   0.062
> system.time(sqrt(1:10^7))
user  system elapsed
0.04    0.00    0.04

这是版本信息

$ R --version
R version 4.1.3 (2022-03-10) -- "One Push-Up"
Copyright (C) 2022 The R Foundation for Statistical Computing
Platform: x86_64-apple-darwin21.4.0 (64-bit)
R is free software and comes with ABSOLUTELY NO WARRANTY.
You are welcome to redistribute it under the terms of the
GNU General Public License versions 2 or 3.
For more information about these matters see
https://www.gnu.org/licenses/.
$ sw_vers
ProductName:    macOS
ProductVersion: 12.3.1
BuildVersion:   21E258

原因是第二个表达式只是长度为1 的list

> length(list(1:10^7))
[1] 1

这与直接应用CCD_ 3基本相同。相反,如果我们想纯粹对list的每个元素进行此操作,则需要as.list而不是list,即

> length(as.list(1:10^7))
[1] 10000000

如果意图在向量的每个元素上循环,则从vector转换为list是不必要的。在vector中,每个元素都是一个单元(与matrix相同,仅具有dim属性(,但在data.frame/tibble/data.table中,每个单元都是一列。因此,lapply在单元上循环,即数据帧中的列,其中作为vector中的单个元素。当我们用list包裹一个向量时,它将整个向量封装为单个list元素

> list(1:3)
[[1]]
[1] 1 2 3
> as.list(1:3)
[[1]]
[1] 1
[[2]]
[1] 2
[[3]]
[1] 3

由于sqrt是一个矢量化函数,当我们通过在第一个list上循环来应用sqrt时,它只循环一次,但在第二个循环中,它循环多次。


因此,我们得到了类似的定时(当然,额外的定时将是用as.list将矢量转换为list(

>  system.time(lapply(1:10^7, sqrt))
user  system elapsed 
4.364   0.220   4.748 
> system.time(lapply(as.list(1:10^7), sqrt))
user  system elapsed 
4.882   0.367   5.518 

更快的选择是使用vapply(如果我们在循环上应用非矢量化函数(

> system.time(vapply(1:10^7, sqrt, numeric(1)))
user  system elapsed 
2.464   0.172   2.633