“*apply”家族真的没有矢量化吗?

2023-11-29

所以我们习惯对每个 R 新用户说“apply没有矢量化,看看 Patrick BurnsR地狱圈 4” 其中说(我引用):

常见的反应是使用 apply 系列中的函数。这不是 向量化,它是循环隐藏的。 apply 函数有一个 for 循环 它的定义。 lapply 函数隐藏了循环,但执行 时间往往大致等于显式 for 循环。

确实,快速浏览一下apply源代码揭示了循环:

grep("for", capture.output(getAnywhere("apply")), value = TRUE)
## [1] "        for (i in 1L:d2) {"  "    else for (i in 1L:d2) {"

到目前为止还不错,但是看一下lapply or vapply实际上揭示了完全不同的画面:

lapply
## function (X, FUN, ...) 
## {
##     FUN <- match.fun(FUN)
##     if (!is.vector(X) || is.object(X)) 
##        X <- as.list(X)
##     .Internal(lapply(X, FUN))
## }
## <bytecode: 0x000000000284b618>
## <environment: namespace:base>

所以显然没有Rfor循环隐藏在那里,而是调用内部 C 编写的函数。

快速浏览一下rabbit hole显示几乎相同的图片

此外,我们以colMeans例如,函数从未被指责没有被矢量化

colMeans
# function (x, na.rm = FALSE, dims = 1L) 
# {
#   if (is.data.frame(x)) 
#     x <- as.matrix(x)
#   if (!is.array(x) || length(dn <- dim(x)) < 2L) 
#     stop("'x' must be an array of at least two dimensions")
#   if (dims < 1L || dims > length(dn) - 1L) 
#     stop("invalid 'dims'")
#   n <- prod(dn[1L:dims])
#   dn <- dn[-(1L:dims)]
#   z <- if (is.complex(x)) 
#     .Internal(colMeans(Re(x), n, prod(dn), na.rm)) + (0+1i) * 
#     .Internal(colMeans(Im(x), n, prod(dn), na.rm))
#   else .Internal(colMeans(x, n, prod(dn), na.rm))
#   if (length(dn) > 1L) {
#     dim(z) <- dn
#     dimnames(z) <- dimnames(x)[-(1L:dims)]
#   }
#   else names(z) <- dimnames(x)[[dims + 1]]
#   z
# }
# <bytecode: 0x0000000008f89d20>
#   <environment: namespace:base>

