我从 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>
答案 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
uniroot
和nleqslv
找到的解决方案非常接近。
最后,不 nleqslv
在您的示例中速度较慢,但您使用data.table
的方式较慢。我无法帮助你。