Add insertdims method which is inverse to dropdims#45793
Add insertdims method which is inverse to dropdims#45793LilithHafner merged 29 commits intoJuliaLang:masterfrom
Conversation
|
Haven't looked into the code since then. |
|
@mkitti had a great solution which has slightly differently semantics julia> function insertdims(A; dims)
idx = ntuple(ndims(A) + length(dims)) do i
if i ∈ dims
[CartesianIndex()]
else
(:)
end
end
@view A[idx...]
end
insertdims (generic function with 1 method)
julia> A = zeros(5,4,3);
julia> size(insertdims(A; dims = (1,3,6)))
(1, 5, 1, 4, 3, 1)
julia> dropdims(b, dims=(1,2))
2×2 Matrix{Int64}:
1 2
3 4
julia> b = insertdims(a, dims=(1,2))
1×1×2×2 Array{Int64, 4}:
[:, :, 1, 1] =
1
[:, :, 2, 1] =
3
[:, :, 1, 2] =
2
[:, :, 2, 2] =
4
julia> dropdims(b, dims=(1,2))
2×2 Matrix{Int64}:
1 2
3 4
julia> b = insertdims(a, dims=(1,2))
1×1×2×2 Array{Int64, 4}:
[:, :, 1, 1] =
1
[:, :, 2, 1] =
3
[:, :, 1, 2] =
2
[:, :, 2, 2] =
4 |
|
Not to discourage/criticize this PR, but: such functionality is available with a nice, consistent, and general syntax in a package: julia> using Accessors
julia> A = zeros(5, 4, 3);
# insert singleton dimension
julia> B = @insert size(A)[1] = 1;
julia> size(B)
(1, 5, 4, 3)
# delete singleton dimension:
julia> C = @delete size(B)[1];
julia> size(C)
(5, 4, 3)Multidim insert/delete not implemented, but can potentially be added as well. |
|
That looks really cool!! I like that it is implemented but I think it still would be nice to have |
Yeah, and the best part - it's all totally composable! julia> A = zeros(5, 4, 3, 1);
# drop singleton dimension, wherever it is:
julia> B = @delete size(A) |> _[findfirst(==(1), _)];
julia> size(B)
(5, 4, 3)
# insert singleton dimension last:
julia> C = @insert last(size(A)) = 1);
julia> size(C)
(5, 4, 3, 1, 1) |
One thing I like about the semantics of my implementation is that the following is mostly true: There might be a flaw though: Perhaps we need the following julia> function insertdims(A; dims)
idx = ntuple(maximum((ndims(A) + length(dims), dims...))) do i
if i ∈ dims
[CartesianIndex()]
else
(:)
end
end
@view A[idx...]
end
insertdims (generic function with 1 method)
julia> size(insertdims(A; dims=7))
(3, 4, 5, 1, 1, 1, 1) |
|
From triage, WWMBD: what would @mbauman do? The traditional answer is just spell An alternative to this pull request would be document the above better and/or define the above constant. Has any thinking changed? |
That's not type stable, is it? |
We could consider a type stable version in some circumstances, such as providing |
|
I think doing this as a reshape operation as initially proposed here makes far more sense than an indexing one. The reshape is going to be significantly easier on both type stability and preserving strided-ness... and it really is more reshape-y than index-y. The only reason to use indexing operations is to match the surface of numpy's |
|
The only issue in my implementation was that it required a sorted input for the inserted dims. |
If someone (could it be you?) reviews #54494 there will be |
|
If @mbauman thinks that requiring sorted tuples as input is fine, we could lift that requirement also later. |
|
In NumPy the convention is different: >>> x = np.array([[1, 2], [3,4]])
>>> np.expand_dims(x, axis=(0, 1,3)).shape
(1, 1, 2, 1, 2)Personally I find this a bit confusing since it counts the inserted dims too. |
|
Numpy seems to match the semantics I proposed earlier. I understand this in terms of where I want the singleton dimensions to be in the final result. |
|
The doc string should be included into the docs: diff --git a/doc/src/base/arrays.md b/doc/src/base/arrays.md
index ccfb237..66fe5c7 100644
--- a/doc/src/base/arrays.md
+++ b/doc/src/base/arrays.md
@@ -138,6 +138,7 @@ Base.parentindices
Base.selectdim
Base.reinterpret
Base.reshape
+Base.insertdims
Base.dropdims
Base.vec
Base.SubArray |
Hmm, I see... That definitely wasn't my first intuition :) But also, maybe the "inverse to |
Co-authored-by: Lilith Orion Hafner <lilithhafner@gmail.com>
Co-authored-by: Lilith Orion Hafner <lilithhafner@gmail.com>
|
@LilithHafner thanks for your suggestions. To be consistent with I also included your docstring and your tests. |
Co-authored-by: Lilith Orion Hafner <lilithhafner@gmail.com>
nsajko
left a comment
There was a problem hiding this comment.
The type system doesn't ensure N isa Int, but we can be explicit about this to help Julia's abstract type inference.
|
Triage is happy with adding this new function and exporting it because it mirrors dropdims. |
Co-authored-by: Lilith Orion Hafner <lilithhafner@gmail.com>
Co-authored-by: Lilith Orion Hafner <lilithhafner@gmail.com>
Co-authored-by: Lilith Orion Hafner <lilithhafner@gmail.com>
|
ok 🆒 |
Co-authored-by: Mark Kittisopikul <mkitti@users.noreply.github.com>
|
Thank you @roflmaostc! I appreciate your commitment and follow-through. |
Example:
```julia
julia> a = [1 2; 3 4]
2×2 Matrix{Int64}:
1 2
3 4
julia> b = insertdims(a, dims=(1,4))
1×2×2×1 Array{Int64, 4}:
[:, :, 1, 1] =
1 3
[:, :, 2, 1] =
2 4
julia> b[1,1,1,1] = 5; a
2×2 Matrix{Int64}:
5 2
3 4
julia> b = insertdims(a, dims=(1,2))
1×1×2×2 Array{Int64, 4}:
[:, :, 1, 1] =
5
[:, :, 2, 1] =
3
[:, :, 1, 2] =
2
[:, :, 2, 2] =
4
julia> b = insertdims(a, dims=(1,3))
1×2×1×2 Array{Int64, 4}:
[:, :, 1, 1] =
5 3
[:, :, 1, 2] =
2 4
```
---------
Co-authored-by: Neven Sajko <s@purelymail.com>
Co-authored-by: Lilith Orion Hafner <lilithhafner@gmail.com>
Co-authored-by: Mark Kittisopikul <mkitti@users.noreply.github.com>
Hi all!
My first attempt to add a method which we think is quite helpful.
It is similar to
dropdimsbut basically the inverse to it. I tried to follow the implementation ofdropdims. It also looks good in@code_warntype.I would be happy to receive some feedback and suggestions :)
Best,
Felix
Example: