-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
add Slices array type for eachslice/eachrow/eachcol #32310
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 19 commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
9edd085
Add SliceArray type for eachslice/eachcol/eachrow
simonbyrne 9671e8f
rename aliases to Rows/Columns
simonbyrne 4259630
fix field name
simonbyrne 0794f87
fix test
simonbyrne 1997799
more test fixes
simonbyrne ddd0a4f
use axes instead of cartiter
simonbyrne 16c8b66
rename SliceArray => Slices
simonbyrne 1c932bc
use : as sentinel instead of nothing
simonbyrne 1f710f7
typos
simonbyrne a5ee96e
more fixes
simonbyrne 28ce9c8
fix indexing dispatch
simonbyrne c104154
move to slicemap to a field
simonbyrne 51e091d
doc fixes
simonbyrne 2b44296
rearrange parameters
simonbyrne 98d64e6
Apply suggestions from code review
simonbyrne 45271e7
Update base/slicearray.jl
simonbyrne 5e69131
Rename types, add supertype, some minor fixes
nalimilan d62adb6
Fix doctest
nalimilan 48d9c83
Fix version in docstring
nalimilan 52529d8
Merge branch 'master' into sb/eachslice-type
simonbyrne ba675a6
Update base/abstractarraymath.jl
simonbyrne fa7fdcc
Update NEWS.md
nalimilan File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,234 @@ | ||
""" | ||
AbstractSlices{S,N} <: AbstractArray{S,N} | ||
|
||
Supertype for arrays of slices into a parent array over some dimension(s), | ||
returning views that select all the data from the other dimensions. | ||
|
||
`parent` will return the parent array. | ||
""" | ||
abstract type AbstractSlices{T,N} <: AbstractArray{T,N} end | ||
|
||
""" | ||
Slices{P,SM,AX,S,N} <: AbstractSlices{S,N} | ||
|
||
An `AbstractArray` of slices into a parent array over specified dimension(s), | ||
returning views that select all the data from the other dimension(s). | ||
|
||
These should typically be constructed by [`eachslice`](@ref), [`eachcol`](@ref) or | ||
[`eachrow`](@ref). | ||
|
||
[`parent(s::Slices)`](@ref) will return the parent array. | ||
""" | ||
struct Slices{P,SM,AX,S,N} <: AbstractSlices{S,N} | ||
""" | ||
Parent array | ||
""" | ||
parent::P | ||
""" | ||
A tuple of length `ndims(parent)`, denoting how each dimension should be handled: | ||
- an integer `i`: this is the `i`th dimension of the outer `Slices` object. | ||
- `:`: an "inner" dimension | ||
""" | ||
slicemap::SM | ||
""" | ||
A tuple of length `N` containing the [`axes`](@ref) of the `Slices` object. | ||
""" | ||
axes::AX | ||
end | ||
|
||
unitaxis(::AbstractArray) = Base.OneTo(1) | ||
|
||
function Slices(A::P, slicemap::SM, ax::AX) where {P,SM,AX} | ||
N = length(ax) | ||
S = Base._return_type(view, Tuple{P, map((a,l) -> l === (:) ? Colon : eltype(a), axes(A), slicemap)...}) | ||
Slices{P,SM,AX,S,N}(A, slicemap, ax) | ||
end | ||
|
||
_slice_check_dims(N) = nothing | ||
function _slice_check_dims(N, dim, dims...) | ||
1 <= dim <= N || throw(DimensionMismatch("Invalid dimension $dim")) | ||
dim in dims && throw(DimensionMismatch("Dimensions $dims are not unique")) | ||
_slice_check_dims(N,dims...) | ||
end | ||
|
||
@inline function _eachslice(A::AbstractArray{T,N}, dims::NTuple{M,Integer}, drop::Bool) where {T,N,M} | ||
_slice_check_dims(N,dims...) | ||
if drop | ||
# if N = 4, dims = (3,1) then | ||
# axes = (axes(A,3), axes(A,1)) | ||
# slicemap = (2, :, 1, :) | ||
ax = map(dim -> axes(A,dim), dims) | ||
slicemap = ntuple(dim -> something(findfirst(isequal(dim), dims), (:)), N) | ||
return Slices(A, slicemap, ax) | ||
else | ||
# if N = 4, dims = (3,1) then | ||
# axes = (axes(A,1), OneTo(1), axes(A,3), OneTo(1)) | ||
# slicemap = (1, :, 3, :) | ||
ax = ntuple(dim -> dim in dims ? axes(A,dim) : unitaxis(A), N) | ||
slicemap = ntuple(dim -> dim in dims ? dim : (:), N) | ||
return Slices(A, slicemap, ax) | ||
end | ||
end | ||
@inline function _eachslice(A::AbstractArray, dim::Integer, drop::Bool) | ||
_eachslice(A, (dim,), drop) | ||
end | ||
|
||
""" | ||
eachslice(A::AbstractArray; dims, drop=true) | ||
|
||
Create a [`Slices`](@ref) object that is an array of slices over dimensions `dims` of `A`, returning | ||
views that select all the data from the other dimensions in `A`. `dims` can either by an | ||
integer or a tuple of integers. | ||
|
||
If `drop = true` (the default), the outer `Slices` will drop the inner dimensions, and | ||
the ordering of the dimensions will match those in `dims`. If `drop = false`, then the | ||
`Slices` will have the same dimensionality as the underlying array, with inner | ||
dimensions having size 1. | ||
|
||
See also [`eachrow`](@ref), [`eachcol`](@ref), [`mapslices`](@ref) and [`selectdim`](@ref). | ||
|
||
!!! compat "Julia 1.1" | ||
This function requires at least Julia 1.1. | ||
|
||
!!! compat "Julia 1.9" | ||
Prior to Julia 1.9, this returned an iterator, and only a single dimension `dims` was supported. | ||
|
||
# Example | ||
|
||
```jldoctest | ||
julia> m = [1 2 3; 4 5 6; 7 8 9] | ||
3×3 Matrix{Int64}: | ||
1 2 3 | ||
4 5 6 | ||
7 8 9 | ||
|
||
julia> s = eachslice(m, dims=1) | ||
3-element RowSlices{Matrix{Int64}, Tuple{Base.OneTo{Int64}}, SubArray{Int64, 1, Matrix{Int64}, Tuple{Int64, Base.Slice{Base.OneTo{Int64}}}, true}}: | ||
[1, 2, 3] | ||
[4, 5, 6] | ||
[7, 8, 9] | ||
|
||
julia> s[1] | ||
3-element view(::Matrix{Int64}, 1, :) with eltype Int64: | ||
1 | ||
2 | ||
3 | ||
|
||
julia> eachslice(m, dims=1, drop=false) | ||
3×1 Slices{Matrix{Int64}, Tuple{Int64, Colon}, Tuple{Base.OneTo{Int64}, Base.OneTo{Int64}}, SubArray{Int64, 1, Matrix{Int64}, Tuple{Int64, Base.Slice{Base.OneTo{Int64}}}, true}, 2}: | ||
[1, 2, 3] | ||
[4, 5, 6] | ||
[7, 8, 9] | ||
``` | ||
""" | ||
@inline function eachslice(A; dims, drop=true) | ||
_eachslice(A, dims, drop) | ||
end | ||
|
||
""" | ||
eachrow(A::AbstractVecOrMat) <: AbstractVector | ||
|
||
Create a [`RowSlices`](@ref) object that is a vector of rows of matrix or vector `A`. | ||
Row slices are returned as `AbstractVector` views of `A`. | ||
|
||
See also [`eachcol`](@ref), [`eachslice`](@ref) and [`mapslices`](@ref). | ||
|
||
!!! compat "Julia 1.1" | ||
This function requires at least Julia 1.1. | ||
|
||
!!! compat "Julia 1.9" | ||
Prior to Julia 1.9, this returned an iterator. | ||
|
||
# Example | ||
|
||
```jldoctest | ||
julia> a = [1 2; 3 4] | ||
2×2 Matrix{Int64}: | ||
1 2 | ||
3 4 | ||
|
||
julia> s = eachrow(a) | ||
2-element RowSlices{Matrix{Int64}, Tuple{Base.OneTo{Int64}}, SubArray{Int64, 1, Matrix{Int64}, Tuple{Int64, Base.Slice{Base.OneTo{Int64}}}, true}}: | ||
[1, 2] | ||
[3, 4] | ||
|
||
julia> s[1] | ||
2-element view(::Matrix{Int64}, 1, :) with eltype Int64: | ||
1 | ||
2 | ||
``` | ||
""" | ||
eachrow(A::AbstractMatrix) = _eachslice(A, (1,), true) | ||
eachrow(A::AbstractVector) = eachrow(reshape(A, size(A,1), 1)) | ||
|
||
""" | ||
eachcol(A::AbstractVecOrMat) <: AbstractVector | ||
|
||
Create a [`ColumnSlices`](@ref) object that is a vector of columns of matrix or vector `A`. | ||
Column slices are returned as `AbstractVector` views of `A`. | ||
|
||
See also [`eachrow`](@ref), [`eachslice`](@ref) and [`mapslices`](@ref). | ||
|
||
!!! compat "Julia 1.1" | ||
This function requires at least Julia 1.1. | ||
|
||
!!! compat "Julia 1.9" | ||
Prior to Julia 1.9, this returned an iterator. | ||
|
||
# Example | ||
|
||
```jldoctest | ||
julia> a = [1 2; 3 4] | ||
2×2 Matrix{Int64}: | ||
1 2 | ||
3 4 | ||
|
||
julia> s = eachcol(a) | ||
2-element ColumnSlices{Matrix{Int64}, Tuple{Base.OneTo{Int64}}, SubArray{Int64, 1, Matrix{Int64}, Tuple{Base.Slice{Base.OneTo{Int64}}, Int64}, true}}: | ||
[1, 3] | ||
[2, 4] | ||
|
||
julia> s[1] | ||
2-element view(::Matrix{Int64}, :, 1) with eltype Int64: | ||
1 | ||
3 | ||
``` | ||
""" | ||
eachcol(A::AbstractMatrix) = _eachslice(A, (2,), true) | ||
eachcol(A::AbstractVector) = eachcol(reshape(A, size(A, 1), 1)) | ||
|
||
""" | ||
RowSlices{M,AX,S} | ||
|
||
A special case of [`Slices`](@ref) that is a vector of row slices of a matrix, as | ||
constructed by [`eachrow`](@ref). | ||
|
||
[`parent`](@ref) can be used to get the underlying matrix. | ||
""" | ||
const RowSlices{P<:AbstractMatrix,AX,S<:AbstractVector} = Slices{P,Tuple{Int,Colon},AX,S,1} | ||
|
||
""" | ||
ColumnSlices{M,AX,S} | ||
|
||
A special case of [`Slices`](@ref) that is a vector of column slices of a matrix, as | ||
constructed by [`eachcol`](@ref). | ||
|
||
[`parent`](@ref) can be used to get the underlying matrix. | ||
""" | ||
const ColumnSlices{P<:AbstractMatrix,AX,S<:AbstractVector} = Slices{P,Tuple{Colon,Int},AX,S,1} | ||
|
||
|
||
IteratorSize(::Type{Slices{P,SM,AX,S,N}}) where {P,SM,AX,S,N} = HasShape{N}() | ||
axes(s::Slices) = s.axes | ||
size(s::Slices) = map(length, s.axes) | ||
|
||
@inline function _slice_index(s::Slices, c...) | ||
return map(l -> l === (:) ? (:) : c[l], s.slicemap) | ||
end | ||
|
||
Base.@propagate_inbounds getindex(s::Slices{P,SM,AX,S,N}, I::Vararg{Int,N}) where {P,SM,AX,S,N} = | ||
view(s.parent, _slice_index(s, I...)...) | ||
Base.@propagate_inbounds setindex!(s::Slices{P,SM,AX,S,N}, val, I::Vararg{Int,N}) where {P,SM,AX,S,N} = | ||
s.parent[_slice_index(s, I...)...] = val | ||
|
||
parent(s::Slices) = s.parent |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.