Skip to content

Commit 78e17eb

Browse files
committed
WIP: adds AxisArray setindex! methods
Requires JuliaLang/julia#10331. But I think it is also running into a MAX_TUPLE_LEN issue, which is messing up my workaround for JuliaLang/julia#10191. Needs tests, too.
1 parent b4706f1 commit 78e17eb

File tree

1 file changed

+42
-50
lines changed

1 file changed

+42
-50
lines changed

src/indexing.jl

Lines changed: 42 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@ Base.getindex{T}(A::AxisArray{T,1}, idx::Colon) = A
2222

2323
# Linear indexing with an array
2424
Base.getindex{S<:Int}(A::AxisArray, idx::AbstractArray{S}) = A.data[idx]
25+
Base.setindex!{S<:Int}(A::AxisArray, v, idx::AbstractArray{S}) = (A.data[idx] = v)
2526

2627
# Cartesian iteration
2728
Base.eachindex(A::AxisArray) = eachindex(A.data)
2829
Base.getindex(A::AxisArray, idx::Base.IteratorsMD.CartesianIndex) = A.data[idx]
30+
Base.setindex!(A::AxisArray, v, idx::Base.IteratorsMD.CartesianIndex) = (A.data[idx] = v)
2931

3032
# More complicated cases where we must create a subindexed AxisArray
3133
# TODO: do we want to be dogmatic about using views? For the data? For the axes?
@@ -51,15 +53,38 @@ stagedfunction Base.getindex{T,N,D,Ax}(A::AxisArray{T,N,D,Ax}, idxs::Idx...)
5153
AxisArray(data, $axes) # TODO: avoid checking the axes here
5254
end
5355
end
56+
# Setindex is so much simpler. Just assign it to the data:
57+
Base.setindex!(A::AxisArray, v, idxs::Idx...) = (A.data[idxs...] = v)
5458

5559
### Fancier indexing capabilities provided only by AxisArrays ###
5660

57-
# First is the ability to index by named axis.
61+
# Defining the fallbacks on get/setindex are tricky due to ambiguities with
62+
# AbstractArray definitions... but they simply punt to to_index to convert the
63+
# special indexing forms to integers and integer ranges.
64+
# Even though all these splats look scary, they get inlined and don't allocate.
65+
Base.getindex(A::AxisArray, idx::AbstractArray) = A[to_index(A,idx)...]
66+
Base.setindex!(A::AxisArray, v, idx::AbstractArray) = (A[to_index(A,idx)...] = v)
67+
let rargs = Expr[], aargs = Expr[], idxs = Symbol[]
68+
for i = 1:4
69+
isym = symbol("i$i")
70+
push!(rargs, :($isym::Real))
71+
push!(aargs, :($isym::Any))
72+
push!(idxs, isym)
73+
@eval Base.getindex(A::AxisArray, $(rargs...)) = A[to_index(A,$(idxs...))...]
74+
@eval Base.setindex!(A::AxisArray, v, $(rargs...)) = (A[to_index(A,$(idxs...))...] = v)
75+
@eval Base.getindex(A::AxisArray, $(aargs...)) = A[to_index(A,$(idxs...))...]
76+
@eval Base.setindex!(A::AxisArray, v, $(aargs...)) = (A[to_index(A,$(idxs...))...] = v)
77+
end
78+
end
79+
Base.getindex(A::AxisArray, idxs...) = A[to_index(A,idxs...)...]
80+
Base.setindex!(A::AxisArray, v, idxs...) = (A[to_index(A,idxs...)...] = v)
81+
82+
# First is indexing by named axis. We simply sort the axes and re-dispatch.
5883
# When indexing by named axis the shapes of omitted dimensions are preserved
5984
# TODO: should we handle multidimensional Axis indexes? It could be interpreted
6085
# as adding dimensions in the middle of an AxisArray.
6186
# TODO: should we allow repeated axes? As a union of indices of the duplicates?
62-
stagedfunction Base.getindex{T,N,D,Ax}(A::AxisArray{T,N,D,Ax}, I::Axis...)
87+
stagedfunction to_index{T,N,D,Ax}(A::AxisArray{T,N,D,Ax}, I::Axis...)
6388
dims = Int[axisdim(A, ax) for ax in I]
6489
idxs = Expr[:(Colon()) for d = 1:N]
6590
names = axisnames(A)
@@ -68,7 +93,8 @@ stagedfunction Base.getindex{T,N,D,Ax}(A::AxisArray{T,N,D,Ax}, I::Axis...)
6893
idxs[dims[i]] = :(I[$i].val)
6994
end
7095

