Skip to content
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

Breaking: preservedims in tables #917

Open
wants to merge 22 commits into
base: breaking
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/DimensionalData.jl
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ include("tables.jl")
include("plotrecipes.jl")
include("utils.jl")
include("set.jl")
include("opaque.jl")
include("groupby.jl")
include("precompile.jl")
include("interface_tests.jl")
Expand Down
1 change: 1 addition & 0 deletions src/Dimensions/dimension.jl
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ Base.axes(d::Dimension, i) = axes(d)[i]
Base.eachindex(d::Dimension) = eachindex(val(d))
Base.length(d::Dimension) = length(val(d))
Base.ndims(d::Dimension) = 0
Base.parentindices(d::Dimension{<:AbstractArray}) = parentindices(parent(d))
Base.ndims(d::Dimension{<:AbstractArray}) = ndims(val(d))
Base.iterate(d::Dimension{<:AbstractArray}, args...) = iterate(lookup(d), args...)
Base.first(d::Dimension) = val(d)
Expand Down
6 changes: 2 additions & 4 deletions src/Lookups/Lookups.jl
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,15 @@ export Unaligned, Transformed, ArrayLookup
# Deprecated
export LookupArray

const StandardIndices = Union{AbstractArray{<:Integer},Colon,Integer,CartesianIndex,CartesianIndices}

# As much as possible keyword rebuild is automatic
rebuild(x; kw...) = ConstructionBase.setproperties(x, (; kw...))

