nleqslv data.table的性能

时间:2015-03-07 03:15:53

标签: r data.table

我从 data.table 在大数据分析中的高性能中受益匪浅。但是,最近我注意到 data.table 中的 nleqslv 效率不如 data.frame 效率高。不确定是否是因为我使用它的方式。这是一个例子。

library(nleqslv)
library(data.table)
s<-1000
# data.frame
df<-data.frame(pd=rbeta(s,2.5,15),ce=runif(s,0,1))
fn_df<-function(x){ sum(pnorm(qnorm(df[,'pd'])-z*x)*df[,'ce'])-50}

# data.table
dt<-as.data.table(df)
fn_dt<-function(x){ sum(pnorm(qnorm(dt[,pd])-z*x)*dt[,ce])-50}

z<-rnorm(s)
system.time(df_ob<-nleqslv(1,fn_df))
system.time(dt_ob<-nleqslv(1,fn_dt))


> system.time(df_ob<-nleqslv(1,fn_df))
user  system elapsed 
0.032   0.000   0.033
> system.time(dt_ob<-nleqslv(1,fn_dt))
user  system elapsed 
0.092   0.000   0.089  

结果表明 nleqslv data.table 中运行得慢得多。我想知道是否有办法让 nleqslv data.table 中运行得更快,或者至少和 data.frame 一样快。< / p>

2 个答案:

答案 0 :(得分:2)

这里的任务只是对列进行子集化。与data.frames或list相比,使用data.tables没有任何真正的优势,如果这就是你所做的一切。顺便说一下,它的执行速度至少和data.frames一样快,但是[.data.table[.data.frame做的要多得多(只看看这两个函数),这会增加一点点开销,这是显而易见的重复访问时

理解data.tables并不能让你更快地使用它们,这一点非常重要。你必须正确理解和使用它。例如,DT[, j]DT[, j, with=FALSE]其中j是一列或一组列,会返回子集化列的副本。这是因为data.table的引用语义 - 否则通过引用更新一个data.table也会影响另一个。

因此,在您的示例中,使用$[[,因为您将单个列的子集更好,因为它不使用[.data.table

system.time(for (i in 1:1e3) df[, 'pd'])
system.time(for (i in 1:1e3) dt[, pd])

system.time(for (i in 1:1e3) df$pd) 
system.time(for (i in 1:1e3) dt$pd)

system.time(for (i in 1:1e3) df[['pd']])
system.time(for (i in 1:1e3) dt[['pd']])

#       Code      Time (sec)
# df[, 'pd']           0.008
#   dt[, pd]           0.303
#      df$pd           0.008
#      dt$pd           0.007
# df[['pd']]           0.008
# dt[['pd']]           0.006

同样,如果您想重复分配多个列,那么as.list(dt)[cols]会更有效率。 data.table未针对列类型操作的重复子集设计/优化(尽管在导出shallow()时可以解决复制部分。)

如果您正在执行此操作,甚至超过data.frames,将它们设为lists会使其效率更高,因为list是原始类型,并且不会t的开销为[.data.frame偶数。

ll = as.list(df)
system.time(for (i in 1:1e3) ll[['pd']])
#    user  system elapsed 
#       0       0       0 

HTH

答案 1 :(得分:1)

正如我在评论nleqslv中所说,找不到解决问题的方法。 你应该检查你的函数是否有一个解= 0。

试试这个:

s<-1000
# data.frame
df<-data.frame(pd=rbeta(s,2.5,15),ce=runif(s,0,1))
fn_df<-function(x){ sum(pnorm(qnorm(df[,'pd'])-z*x)*df[,'ce'])-50}

z<-rnorm(s)

fn_df(0)
fv <- Vectorize(fn_df,"x")
curve(fv,from=-10,to=10)

并且curve制作的图表清楚地表明函数fn_df的最小值位于0或{0}附近fn_df(0) = 21.2253。因此,fn_df的函数fn_df(x)=0 无解

如果您的函数中的50更改为90,则函数的最小值仍为0或接近0 fn_df(0) = -19.65275,表示存在fn_df(x) = 0的解。喜欢这个

fn_df<-function(x){ sum(pnorm(qnorm(df[,'pd'])-z*x)*df[,'ce'])-90}

让我们来研究你的等式。 您的函数有一个标量参数,因此您可以尝试uniroot。 喜欢这个

uniroot(fn_df, c(-.5,5))

使用此输出(删除空白行以节省空间):

$root
[1] 0.7711256
$f.root
[1] -3.789903e-05
$iter
[1] 8
$init.it
[1] NA
$estim.prec
[1] 6.103516e-05

您遇到的困难是为x找到合适的范围,以使两个端点的函数值符号不同。

所以你可以试试这样的nleqslv

library(nleqslv)
nleqslv(1,fn_df)

使用此结果(删除输出中的空行以节省空间)

$x
[1] 0.7711265
$fvec
[1] 3.268497e-12
$termcd
[1] 1
$message
[1] "Function criterion near zero"
$scalex
[1] 1
$nfcnt
[1] 4
$njcnt
[1] 1
$iter
[1] 4

unirootnleqslv找到的解决方案非常接近。

最后, nleqslv在您的示例中速度较慢,但​​您使用data.table的方式较慢。我无法帮助你。