你如何在 Julia 中对数组进行条件赋值?
How do you perform conditional assignment in arrays in Julia?
Octave我可以做到
octave:1> A = [1 2; 3 4]
A =
1 2
3 4
octave:2> A(A>1) -= 1
A =
1 1
2 3
但在 Julia 中,等效语法不起作用。
julia> A = [1 2; 3 4]
2x2 Array{Int64,2}:
1 2
3 4
julia> A[A>1] -= 1
ERROR: `isless` has no method matching isless(::Int64, ::Array{Int64,2})
in > at operators.jl:33
如何在 Julia 中有条件地为特定数组或矩阵元素赋值?
您的问题不在于作业本身,而是 A > 1
本身不起作用。您可以使用 elementwise A .> 1
代替:
julia> A = [1 2; 3 4];
julia> A .> 1
2×2 BitArray{2}:
false true
true true
julia> A[A .> 1] .-= 1000;
julia> A
2×2 Array{Int64,2}:
1 -998
-997 -996
更新:
请注意,在现代 Julia(>=0.7)中,我们需要使用 .
来表示我们要广播操作(此处,减去标量 1000)以匹配过滤后的大小目标在左边。 (最初提出这个问题时,我们需要 A .> 1
中的点,但 .-=
中不需要。)
要使其在 Julia 1.0 中工作,需要将 =
更改为 .=
。换句话说:
julia> a = [1 2 3 4]
julia> a[a .> 1] .= 1
julia> a
1×4 Array{Int64,2}:
1 1 1 1
否则你会得到类似
的东西
ERROR: MethodError: no method matching setindex_shape_check(::Int64, ::Int64)
在 Julia v1.0 中,您可以使用 replace!
函数来代替逻辑索引,并获得相当大的加速:
julia> B = rand(0:20, 8, 2);
julia> @btime (A[A .> 10] .= 10) setup=(A=copy($B))
595.784 ns (11 allocations: 4.61 KiB)
julia> @btime replace!(x -> x>10 ? 10 : x, A) setup=(A=copy($B))
13.530 ns ns (0 allocations: 0 bytes)
对于较大的矩阵,差异徘徊在 10 倍加速。
加速的原因是逻辑索引解决方案依赖于创建中间数组,而 replace!
避免了这一点。
一种稍微简洁的写法是
replace!(x -> min(x, 10), A)
虽然 min
似乎没有任何加速。
这是另一个几乎一样快的解决方案:
A .= min.(A, 10)
这也避免了分配。
Octave我可以做到
octave:1> A = [1 2; 3 4]
A =
1 2
3 4
octave:2> A(A>1) -= 1
A =
1 1
2 3
但在 Julia 中,等效语法不起作用。
julia> A = [1 2; 3 4]
2x2 Array{Int64,2}:
1 2
3 4
julia> A[A>1] -= 1
ERROR: `isless` has no method matching isless(::Int64, ::Array{Int64,2})
in > at operators.jl:33
如何在 Julia 中有条件地为特定数组或矩阵元素赋值?
您的问题不在于作业本身,而是 A > 1
本身不起作用。您可以使用 elementwise A .> 1
代替:
julia> A = [1 2; 3 4];
julia> A .> 1
2×2 BitArray{2}:
false true
true true
julia> A[A .> 1] .-= 1000;
julia> A
2×2 Array{Int64,2}:
1 -998
-997 -996
更新:
请注意,在现代 Julia(>=0.7)中,我们需要使用 .
来表示我们要广播操作(此处,减去标量 1000)以匹配过滤后的大小目标在左边。 (最初提出这个问题时,我们需要 A .> 1
中的点,但 .-=
中不需要。)
要使其在 Julia 1.0 中工作,需要将 =
更改为 .=
。换句话说:
julia> a = [1 2 3 4]
julia> a[a .> 1] .= 1
julia> a
1×4 Array{Int64,2}:
1 1 1 1
否则你会得到类似
的东西ERROR: MethodError: no method matching setindex_shape_check(::Int64, ::Int64)
在 Julia v1.0 中,您可以使用 replace!
函数来代替逻辑索引,并获得相当大的加速:
julia> B = rand(0:20, 8, 2);
julia> @btime (A[A .> 10] .= 10) setup=(A=copy($B))
595.784 ns (11 allocations: 4.61 KiB)
julia> @btime replace!(x -> x>10 ? 10 : x, A) setup=(A=copy($B))
13.530 ns ns (0 allocations: 0 bytes)
对于较大的矩阵,差异徘徊在 10 倍加速。
加速的原因是逻辑索引解决方案依赖于创建中间数组,而 replace!
避免了这一点。
一种稍微简洁的写法是
replace!(x -> min(x, 10), A)
虽然 min
似乎没有任何加速。
这是另一个几乎一样快的解决方案:
A .= min.(A, 10)
这也避免了分配。