朱莉娅优化

时间:2021-06-23 21:22:52

标签: dataframe julia

我正在尝试在 Julia 中运行引导程序并编写了一个工作引导程序函数。但是,它很慢,并且 R 中的相同代码运行时间减半。我确信我的代码中一定存在一些低效问题,我对使用 Julia 非常陌生。我想知道是否有人可以为我提供一些建议/建议。

这是完全可重现的代码

using DataFrames
using Statistics
using StatsBase

df = DataFrame(rand(1:9, 1000,1000), :auto); # Create data

# Bootstrap function
function bootstrap(;iters=1, data=nothing, statistic=nothing)
    statArr = DataFrame() # Init empty dataframe
    for i in 1:iters
        data_sample = data[sample(1:nrow(data), nrow(data), replace=true), :] # sample the data with replacement
        stat = statistic(data_sample)
        append!(statArr, stat) # push dataframe to empty dataframe
    end
    return statArr
end;

# Statistic function for column means
function meanmap(data)
    return mapcols(col -> mean(col), data)
end;

# Run the bootstrap on the data
@time bootDist = bootstrap(iters = 9999, data = df, statistic = meanmap);

这大约需要 68 秒才能运行,而在 R 中需要 35 秒。

非常感谢您的建议。 谢谢。

1 个答案:

答案 0 :(得分:3)

使用 DataFrames.jl 你可以做例如:

function bootstrap(;iters=1, data=nothing, statistic=nothing)
    statArr = Float64.(empty(data)) # Init empty dataframe
    for i in 1:iters
        stat = statistic(data, rand(1:nrow(data), nrow(data)))
        push!(statArr, stat) # push row to empty dataframe
    end
    return statArr
end;

# Statistic function for column means
meanmap(data, sel) = [mean(@view x[sel]) for x in eachcol(data)]

哪个应该比 R 快。变化是:

  • 主要:使用视图而不是在每次迭代中复制所有内容
  • 次要:不要为每个引导复制创建一个数据框,而是创建一个向量并push!它而不是append!对其进行julia> x = rand(1:nrow(df), nrow(df)); julia> y = df[!, 1]; julia> f(y, x) = mean(@view y[x]); julia> g(y, x) = [f(y, x) for _ in 1:9999*1000]; julia> @time g(y, x); (这样可以节省创建和验证数据框对象的时间)

(我只对代码进行了主要优化;还有一些额外的小优化可以进行,但它们不应显着影响运行时间)

另请注意,您接近最大执行速度为:

rand(1:nrow(data), nrow(data))

大致是您可以预期的执行时间下限,它并不比上面的代码快多少(当然,它快了 25%-30%,因为它的工作量更少,CPU 也更多缓存友好)。


作为一个小评论,显示在这种情况下细节的重要性(我认为这很有趣,虽然这是一个小优化,所以我没有考虑)。

如果您使用 sort!(rand(1:nrow(data), nrow(data))) 而不是 mean,您可以多节省 1 秒。原因是通过这种方式,您可以确保在计算 mean 时按顺序访问数据(这对 CPU 缓存更友好,并且 -t 不受观察顺序的影响)。

像这样的第二个评论是在多 CPU 机器上(并使用 function bootstrap(;iters=1, data=nothing, statistic=nothing) statArr = Float64.(empty(data)) # Init empty dataframe tmp = Vector{Any}(undef, iters) Threads.@threads for i in 1:iters stat = statistic(data, rand(1:nrow(data), nrow(data))) tmp[i] = stat end for v in tmp push!(statArr, v) # push dataframe to empty dataframe end return statArr end 开关选择使用多个线程启动 Julia)可以使用线程来加速这样的事情(再次 - 我做到了不是将这里的东西优化到最后一次可能的调整,而是想展示主要思想):

if ( testvalue[i] != "HRZ") {

这在 Julia 中实现起来更快也更容易(虽然可行,但在 R 中不那么容易)。


关于视图,您可以阅读here