71-
return :(A[$(idxs...)])
96+
meta = Expr(:meta, :inline)
97+
return :($meta; to_index(A, $(idxs...)))
7298
end
7399

74100
### Indexing along values of the axes ###
@@ -93,58 +119,24 @@ function axisindexes{T}(::Type{Categorical}, ax::AbstractVector{T}, idx::Abstrac
93119
res
94120
end
95121

96-
# Defining the fallbacks on getindex are tricky due to ambiguities with
97-
# AbstractArray definitions -
98-
let args = Expr[], idxs = Symbol[]
99-
for i = 1:4
100-
isym = symbol("i$i")
101-
push!(args, :($isym::Real))
102-
push!(idxs, isym)
103-
@eval Base.getindex{T,N,D,Ax}(A::AxisArray{T,N,D,Ax}, $(args...)) = fallback_getindex(A, $(idxs...))
104-
end
105-
end
106-
Base.getindex{T,N,D,Ax}(A::AxisArray{T,N,D,Ax}, idx::AbstractArray) = fallback_getindex(A, idx)
107-
Base.getindex{T,N,D,Ax}(A::AxisArray{T,N,D,Ax}, idxs...) = fallback_getindex(A, idxs...)
108-
109122
# These catch-all methods attempt to convert any axis-specific non-standard
110-
# indexing types to their integer or integer range equivalents using the
111-
# They are separate from the `Base.getindex` function to help alleviate
123+
# indexing types to their integer or integer range equivalents using axisindexes
124+
# They are separate from the `Base.getindex` function to help alleviate
112125
# ambiguity warnings from, e.g., `getindex(::AbstractArray, ::Real...)`.
113-
# TODO: These could be generated with meta-meta-programming
114-
stagedfunction fallback_getindex{T,N,D,Ax}(A::AxisArray{T,N,D,Ax}, I1)
115-
ex = :(getindex(A))
116-
push!(ex.args, I1 <: Idx || length(Ax) < 1 ? :(I1) : :(axisindexes(A.axes[1], I1)))
117-
for _=2:N
118-
push!(ex.args, :(Colon()))
119-
end
120-
ex
121-
end
122-
stagedfunction fallback_getindex{T,N,D,Ax}(A::AxisArray{T,N,D,Ax}, I1, I2)
123-
ex = :(getindex(A))
124-
push!(ex.args, I1 <: Idx || length(Ax) < 1 ? :(I1) : :(axisindexes(A.axes[1], I1)))
125-
push!(ex.args, I2 <: Idx || length(Ax) < 2 ? :(I2) : :(axisindexes(A.axes[2], I2)))
126-
for _=3:N
127-
push!(ex.args, :(Colon()))
128-
end
129-
ex
130-
end
131-
stagedfunction fallback_getindex{T,N,D,Ax}(A::AxisArray{T,N,D,Ax}, I1, I2, I3)
132-
ex = :(getindex(A))
133-
push!(ex.args, I1 <: Idx || length(Ax) < 1 ? :(I1) : :(axisindexes(A.axes[1], I1)))
134-
push!(ex.args, I2 <: Idx || length(Ax) < 2 ? :(I2) : :(axisindexes(A.axes[2], I2)))
135-
push!(ex.args, I3 <: Idx || length(Ax) < 3 ? :(I3) : :(axisindexes(A.axes[3], I3)))
136-
for _=4:N
137-
push!(ex.args, :(Colon()))
138-
end
139-
ex
140-
end
141-
stagedfunction fallback_getindex{T,N,D,Ax}(A::AxisArray{T,N,D,Ax}, I...)
142-
ex = :(getindex(A))
126+
stagedfunction to_index{T,N,D,Ax}(A::AxisArray{T,N,D,Ax}, I...)
127+
ex = Expr(:tuple)
143128
for i=1:length(I)
144-
push!(ex.args, I[i] <: Idx || length(Ax) < i ? :(I[$i]) : :(axisindexes(A.axes[$i], I[$i])))
129+
if I[i] <: Idx
130+
push!(ex.args, :(I[$i]))
131+
elseif i <= length(Ax)
132+
push!(ex.args, :(axisindexes(A.axes[$i], I[$i])))
133+
else
134+
push!(ex.args, :(error("dimension ", $i, " does not have an axis to index")))
135+
end
145136
end
146137
for _=length(I)+1:N
147138
push!(ex.args, :(Colon()))
148139
end
149-
ex
140+
meta = Expr(:meta, :inline)
141+
return :($meta; $ex)
150142
end

0 commit comments

Comments
 (0)