tapyu
August 3, 2022, 5:26am
1
Consider the following tensor
A = reshape(1:24,4,2,3)
I want to split it into two elements: B, which is the second frontal of A; and C, which is the remaining elements. Getting B is pretty obvious,
B = A[:,:,2]
But I did not find a clever way to get C since functions such as popat!()
and deleteat!()
do not work for high-order arrays.
popat!(A, :, :, 2)
ERROR: MethodError: no method matching popat!(::Array{Int64, 3}, ::Colon, ::Colon, ::Int64)
Closest candidates are:
popat!(::Vector, ::Integer, ::Any) at /usr/share/julia/base/array.jl:1296
Stacktrace:
[1] top-level scope
@ REPL[58]:1
PS: For those who don’t know what a frontal is, here it is:
tapyu
August 3, 2022, 5:41am
2
I just found the answer, I didn’t know that it is possible to select an element like that is julia
B = A[:,:,2]
C = A[:,:,1:end.!=2]
you can also do:
using InvertedIndices
A[:, :, Not(2)]
3 Likes
tapyu
August 4, 2022, 11:22pm
4
That is nice but is not built-in
Nothing wrong with a small dependency. That’s why packages exist.
FWIW InvertedIndices.jl has no dependencies so it really is super light weight.
tapyu
August 5, 2022, 7:20am
6
That is not the point. IMHO, the built-in solution solve it in a idiomatic way, making InvertedIndices
unnecessary for this purpose. That is why I put my comment as solution.
InvertedIndices
is also much slower than the builtin solution:
julia> A = rand(10, 10, 10);
julia> @btime $A[:, :, (1:end) .!= 2];
1.457 μs (3 allocations: 7.28 KiB)
julia> @btime $A[:, :, Not(2)];
4.558 μs (48 allocations: 8.62 KiB)
julia> A = rand(100, 100, 1000);
julia> @btime @view($A[:, :, (1:end) .!= 2]);
2.210 μs (4 allocations: 12.34 KiB)
julia> @btime @view($A[:, :, Not(2)]);
272.543 μs (5978 allocations: 179.41 KiB)
Not sure if these performance issues are fundamental or can be improved.
1 Like
On my PC the following is even better:
@btime @view($A[:, :, [1;3:end]]);
tapyu
August 5, 2022, 9:37am
9
The drawback is that you cannot make it generic, while you can change 2
in A[:, :, (1:end) .!= 2])
by a variable.
I think you can and it’s still faster:
n = 4
@btime @view($A[:, :, [1:($n-1);($n+1):end]]);
2 Likes
Below is a generalization that does not penalize the performance of the solution found by @rafael.guerra
n=[3,5,9]
@btime @view($A[:, :, deleteat!(collect(1:size($A,ndims($A))),$n)]);
here a function that takes advantage of the fact that the set of indices to be excluded is ordered.
@btime @view($A[:, :, notin($A,$n)]);
function notin(A,n)
j=1
k=1
s=size(A,ndims(A))
sn=length(n)
nin=Vector{Int64}(undef, s-sn)
for i in 1:s
if i!=n[k]
nin[j]=i
j+=1
else
if k==sn
copyto!(nin,j,1:s,i+1,s-i)
break
else
k+=1
end
end
end
nin
end
You should file an issue. I would expect Not
to be faster.