我想获取一个数组x并将所有大于 5 的数字更改为 5。在一行中执行此操作的标准方法是什么?
下面是一些在几行中执行此操作的代码。这个关于逻辑索引的问题是相关的,但似乎涉及选择而不是分配。谢谢
x = [1 2 6 7]
for i in 1:length(x)
if x[i] >= 5
x[i] = 5
end
end
Run Code Online (Sandbox Code Playgroud)
期望的输出:
x = [1 2 5 5]
广播运算符.适用于任何函数,包括关系运算符,也适用于赋值。因此,直观的单行是:
x[x .> 5] .= 5
Run Code Online (Sandbox Code Playgroud)
这部分x .> 5广播> 5过x,导致指示大于5元件更大的这部分的布尔值的向量.= 5广播的分配5在所有元素指示通过x[x .> 5]。
但是,受到Benoit 下面非常酷的答案中显着加速的启发(请务必查看),我决定还添加一个优化变体并进行速度测试。上述方法虽然看起来非常直观,但并不是最佳的,因为它为索引分配了一个临时的布尔值数组。避免临时分配的(更多)最佳方法,并且作为奖励适用于任何谓词(条件)函数是:
function f_cond!(x::Vector{Int}, f::Function, val::Int)
@inbounds for n in eachindex(x)
f(x[n]) && (x[n] = val)
end
return x
end
Run Code Online (Sandbox Code Playgroud)
因此,使用此函数,我们将编写f_cond!(x, a->a>5, 5)which 分配5给条件(匿名)函数a->a>5评估为 的任何元素true。显然,这个解决方案不是一个简洁的单线解决方案,但请查看以下速度测试:
julia> using BenchmarkTools
julia> x1 = rand(1:10, 100);
julia> x2 = copy(x1);
julia> @btime $x1[$x1 .> 5] .= 5;
327.862 ns (8 allocations: 336 bytes)
julia> @btime f_cond!($x2, a->a>5, 5);
15.067 ns (0 allocations: 0 bytes)
Run Code Online (Sandbox Code Playgroud)
这速度快得可笑。此外,您可以替换Int为T<:Any. 鉴于加速,人们可能想知道是否有一个函数Base已经做到了这一点。单行是:
map!(a->a>5 ? 5 : a, x, x)
Run Code Online (Sandbox Code Playgroud)
虽然这比第一种方法显着加快了速度,但与第二种方法相差甚远。
顺便说一句,我觉得这一定是另一个 StackOverflow 问题的重复,但 5 分钟的搜索没有发现任何东西。
您也可以广播min:
x .= min.(x, 5)
Run Code Online (Sandbox Code Playgroud)
请注意,这比使用(稍微)更有效,x[x .> 5] .= 5因为它不分配布尔值的临时数组, x .> 5,并且可以自动向量化,只需一次通过内存(根据下面的奥斯卡评论):
julia> using BenchmarkTools
julia> x = [1 2 6 7] ; @btime $x .= min.($x, 5) ; # fast, no allocations
19.144 ns (0 allocations: 0 bytes)
julia> x = [1 2 6 7] ; @btime $x[$x .> 5] .= 5 ; # slower, allocates
148.678 ns (5 allocations: 304 bytes)
Run Code Online (Sandbox Code Playgroud)