【问题标题】:Unpacking return of map in Julia在 Julia 中解包返回地图
【发布时间】:2020-04-06 14:33:39
【问题描述】:

我有一个返回数组的函数。我想将函数映射到输入向量,输出是所有数组的简单串联。功能是:

function log_it(r, bzero = 0.25, N = 400)
    main = rand(Float16, (N+150));
    main[1] = bzero;
    for i in 2:N+150
        main[i] = *(r, main[i-1], (1-main[i-1]))
    end;
    y = unique(main[(N+1):(N+150)]);
    r_vec = repeat([r], size(y)[1]);
    hcat(r_vec, y)
end;

我可以很好地映射它:

map(log_it, 2.4:0.001:2.405)

但结果很糟糕:

 [2.4 0.58349609375]
 [2.401 0.58349609375]
 [2.402 0.583984375; 2.402 0.58349609375]
 [2.403 0.583984375]
 [2.404 0.583984375]
 [2.405 0.58447265625; 2.405 0.583984375]

注意,嵌套数组的长度是无限的 - 我正在寻找一种不依赖于预先知道嵌套数组长度的解决方案。

我想要的是这样的:

 2.4    0.583496
 2.401  0.583496
 2.402  0.583984
 2.402  0.583496
 2.403  0.583984
 2.404  0.583984
 2.405  0.584473
 2.405  0.583984

我使用 for 循环制作的:

results = Array{Float64, 2}(undef, 0, 2)
    for i in 2.4:0.001:2.405
        results = cat(results, log_it(i), dims = 1)
    end
    results

代码运行良好,但 for 循环的时间大约是原来的四倍。我也觉得 map 是正确的方法,我只是错过了一些东西——要么以返回一个不错的数组向量的方式执行 map,要么在数组的某些突变中“不嵌套”。我尝试过查看 flatten 和 collect 之类的功能,但找不到任何东西。

非常感谢!

【问题讨论】:

    标签: julia


    【解决方案1】:

    您确定您的基准测试正确吗?特别是对于非常快速的操作,基准测试有时会很棘手。作为一个起点,我建议确保您始终将要进行基准测试的任何代码包装到一个函数中,并使用BenchmarkTools 包来获得可靠的计时。

    在 Julia 中编写循环通常不会造成性能损失,因此与map 相比,循环的运行时间增加了 3 倍听起来很可疑。

    这是我得到的:

    julia> using BenchmarkTools
    
    julia> @btime map(log_it, 2.4:0.001:2.405)
    121.426 μs (73 allocations: 14.50 KiB)
    
    julia> function with_loop()
               results = Array{Float64, 2}(undef, 0, 2)
               for i in 2.4:0.001:2.405
                   results = cat(results, log_it(i), dims = 1)
               end
              results
           end
    
    julia> @btime with_loop()
    173.492 μs (295 allocations: 23.67 KiB)
    

    所以循环慢了大约 50%,但那是因为你分配了更多。

    当您使用map 时,通常使用broadcasting 来表达您正在做的事情的更加Julia 的方式。这适用于任何用户定义的函数:

    julia> @btime log_it.(2.4:0.001:2.405)
    121.434 μs (73 allocations: 14.50 KiB)
    

    相当于你的map 表达式。我认为您正在寻找的只是一种堆叠所有结果向量的方法 - 您可以使用 vcat 并为此进行喷溅:

    julia> @btime  vcat(log_it.(2.4:0.001:2.405)...)
    122.837 μs (77 allocations: 14.84 KiB)
    

    只是为了确认:

    julia> vcat(log_it.(2.4:0.001:2.405)...) == with_loop()
    true
    

    因此,在 map 解决方案的速度和内存成本下,使用广播和连接可以得到与循环相同的结果。

    【讨论】:

    • 喷溅正是我想要的,谢谢! OOI,有地图胜过广播的场合吗?
    • 不确定我是否会说它“胜过”它——速度应该是相似的(但可能取决于编译器内部,所以事情可能会改变!)。我建议你看看这里:julialang.org/blog/2017/01/moredots/#broadcast_vs_map(这是一个非常有用的整体,但你正在寻找的比较在最后)
    猜你喜欢
    • 2018-02-26
    • 2017-06-18
    • 1970-01-01
    • 2018-05-18
    • 1970-01-01
    • 2016-04-29
    • 1970-01-01
    • 2019-02-19
    • 1970-01-01
    相关资源
    最近更新 更多