啊?它也只是调用.Internal(colMeans(...我们也可以在兔子洞。那么这与.Internal(lapply(..?

实际上,快速基准测试表明sapply表现不差于colMeans比一个好得多for大数据集的循环

m <- as.data.frame(matrix(1:1e7, ncol = 1e5))
system.time(colMeans(m))
# user  system elapsed 
# 1.69    0.03    1.73 
system.time(sapply(m, mean))
# user  system elapsed 
# 1.50    0.03    1.60 
system.time(apply(m, 2, mean))
# user  system elapsed 
# 3.84    0.03    3.90 
system.time(for(i in 1:ncol(m)) mean(m[, i]))
# user  system elapsed 
# 13.78    0.01   13.93 

换句话说,这样说是否正确lapply and vapply 实际上是矢量化的(相比apply这是一个for循环也调用lapply)帕特里克·伯恩斯的真正意思是什么?


首先,在您的示例中,您对“data.frame”进行测试,这对于colMeans, apply and "[.data.frame"因为它们有开销:

system.time(as.matrix(m))  #called by `colMeans` and `apply`
#   user  system elapsed 
#   1.03    0.00    1.05
system.time(for(i in 1:ncol(m)) m[, i])  #in the `for` loop
#   user  system elapsed 
#  12.93    0.01   13.07

在矩阵上,图片有点不同:

mm = as.matrix(m)
system.time(colMeans(mm))
#   user  system elapsed 
#   0.01    0.00    0.01 
system.time(apply(mm, 2, mean))
#   user  system elapsed 
#   1.48    0.03    1.53 
system.time(for(i in 1:ncol(mm)) mean(mm[, i]))
#   user  system elapsed 
#   1.22    0.00    1.21

关于问题的主要部分,两者的主要区别lapply/mapply/etc 和简单的 R 循环是循环完成的地方。正如 Roland 指出的,C 循环和 R 循环都需要在每次迭代中评估 R 函数,这是成本最高的。真正快速的 C 函数是那些在 C 中完成所有操作的函数,所以,我想,这应该就是“向量化”的意义所在?

我们找到每个“列表”元素的平均值的示例:

(编辑 2016 年 5 月 11 日:我相信寻找“平均值”的例子对于迭代评估 R 函数和编译代码之间的差异来说并不是一个好的设置,(1)因为 R 的平均值算法在“数字”上的特殊性而不是简单的sum(x) / length(x)(2)用以下命令测试“列表”应该更有意义length(x) >> lengths(x)。因此,“平均”示例被移至末尾并替换为另一个示例。)

作为一个简单的例子,我们可以考虑找到每个的相反length == 1“列表”的元素:

In a tmp.c file:

#include <R.h>
#define USE_RINTERNALS 
#include <Rinternals.h>
#include <Rdefines.h>

/* call a C function inside another */
double oppC(double x) { return(ISNAN(x) ? NA_REAL : -x); }
SEXP sapply_oppC(SEXP x)
{
    SEXP ans = PROTECT(allocVector(REALSXP, LENGTH(x)));
    for(int i = 0; i < LENGTH(x); i++) 
        REAL(ans)[i] = oppC(REAL(VECTOR_ELT(x, i))[0]);

    UNPROTECT(1);
    return(ans);
}

/* call an R function inside a C function;
 * will be used with 'f' as a closure and as a builtin */    
SEXP sapply_oppR(SEXP x, SEXP f)
{
    SEXP call = PROTECT(allocVector(LANGSXP, 2));
    SETCAR(call, install(CHAR(STRING_ELT(f, 0))));

    SEXP ans = PROTECT(allocVector(REALSXP, LENGTH(x)));     
    for(int i = 0; i < LENGTH(x); i++) { 
        SETCADR(call, VECTOR_ELT(x, i));
        REAL(ans)[i] = REAL(eval(call, R_GlobalEnv))[0];
    }

    UNPROTECT(2);
    return(ans);
}

在 R 端:

system("R CMD SHLIB /home/~/tmp.c")
dyn.load("/home/~/tmp.so")

有数据:

set.seed(007)
myls = rep_len(as.list(c(NA, runif(3))), 1e7)

#a closure wrapper of `-`
oppR = function(x) -x

for_oppR = compiler::cmpfun(function(x, f)
{
    f = match.fun(f)  
    ans = numeric(length(x))
    for(i in seq_along(x)) ans[[i]] = f(x[[i]])
    return(ans)
})

基准测试:

#call a C function iteratively
system.time({ sapplyC =  .Call("sapply_oppC", myls) }) 
#   user  system elapsed 
#  0.048   0.000   0.047 

#evaluate an R closure iteratively
system.time({ sapplyRC =  .Call("sapply_oppR", myls, "oppR") }) 
#   user  system elapsed 
#  3.348   0.000   3.358 

#evaluate an R builtin iteratively
system.time({ sapplyRCprim =  .Call("sapply_oppR", myls, "-") }) 
#   user  system elapsed 
#  0.652   0.000   0.653 

#loop with a R closure
system.time({ forR = for_oppR(myls, "oppR") })
#   user  system elapsed 
#  4.396   0.000   4.409 

#loop with an R builtin
system.time({ forRprim = for_oppR(myls, "-") })
#   user  system elapsed 
#  1.908   0.000   1.913 

#for reference and testing 
system.time({ sapplyR = unlist(lapply(myls, oppR)) })
#   user  system elapsed 
#  7.080   0.068   7.170 
system.time({ sapplyRprim = unlist(lapply(myls, `-`)) }) 
#   user  system elapsed 
#  3.524   0.064   3.598 

all.equal(sapplyR, sapplyRprim)
#[1] TRUE 
all.equal(sapplyR, sapplyC)
#[1] TRUE
all.equal(sapplyR, sapplyRC)
#[1] TRUE
all.equal(sapplyR, sapplyRCprim)
#[1] TRUE
all.equal(sapplyR, forR)
#[1] TRUE
all.equal(sapplyR, forRprim)
#[1] TRUE

(遵循均值查找的原始示例):

#all computations in C
all_C = inline::cfunction(sig = c(R_ls = "list"), body = '
    SEXP tmp, ans;
    PROTECT(ans = allocVector(REALSXP, LENGTH(R_ls)));

    double *ptmp, *pans = REAL(ans);

    for(int i = 0; i < LENGTH(R_ls); i++) {
        pans[i] = 0.0;

        PROTECT(tmp = coerceVector(VECTOR_ELT(R_ls, i), REALSXP));
        ptmp = REAL(tmp);

        for(int j = 0; j < LENGTH(tmp); j++) pans[i] += ptmp[j];

        pans[i] /= LENGTH(tmp);

        UNPROTECT(1);
    }

    UNPROTECT(1);
    return(ans);
')

#a very simple `lapply(x, mean)`
C_and_R = inline::cfunction(sig = c(R_ls = "list"), body = '
    SEXP call, ans, ret;

    PROTECT(call = allocList(2));
    SET_TYPEOF(call, LANGSXP);
    SETCAR(call, install("mean"));

    PROTECT(ans = allocVector(VECSXP, LENGTH(R_ls)));
    PROTECT(ret = allocVector(REALSXP, LENGTH(ans)));

    for(int i = 0; i < LENGTH(R_ls); i++) {
        SETCADR(call, VECTOR_ELT(R_ls, i));
        SET_VECTOR_ELT(ans, i, eval(call, R_GlobalEnv));
    }

    double *pret = REAL(ret);
    for(int i = 0; i < LENGTH(ans); i++) pret[i] = REAL(VECTOR_ELT(ans, i))[0];

    UNPROTECT(3);
    return(ret);
')                    

R_lapply = function(x) unlist(lapply(x, mean))                       

R_loop = function(x) 
{
    ans = numeric(length(x))
    for(i in seq_along(x)) ans[i] = mean(x[[i]])
    return(ans)
} 

R_loopcmp = compiler::cmpfun(R_loop)


set.seed(007); myls = replicate(1e4, runif(1e3), simplify = FALSE)
all.equal(all_C(myls), C_and_R(myls))
#[1] TRUE
all.equal(all_C(myls), R_lapply(myls))
#[1] TRUE
all.equal(all_C(myls), R_loop(myls))
#[1] TRUE
all.equal(all_C(myls), R_loopcmp(myls))
#[1] TRUE

microbenchmark::microbenchmark(all_C(myls), 
                               C_and_R(myls), 
                               R_lapply(myls), 
                               R_loop(myls), 
                               R_loopcmp(myls), 
                               times = 15)
#Unit: milliseconds
#            expr       min        lq    median        uq      max neval
#     all_C(myls)  37.29183  38.19107  38.69359  39.58083  41.3861    15
#   C_and_R(myls) 117.21457 123.22044 124.58148 130.85513 169.6822    15
#  R_lapply(myls)  98.48009 103.80717 106.55519 109.54890 116.3150    15
#    R_loop(myls) 122.40367 130.85061 132.61378 138.53664 178.5128    15
# R_loopcmp(myls) 105.63228 111.38340 112.16781 115.68909 128.1976    15
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

“*apply”家族真的没有矢量化吗? 的相关文章

  • R:ifelse 中的字符串列表

    我正在寻找与 MySQL 中的 where var in 语句类似的东西 我的代码如下 data lt data frame id 10001 10030 cc1 rep c a b c 10 attach data data new lt
  • 如何在R中匹配具有相同主键的两个表中的数据

    我有两个表 其中包含有关人员的数据 df1 lt data frame id c 113 202 377 288 359 name c Alex Silvia Peter Jack Jonny 这为我提供了 id name 1 113 Al
  • JavaFX 中 WebView 的性能

    我有一个 HTML5 UI 和一个 Java 后端 并且希望避免在纯 java 中重建 HTML ui 所以我的想法是运行本地 Web 服务器并使用 WebView 在 本机 窗口中呈现它 解决方案似乎是使用可以嵌入到 swing 中的 J
  • 循环遍历数组并删除项目,而不中断 for 循环

    我有以下 for 循环 当我使用splice 要删除一个项目 我发现 秒 未定义 我可以检查它是否未定义 但我觉得可能有一种更优雅的方法来做到这一点 我们的愿望是简单地删除一个项目并继续 for i 0 len Auction auctio
  • 修改linux下的路径

    虽然我认为我已经接近 Linux 专业人士 但显然我仍然是一个初学者 当我登录服务器时 我需要使用最新版本的R 统计软件 R 安装在 2 个地方 当我运行以下命令时 which R I get usr bin R 进而 R version
  • 空间数据xyz到矩阵

    我有一个大数据框 100 000 行 其中包含 LON LAT VALUE 我想将其转换为矩阵 EPSG 中的坐标 3035 我使用以下命令尝试了 reshape2 包 acast df lon lat value var value 效果
  • dplyr 中的标准评估:全局环境中的函数出现“无法找到函数”错误

    我试图在 dplyr 中对全局环境中的函数使用标准评估 但出现 无法找到函数 错误 这是一些代码 create data frame df lt data frame x rnorm 10 y rnorm 10 define arbitra
  • 通过 Shiny 中的串扰将 Plotly 与 DT 结合使用

    我正在编写一个应用程序来将 csv 文件读取为闪亮的并将散点图与 DT 表链接起来 我几乎遵循了 Plotly 网站上 DT 数据表上的示例 https plot ly r datatable https plot ly r datatab
  • 按组计算连续行中的值之间的差异

    这是我的一个df 数据框 group value 1 10 1 20 1 25 2 5 2 10 2 15 我需要按组计算连续行中的值之间的差异 所以 我需要一个结果 group value diff 1 10 NA because the
  • 如果条目出现次数少于 x 则删除数据框中的行

    我有以下数据框 称之为 df 它是由三个向量组成的数据框 姓名 年龄 和 邮政编码 df Name Age ZipCode 1 Joe 16 60559 2 Jim 20 60637 3 Bob 64 94127 4 Joe 23 9412
  • 使用 ggplot 构面时增加闪亮的绘图大小

    有没有办法增加绘图窗口的大小shiny取决于在一个中使用的面的数量ggplot图 也许使用垂直滚动 例如 使用下面的示例 当输入为 A 有三个方面 情节看起来不错 当选项 B 选择绘图数量会增加 但绘图窗口保持相同大小 导致绘图太小 是否有
  • matlab中无限while嵌套在for循环中

    我想做一个while循环 嵌套在for在 Matlab 中循环以查找数据中不同对之间的距离 我的数据具有以下形式 ID lon lat time 1 33 56 40 89 803 2 32 45 41 03 803 3 35 78 39
  • 如何从 R 数据框中提取关键字

    我是 R 中文本挖掘的新手 我想从数据框的列中删除停用词 即提取关键字 并将这些关键字放入新列中 我尝试制作一个语料库 但它对我没有帮助 df C3是我目前拥有的 我想添加栏目df C4 但我无法让它工作 df lt structure l
  • 如何最大限度地提高服务器性能?

    我一直在努力了解性能和可扩展性 并想知道开发人员 系统管理员正在做什么来提高他们的系统的效率 为了标准化答案 如果您能尽力回答以下任一问题 将会有所帮助 Profile Magazine publication on Joomla Jobs
  • 迭代列表的奇怪速度差异

    我创建了两个重复两个不同值的长列表 在第一个列表中 值交替出现 在第二个列表中 一个值出现在另一个值之前 a1 object object 10 6 a2 a1 2 a1 1 2 然后我迭代它们 不对它们执行任何操作 for in a1 p
  • 合并数据框而不重复行

    我想合并两个数据框 但如果有多个匹配项 则不想重复行 相反 我想总结一下那天的观察结果 来自 合并 提取两个数据框中与指定列匹配的行并将其连接在一起 如果有多个匹配项 则所有可能的匹配项各贡献一行 这是一些示例代码 days lt as d
  • 在 C 中复制两个相邻字节的最快方法是什么?

    好吧 让我们从最明显的解决方案开始 memcpy Ptr const char a b 2 调用库函数的开销相当大 编译器有时不会优化它 我不会依赖编译器优化 但即使 GCC 很聪明 如果我将程序移植到带有垃圾编译器的更奇特的平台上 我也不
  • 计算互相关函数?

    In R 我在用ccf or acf计算成对互相关函数 以便我可以找出哪个移位给我带来最大值 从它的外观来看 R给我一个标准化的值序列 Python 的 scipy 中是否有类似的东西 或者我应该使用fft模块 目前 我正在这样做 xcor
  • 从 R 中的方差分析 (glm) 中提取残余偏差

    我在 R 中安装了一个 glm 模型并采用了方差分析表 我需要提取 残余偏差 列 但它会产生错误 以下是代码 创建数据 counts lt c 18 17 15 20 10 20 25 13 12 outcome lt gl 3 1 9 t
  • 如何绘制大时间序列(数千次给药次数/药物剂量)?

    我正在尝试绘制医院中如何开出单一药物的图解 在这个虚拟数据库中 我在 2017 年 1 月 1 日之后遇到了 1000 名患者 绘图的目的是了解该药物的给药模式 在接近入院 出院或患者住院期间是否更频繁 高剂量给药 Get random d

随机推荐

  • 如何在 Perl 中解析 XML 并创建树结构

    我正在解析 XML 文件XML Simple 有没有办法从 XML 中获取树形形式 如果是这样 请举例说明或建议 CPAN 包 我想知道之后我必须处理哪个标签column等等 标签没有顺序 这column标签可以出现在Table or di
  • 用于检索各种日期范围内的 SUM 的 SQL 查询

    我有一个表格 其中包含有关已售产品 客户 购买日期和已售单位摘要的信息 我想要得到的结果应该是 4 行 其中前三行是一月 二月和三月 最后一行是这 3 个月内未售出的产品 这是桌子 http imageshack us a img823 8
  • 不能对承诺式任务调用 Start。异常即将到来

    我正在创建一个简单的 wpf 桌面应用程序 UI 只有一个按钮和 cs 文件中的代码 例如 private void Button Click 2 object sender RoutedEventArgs e FunctionA publ
  • 随机数类内初始化

    我目前正在创建一个类 我希望每次创建对象时都用随机数初始化其中一个私有成员 下面的代码不会产生任何问题 private unsigned random rand 10 不过 我想使用 C 11 随机引擎和发行版来执行此操作 我希望能够按照以
  • 如何在android中的asynctask中检查互联网连接

    Override protected void onCreate Bundle savedInstanceState TODO Auto generated method stub super onCreate savedInstanceS
  • Woocommerce 多个结账页面

    所以我一直在用头撞我的电脑 试图弄清楚如何让它工作 并且想知道这是否可能 只要做一些工作 一切皆有可能 我的最终目标是拥有多个包含 Woocommerce 结帐表单的页面 以便我可以拥有一个用于自定义单页订阅结帐的页面 当我通过设置面板将结
  • SurfaceTexture 的 onFrameAvailable() 方法总是调用得太晚

    我正在尝试让以下 MediaExtractor 示例正常工作 http bigflake com mediacodec ExtractMpegFramesTest java 需要 4 1 API 16 我遇到的问题是 outputSurfa
  • 使用最近邻缩放图像

    我一直在尝试让我的程序放大图像 我在为缩放图像分配新空间时遇到一些问题 但我认为它已经解决了 我遇到的问题是 当我尝试从临时内存持有者发回图像时 程序崩溃了 加载的图像放置在我的struct Image 像素被放置在img gt pixel
  • jQuery:查找特定父级之前的所有父级

    jQuery 中是否有一个内置函数可以让我将所有父级添加到具有特定 ID 的父级 我有一个深度嵌套的无序列表 如果我有对 li 之一的引用 我需要找到所有父 li 直到根 ul 如果我使用parents 它会给我所有的父母直到文档的根目录
  • 使用 iPhone sdk 编辑 PDF

    我想在现有的 pdf 上添加一些图像 单击这些图像后我应该能够 显示一些动画或能够播放音乐 是否可以这样做 我使用 pageCurlUp 动画逐页显示 pdf 但我不知道如何使用外部图像显示 pdf 不同页面和不同位置会有不同的图像 请指导
  • 当不通过 ObjectMapper 时,如何在 JsonParser 上设置 ObjectCodec?

    注意 这是使用 Jackson 2 3 2 为了满足我的一个项目的需要 我正在编写一个自定义的JsonParser其中记录了一个Map钥匙在哪里JsonPointers 和值是Integers 指针指向的行号 该类被命名为LineRecor
  • Ionic 3 延迟加载使大型 html 文件出现滞后

    我在我的项目中使用 ionic 3 但在延迟加载方面遇到了一些问题 我有一个ResultPage与模板resultpage html有超过1000html 行代码 在里面HomePage我想导航到ResultPage by navCtrl
  • Bash - 简单问号(?)的含义

    我正在尝试一些 bash 功能 当我尝试回显一些输出时 我注意到 echo what about in some more complex example 结果是 在一些更复杂的例子中怎么样 我知道转义问号或整行引号可以解决问题 但我很好奇
  • Xcode 未找到匹配的私钥

    我有一个从另一台 Mac 分发的应用程序 我需要从另一台 Mac 获得什么才能将我的应用程序存档以供上传 以及我需要在 Xcode 路径中进行哪些更改才能使所有工作正常进行 谢谢 抱歉英语不好 如果您使用某人的证书 您还必须获取与该证书关联
  • 什么是词向量维度

    我目前是深度学习的业余爱好者 正在这个网站上阅读有关 word2vector 的内容https www kaggle com c word2vec nlp tutorial details part 3 more fun with word
  • 是否有一个函数可以在给定索引号的情况下生成特定的 n Multichoose r 组合?

    例如 3 multichoose 2 有以下组合 i combo 0 0 0 1 0 1 2 0 2 3 1 1 4 1 2 5 2 2 是否可以编写一个参数为 n r i 的函数并返回有问题的组合 而不迭代之前的每个组合 是否可以编写一个
  • 从控制器 CakePHP 3.x 执行 shell

    我在 CakePHP Shell 中有一个特定任务 它由 CRON 作业执行 但我希望用户能够随时从网络界面 如按钮或类似的东西 执行它 所以 我的问题是 是否可以从控制器执行 shell 在控制器中模拟这一点 bin cake MyShe
  • 获取两个地理点之间的距离

    我想制作一个应用程序来检查用户所在的最近位置 我可以轻松获取用户的位置 并且我已经有了带有纬度和经度的地点列表 了解列表中与当前用户位置最近的位置的最佳方法是什么 我在 google API 中找不到任何内容 Location loc1 n
  • Laravel 如何处理 PHP 警告?

    我正在尝试使用 Laravel 连接到 LDAP 服务器 重要的是我正在使用 PHP 函数ldap connect and ldap bind而不是使用包来处理它 关键是当我提供错误的用户名和密码时 ldap bind函数给我们一个 PHP
  • “*apply”家族真的没有矢量化吗?

    所以我们习惯对每个 R 新用户说 apply没有矢量化 看看 Patrick BurnsR地狱圈 4 其中说 我引用 常见的反应是使用 apply 系列中的函数 这不是 向量化 它是循环隐藏的 apply 函数有一个 for 循环 它的定义