include("metadata.jl")
include("lookup_traits.jl")
include("metadata.jl")
include("lookup_arrays.jl")
include("beginend.jl")
include("predicates.jl")
include("selector.jl")
include("beginend.jl")
include("indexing.jl")
include("methods.jl")
include("utils.jl")
Expand Down
2 changes: 2 additions & 0 deletions src/Lookups/beginend.jl
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ Base.to_indices(A, inds, (r, args...)::Tuple{<:Union{Begin,End,<:LazyMath},Varar
_to_index(inds, a::Int) = a
_to_index(inds, ::Begin) = first(inds)
_to_index(inds, ::End) = last(inds)
_to_index(inds, ::Type{Begin}) = first(inds)
_to_index(inds, ::Type{End}) = last(inds)
_to_index(inds, l::LazyMath{End}) = l.f(last(inds))
_to_index(inds, l::LazyMath{Begin}) = l.f(first(inds))

Expand Down
11 changes: 6 additions & 5 deletions src/Lookups/lookup_arrays.jl
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Base.first(l::Lookup) = first(parent(l))
Base.last(l::Lookup) = last(parent(l))
Base.firstindex(l::Lookup) = firstindex(parent(l))
Base.lastindex(l::Lookup) = lastindex(parent(l))
Base.parentindices(l::Lookup) = parentindices(parent(l))
function Base.:(==)(l1::Lookup, l2::Lookup)
basetypeof(l1) == basetypeof(l2) && parent(l1) == parent(l2)
end
Expand Down Expand Up @@ -159,11 +160,10 @@ NoLookup() = NoLookup(AutoValues())
rebuild(l::NoLookup; data=parent(l), kw...) = NoLookup(data)

# Used in @d broadcasts
struct Length1NoLookup <: AbstractNoLookup end
Length1NoLookup(::AbstractVector) = Length1NoLookup()

rebuild(l::Length1NoLookup; kw...) = Length1NoLookup()
Base.parent(::Length1NoLookup) = Base.OneTo(1)
struct Length1NoLookup{A<:AbstractUnitRange} <: AbstractNoLookup
data::A
end
Length1NoLookup() = Length1NoLookup(Base.OneTo(1))

"""
AbstractSampled <: Aligned
Expand Down Expand Up @@ -866,6 +866,7 @@ promote_first(x1, x2, xs...) =
# Fallback NoLookup if not identical type
promote_first(l1::Lookup) = l1
promote_first(l1::L, ls::L...) where L<:Lookup = rebuild(l1; metadata=NoMetadata)
promote_first(l1::L, ls::L...) where L<:AbstractNoLookup = l1
function promote_first(l1::Lookup, ls1::Lookup...)
ls = _remove(Length1NoLookup, l1, ls1...)
if length(ls) != length(ls1) + 1
Expand Down
9 changes: 9 additions & 0 deletions src/Lookups/selector.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
const StandardIndices = Union{
AbstractArray{<:Integer},
Colon,
Integer,
CartesianIndex,
CartesianIndices,
BeginEndRange,
}

struct SelectorError{L,S} <: Exception
lookup::L
selector::S
Expand Down
72 changes: 55 additions & 17 deletions src/array/array.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const IDim = Dimension{<:StandardIndices}
const MaybeDimTuple = Tuple{Vararg{Dimension}}

"""
AbstractBasicDimArray <: AbstractArray
Expand All @@ -8,7 +9,7 @@ returns a `Tuple` of `Dimension`

Only keyword `rebuild` is guaranteed to work with `AbstractBasicDimArray`.
"""
abstract type AbstractBasicDimArray{T,N,D<:Tuple} <: AbstractArray{T,N} end
abstract type AbstractBasicDimArray{T,N,D<:MaybeDimTuple} <: AbstractArray{T,N} end

const AbstractBasicDimVector = AbstractBasicDimArray{T,1} where T
const AbstractBasicDimMatrix = AbstractBasicDimArray{T,2} where T
Expand Down Expand Up @@ -94,10 +95,27 @@ metadata(A::AbstractDimArray) = A.metadata

layerdims(A::AbstractDimArray) = basedims(A)

@inline rebuildsliced(A::AbstractBasicDimArray, args...) = rebuildsliced(getindex, A, args...)
@inline function rebuildsliced(f::Function, A::AbstractBasicDimArray, data::AbstractArray, I::Tuple, name=name(A))
"""
rebuildsliced(f::Function, A::AbstractBasicDimArray, I)

Rebuild `AbstractDimArray` where `f` is `getindex` , `view` or `dotview`.

This does not need to be defined for `AbstractDimArray`, as `f`
is simply called on the parent array, dims and refdims are sliced with `slicedims`,
and `rebuild` is called.

However for custom `AbstractBasicDimArray`, `rebuildsliced` methods are needed
to define slicing behavior, as there not be a parent array.
"""
@propagate_inbounds rebuildsliced(A::AbstractBasicDimArray, args...) = rebuildsliced(getindex, A, args...)
@propagate_inbounds function rebuildsliced(f::Function, A::AbstractDimArray, I::Tuple, name=name(A))
I1 = to_indices(A, I)
data = f(parent(A), I1...)
return rebuildsliced(f, A, data, I1, name)
end
@propagate_inbounds function rebuildsliced(f::Function, A::AbstractDimArray, data::AbstractArray, I::Tuple, name=name(A))
I1 = to_indices(A, I)
rebuild(A, data, slicedims(f, A, I1)..., name)
return rebuild(A, data, slicedims(f, A, I1)..., name)
end

# Array interface methods ######################################################
Expand All @@ -107,6 +125,7 @@ Base.axes(A::AbstractDimArray) = map(Dimensions.DimUnitRange, axes(parent(A)), d
Base.iterate(A::AbstractDimArray, args...) = iterate(parent(A), args...)
Base.IndexStyle(A::AbstractDimArray) = Base.IndexStyle(parent(A))
Base.parent(A::AbstractDimArray) = data(A)
Base.parentindices(A::AbstractDimArray) = parentindices(parent(A))
Base.vec(A::AbstractDimArray) = vec(parent(A))
# Only compare data and dim - metadata and refdims can be different
Base.:(==)(A1::AbstractDimArray, A2::AbstractDimArray) =
Expand Down Expand Up @@ -170,14 +189,6 @@ end
# An alternative would be to fill missing dims with `Anon`, and keep existing
# dims but strip the Lookup? It just seems a little complicated when the methods
# below using DimTuple work better anyway.
Base.similar(A::AbstractDimArray, i::Integer, I::Vararg{Integer}; kw...) =
similar(A, eltype(A), (i, I...); kw...)
Base.similar(A::AbstractDimArray, I::Tuple{Int,Vararg{Int}}; kw...) =
similar(A, eltype(A), I; kw...)
Base.similar(A::AbstractDimArray, ::Type{T}, i::Integer, I::Vararg{Integer}; kw...) where T =
similar(A, T, (i, I...); kw...)
Base.similar(A::AbstractDimArray, ::Type{T}, I::Tuple{Int,Vararg{Int}}; kw...) where T =
similar(parent(A), T, I)

const MaybeDimUnitRange = Union{Integer,Base.OneTo,Dimensions.DimUnitRange}
# when all axes are DimUnitRanges we can return an `AbstractDimArray`
Expand Down Expand Up @@ -256,14 +267,27 @@ function _similar(::Type{T}, shape::Tuple; kw...) where {T<:AbstractArray}
end

# With Dimensions we can return an `AbstractDimArray`
Base.similar(A::AbstractBasicDimArray, D::DimTuple; kw...) = Base.similar(A, eltype(A), D; kw...)
Base.similar(A::AbstractBasicDimArray, D::Dimension...; kw...) = Base.similar(A, eltype(A), D; kw...)
Base.similar(A::AbstractBasicDimArray, ::Type{T}, D::Dimension...; kw...) where T =
Base.similar(A, T, D; kw...)
Base.similar(A::AbstractBasicDimArray, d1::Dimension, D::Dimension...; kw...) =
Base.similar(A, eltype(A), (d1, D...); kw...)
Base.similar(A::AbstractBasicDimArray, ::Type{T}, d1::Dimension, D::Dimension...; kw...) where T =
Base.similar(A, T, (d1, D...); kw...)
Base.similar(A::AbstractBasicDimArray, D::DimTuple; kw...) =
Base.similar(A, eltype(A), D; kw...)
function Base.similar(A::AbstractBasicDimArray, ::Type{T}, D::DimTuple; kw...) where T
data = _arraytype(T)(undef, _dimlength(D))
dimconstructor(D)(data, D; kw...)
end
function Base.similar(A::AbstractBasicDimArray, ::Type{T}, D::Tuple{};
refdims=(), name=_noname(A), metadata=NoMetadata(), kw...
) where T
data = _arraytype(T)(undef, _dimlength(D))
dimconstructor(D)(data, (); refdims, name, metadata, kw...)
end

function Base.similar(A::AbstractDimArray, ::Type{T}, D::DimTuple;
refdims=(), name=_noname(A), metadata=NoMetadata(), kw...
) where T
data = similar(parent(A), T, _dimlength(D))
data = _arraytype(T)(undef, _dimlength(D))
dims = _maybestripval(D)
return rebuild(A; data, dims, refdims, metadata, name, kw...)
end
Expand All @@ -274,6 +298,20 @@ function Base.similar(A::AbstractDimArray, ::Type{T}, D::Tuple{};
rebuild(A; data, dims=(), refdims, metadata, name, kw...)
end

Base.similar(A::AbstractBasicDimArray, shape::Int...; kw...) =
similar(A, eltype(A), shape; kw...)
Base.similar(A::AbstractBasicDimArray, shape::Tuple{Vararg{Int}}; kw...) =
similar(A, eltype(A), shape; kw...)
Base.similar(A::AbstractBasicDimArray, ::Type{T}, shape::Int...; kw...) where T =
similar(A, T, shape; kw...)
Base.similar(A::AbstractBasicDimArray, ::Type{T}, shape::Tuple{Vararg{Int}}; kw...) where T =
_arraytype(T)(undef, shape)
Base.similar(A::AbstractDimArray, ::Type{T}, shape::Tuple{Vararg{Int}}; kw...) where T =
similar(parent(A), T, shape)

_arraytype(::Type{T}) where T = Array{T}
_arraytype(::Type{Bool}) = BitArray

# Keep the same type in `similar`
_noname(A::AbstractBasicDimArray) = _noname(name(A))
_noname(s::String) = ""
Expand Down
40 changes: 36 additions & 4 deletions src/array/broadcast.jl
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ strict_broadcast!(x::Bool) = STRICT_BROADCAST_CHECKS[] = x
# It preserves the dimension names.
# `S` should be the `BroadcastStyle` of the wrapped type.
# Copied from NamedDims.jl (thanks @oxinabox).
struct DimensionalStyle{S <: BroadcastStyle} <: AbstractArrayStyle{Any} end
struct BasicDimensionalStyle{N} <: AbstractArrayStyle{Any} end

struct DimensionalStyle{S<:BroadcastStyle} <: AbstractArrayStyle{Any} end
DimensionalStyle(::S) where {S} = DimensionalStyle{S}()
DimensionalStyle(::S, ::Val{N}) where {S,N} = DimensionalStyle(S(Val(N)))
DimensionalStyle(::Val{N}) where N = DimensionalStyle{DefaultArrayStyle{N}}()
Expand All @@ -53,6 +55,8 @@ function BroadcastStyle(::Type{<:AbstractDimArray{T,N,D,A}}) where {T,N,D,A}
inner_style = typeof(BroadcastStyle(A))
return DimensionalStyle{inner_style}()
end
BroadcastStyle(::Type{<:AbstractBasicDimArray{T,N}}) where {T,N} =
BasicDimensionalStyle{N}()

BroadcastStyle(::DimensionalStyle, ::Base.Broadcast.Unknown) = Unknown()
BroadcastStyle(::Base.Broadcast.Unknown, ::DimensionalStyle) = Unknown()
Expand All @@ -79,12 +83,31 @@ function Broadcast.copy(bc::Broadcasted{DimensionalStyle{S}}) where S
dims = format(Dimensions.promotedims(bdims...; skip_length_one=true), data)
return rebuild(A; data, dims, refdims=refdims(A), name=Symbol(""))
end
function Broadcast.copy(bc::Broadcasted{BasicDimensionalStyle{N}}) where N
A = _firstdimarray(bc)
data = collect(bc)
A isa Nothing && return data # No AbstractDimArray

bdims = _broadcasted_dims(bc)
_comparedims_broadcast(A, bdims...)

data isa AbstractArray || return data # result is a scalar

# Return an AbstractDimArray
dims = format(Dimensions.promotedims(bdims...; skip_length_one=true), data)
return dimconstructor(dims)(data, dims; refdims=refdims(A), name=Symbol(""))
end

function Base.copyto!(dest::AbstractArray, bc::Broadcasted{DimensionalStyle{S}}) where S
fda = _firstdimarray(bc)
isnothing(fda) || _comparedims_broadcast(fda, _broadcasted_dims(bc)...)
copyto!(dest, _unwrap_broadcasted(bc))
end
function Base.copyto!(dest::AbstractArray, bc::Broadcasted{BasicDimensionalStyle{N}}) where N
fda = _firstdimarray(bc)
isnothing(fda) || _comparedims_broadcast(fda, _broadcasted_dims(bc)...)
copyto!(dest, bc)
end

@inline function Base.Broadcast.materialize!(dest::AbstractDimArray, bc::Base.Broadcast.Broadcasted{<:Any})
# Need to check whether the dims are compatible in dest,
Expand All @@ -97,7 +120,15 @@ end

function Base.similar(bc::Broadcast.Broadcasted{DimensionalStyle{S}}, ::Type{T}) where {S,T}
A = _firstdimarray(bc)
rebuildsliced(A, similar(_unwrap_broadcasted(bc), T, axes(bc)...), axes(bc), Symbol(""))
data = similar(_unwrap_broadcasted(bc), T, size(bc))
dims, refdims = slicedims(A, axes(bc))
return rebuild(A; data, dims, refdims, name=Symbol(""))
end
function Base.similar(bc::Broadcast.Broadcasted{BasicDimensionalStyle{N}}, ::Type{T}) where {N,T}
A = _firstdimarray(bc)
data = similar(A, T, size(bc))
dims, refdims = slicedims(A, axes(bc))
return dimconstructor(dims)(data, dims; refdims, name=Symbol(""))
end


Expand Down Expand Up @@ -383,9 +414,10 @@ _unwrap_broadcasted(boda::BroadcastOptionsDimArray) = parent(parent(boda))

# Get the first dimensional array in the broadcast
_firstdimarray(x::Broadcasted) = _firstdimarray(x.args)
_firstdimarray(x::Tuple{<:AbstractDimArray,Vararg}) = x[1]
_firstdimarray(x::Tuple{<:AbstractBasicDimArray,Vararg}) = x[1]
_firstdimarray(x::AbstractBasicDimArray) = x
_firstdimarray(ext::Base.Broadcast.Extruded) = _firstdimarray(ext.x)
function _firstdimarray(x::Tuple{<:Broadcasted,Vararg})
function _firstdimarray(x::Tuple{<:Union{Broadcasted,Base.Broadcast.Extruded},Vararg})
found = _firstdimarray(x[1])
if found isa Nothing
_firstdimarray(tail(x))
Expand Down
Loading
Loading