R 方法在不使用apply函数的情况下对data.table的每一行进行操作

R 方法在不使用apply函数的情况下对data.table的每一行进行操作,r,data.table,R,Data.table,我在下面写了一个简单的函数: mcs 6)>0)、NA、sd(v))} 它应该取一个向量,对它进行排序,然后检查每个连续的差值是否大于6。如果差值大于6,则返回NA;如果差值不大于6,则返回标准偏差 我希望将此函数应用于数据表的所有行(仅选择某些列),然后将每行的返回值作为新的列条目附加到数据表中 例如,给定这样一个数据表 > dat <- data.table(A=c(1,2,3,4,5), B=c(2,3,4,10,6), C=c(3,4,10,6,8), D=c(3,3,

我在下面写了一个简单的函数:

mcs 6)>0)、NA、sd(v))}

它应该取一个向量,对它进行排序,然后检查每个连续的差值是否大于6。如果差值大于6,则返回NA;如果差值不大于6,则返回标准偏差

我希望将此函数应用于数据表的所有行(仅选择某些列),然后将每行的返回值作为新的列条目附加到数据表中

例如,给定这样一个数据表

> dat <- data.table(A=c(1,2,3,4,5), B=c(2,3,4,10,6), C=c(3,4,10,6,8),   
D=c(3,3,3,3,3))  
> dat  
   A  B  C D  
1: 1  2  3 3  
2: 2  3  4 3  
3: 3  4 10 3  
4: 4 10  6 3  
5: 5  6  8 3  
我了解到,可以使用以下方法对数据表执行按行操作:

> dat[, sd:=apply(.SD, 1, mcs), .SDcols=(c(2,3,4))]
除了速度太慢之外,这种方法是有效的。我必须在几个大型数据表上执行此操作,为此我编写了一个脚本。但是,它只适用于较小的数据表。对于约300000行的表,它会在几秒钟内完成,但当我尝试对约8亿行的表执行此操作时,我的程序不会完成。我试着等了两个小时,我想是游戏机坏了什么的,因为游戏机冻结了。我试过几次运行脚本,它总是正确地完成前几个较小的表(我让程序将表写入一个文件进行检查),但当它到达大数据表时,它永远不会完成。我在一个计算集群上运行这个,所以我绝对不认为这是一个硬件限制。可能是糟糕的代码

我假设瓶颈是在apply中完成的循环,但我不知道如何使它更快。我对R相当陌生,所以我不知道如何优化我的代码。我在网上看到了很多关于矢量化的帖子,我想如果我能同时将我的函数应用到每一行,速度会快得多,但我不知道怎么做。请帮忙

编辑
对不起,我复制我的
mcs
函数时出错。我已经更新了

编辑2

对于那些感兴趣的人,我最终把表分成两半,分别操作了一半,这对我来说是有效的。

如果你真的需要速度,最好还是用RCPP移动到C++,这就给了我们一个100X以上的解决方案。 资料 我确实制作了一些不同的示例数据来测试这一点,这些数据有1000行,而不是5行:

set.seed(123)
dat <- data.table(A = rnorm(1e3, sd=4), B = rnorm(1e3, sd=4), C = rnorm(1e3, sd=4),
                  D = rnorm(1e3, sd=4), E = rnorm(1e3, sd=4))
现在让我们进行基准测试:

benchmark(mcs1 = dat[, sd:=apply(.SD, 1, mcs1), .SDcols=(c(2,3,4))],
          mcs2 = dat[, sd:=mcs2(.SD), .SDcols=(c(2,3,4))],
          order = 'relative',
          columns = c('test', 'elapsed', 'relative', 'user.self'))


  test elapsed relative user.self
2 mcs2    0.19    1.000     0.183
1 mcs1   21.34  112.316    20.044
如何编译这段代码 < P>作为Rcpp通过C++代码的介绍,我建议Hadley Wickham的高级R。如果你打算做进一步的Rcpp,我强烈建议你也阅读官方文档和小插图,但是威克姆的书可能是一个更友好的初学者作为起点。出于您的目的,您只需要启动并运行Rcpp,以便编译上面的代码

为了让这段代码为您工作,如果您还没有Rcpp包,则需要它。您可以通过运行

install.packages(Rcpp)
注意,你还需要一个编译器;如果你使用的是基于Debian的Linux系统,比如Ubuntu,你可以运行

sudo apt install r-base-dev
从终点站。如果您使用的是Mac或Windows,请查看有关设置的说明,或查看上面链接的Wickham章节

<> >一旦安装了RCPP,将上面的C++代码保存到文件中。假设在我们的示例中,文件名为“SOanswer.cpp”。然后,通过在R脚本中添加以下两行代码,可以从R中使用其
mcs2()
函数:

library(Rcpp)
sourceCpp("SOanswer.cpp") # assuming the file is in your working directory

就这样!现在,您的R脚本可以调用
mcs2()
,并且运行得更快。如果你想了解更多关于Rcpp的信息,除了上面的Wickham一章之外,我还想从RStudio(RStudio也有很多链接,其中一些链接在这里)查阅参考手册和可用的小插曲,您还可以在周围找到一些非常有用的东西。

您应该仔细检查您的
mcs
函数,因为它的输出是不同的。应用于第一行
mcs(c(1,2,3,3)
的结果是
0.9574271
,而不是你发布的
0.5773503
。我能够通过函数
mcs2 6)、NA、sd(c(…)}
将计算时间缩短25%,并将其称为
dat[,sd2:=mapply(mcs2,B,c,D)]
。但是,几乎所有的收益都是通过使用排序方法“quick”实现的,该方法在10万行和5列上进行了测试。不过,我希望其他人能想出一个更快的替代方案,他们好奇地想看看其他解决方案。
mcs
在OP中使用
sum(diff(sort(v))>6
而不是
sum(diff(sort(v))>6
。此外,在
all.equal
中,“dat”的“sd”列第二次被覆盖(使用
mcs3
),因此,相同的对象被两次传递给
all.equal
。是否要编写一个RCPP变量来检查结果差异的sny是否也大于6?会对它的表现感到好奇。另外,我使用mapply调用函数msc2,而不是使用apply with.SD。这应该会提高一点性能。@Florian——是的,当你评论时,他正在研究它;现在添加了,谢谢你的帮助。我不熟悉在R中使用C++。我只是直接在同一个脚本中编写C++代码,还是需要单独编写C++文件?@ RealStestDead我会编辑答案,其中包含一些关于这个问题的说明。
install.packages(Rcpp)
sudo apt install r-base-dev
library(Rcpp)
sourceCpp("SOanswer.cpp") # assuming the file is in your working directory