data.table 的 nleqslv 性能
nleqslv performance for data.table
data.table在大数据分析方面的高性能让我受益匪浅。然而,最近我注意到 nleqslv 在 data.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
uniroot
和nleqslv
找到的解非常接近。
最后,而非 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
data.table在大数据分析方面的高性能让我受益匪浅。然而,最近我注意到 nleqslv 在 data.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
uniroot
和nleqslv
找到的解非常接近。
最后,而非 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