data.table 的 nleqslv 性能

nleqslv performance for data.table

data.table在大数据分析方面的高性能让我受益匪浅。然而,最近我注意到 nleqslvdata.table 中的效率不如 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 运行s 在 data.table 中慢得多。我想知道是否有办法让 nleqslv 运行 在 data.table 中更快,或者至少和在 data.table 中一样快data.frame.

正如我在评论中所说 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 fn_df(0) = 21.2253。所以你的函数 fn_df 没有解决方案 fn_df(x)=0.

如果函数中的 50 更改为 90,则函数的最小值仍然位于或接近 0,fn_df(0) = -19.65275 暗示存在 [=25= 的解决方案].像这样

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

现在让我们研究求解方程式。 您的函数只有一个标量参数,因此您可以尝试 uniroot。 像这样

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

使用此输出(删除空行以保存 space):

$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)

有了这个结果(再次删除输出中的空行以保存 space)

$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 的方式却很慢。我帮不了你。

您的任务只是对列进行子集化。与 data.frames 或列表相比,使用 data.tables 并没有真正的优势,如果仅此而已。令人欣慰的是,它的执行速度至少与 data.frames 一样快,但 [.data.table 的执行速度比 [.data.frame 快得多(只要看看这两个函数)并且会增加一些开销,这重复访问时很明显。

了解 data.tables 不会让您使用它们的所有东西都更快,这一点非常重要。你必须理解并正确使用它。例如,DT[, j] returns 子集列的 copy。这是因为 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 是原始类型,并且甚至没有 [.data.frame 的开销。

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

HTH