Skip to content

Commit 77ec916

Browse files
author
Michael Abbott
committed
add InvertedIndices
1 parent 1d75988 commit 77ec916

File tree

6 files changed

+44
-21
lines changed

6 files changed

+44
-21
lines changed

Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ version = "0.0.1"
66
[deps]
77
Compat = "34da2185-b29b-5c13-b0c7-acf172513d20"
88
IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953"
9+
InvertedIndices = "41ab1584-1d38-5bbf-9106-f11c6c58b48f"
910
LazyStack = "1fad7336-0346-5a1a-a56f-a06ba010965b"
1011
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
1112
NamedDims = "356022a1-0364-5f58-8944-0da4b18d706f"
@@ -17,6 +18,7 @@ Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
1718
BenchmarkTools = "0.5"
1819
Compat = "3.1"
1920
IntervalSets = "0.3, 0.4"
21+
InvertedIndices = "1.0"
2022
LazyStack = "0.0.7"
2123
NamedDims = "0.2.20"
2224
OffsetArrays = "0.10, 0.11, 1.0"

README.md

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,14 @@ There are also a numer of special selectors, which work like this:
4848
|-----------------|------------------|-------------------------|---------|
4949
| one nearest | `B[time = 3]` | `B(time = Near(17.0))` | vector |
5050
| all in a range | `B[2:5, :]` | `B(Interval(14,25), :)` | matrix |
51-
| all matching | `B[3:end, 3:3]` | `B(>(17), ==(33))` | matrix |
51+
| all matching | `B[3:end, Not(3)]` | `B(>(17), !=(33))` | matrix |
5252
| mixture | `A[1, 2, end]` | `A(:left, Index[2], Index[end])` | scalar |
5353
| non-scalar | `B[iter=[1, 3]]` | `B(iter=[31, 33])` | matrix |
5454

5555
Here `Interval(13,18)` can also be written `13..18`, it's from [IntervalSets.jl](https://github.com/JuliaMath/IntervalSets.jl).
5656
Any functions can be used to select keys, including lambdas: `B(time = t -> 0<t<17)`.
5757
You may give just one `::Base.Fix2` function
58-
(such as `<=(18)` or `!=(20)`) provided its argument type matches the keys of one dimension.
58+
(such as `<=(18)` or `==(20)`) provided its argument type matches the keys of one dimension.
5959
An interval or a function always selects via `findall`,
6060
i.e. it does not drop a dimension, even if there is exactly one match.
6161

@@ -76,17 +76,18 @@ see [PR#5](https://github.com/mcabbott/AxisKeys.jl/pull/5) for an attempt.)
7676
KeyedArray(rand(Int8, 2,10), ([:a, :b], 10:10:100)) # AbstractArray, Tuple{AbstractVector, ...}
7777
```
7878

79-
A nested pair with names can be constructed with keywords,
80-
and (apart from a few bugs) this should work the same in either order:
79+
A nested pair with names can be constructed with keywords for names,
80+
and (apart from a few bugs) everything should work the same way in either order:
8181

8282
```julia
8383
KeyedArray(rand(Int8, 2,10), row=[:a, :b], col=10:10:100) # KeyedArray(NamedDimsArray(...))
8484
NamedDimsArray(rand(Int8, 2,10), row=[:a, :b], col=10:10:100) # NamedDimsArray(KeyedArray(...))
8585
```
8686

8787
The function `wrapdims` does a bit more checking and fixing, but is not type-stable.
88-
It will adjust the length of key vectors if it can, and their indexing if needed to match the array.
89-
The order of wrappers produced is controlled by `AxisKeys.nameouter()=false`.
88+
It will adjust the length of ranges of keys if it can,
89+
and will fix indexing offsets if needed to match the array.
90+
The resulting order of wrappers is controlled by `AxisKeys.nameouter()=false`.
9091

9192
```julia
9293
wrapdims(rand(Int8, 10), alpha='a':'z')
@@ -100,14 +101,11 @@ axiskeys(ans,1) # 10:10:100 with indices 0:9
100101

101102
As usual `axes(A)` returns (a tuple of vectors of) indices,
102103
and `axiskeys(A)` returns (a tuple of vectors of) keys.
103-
If the array has names, then `dimnames(A)` returns them,
104-
and functions like `axes(A, name)` give just one.
104+
If the array has names, then `dimnames(A)` returns them.
105+
These functions work like `size(A, d) = size(A, name)` to get just one.
105106

106107
Many functions should work, for example:
107108

108-
* Reductions like `sum(A; dims=:channel)` can use dimension names.
109-
Likewise `prod`, `mean` etc., and `dropdims`.
110-
111109
* Broadcasting `log.(A)` and `map(log, A)`, as well as comprehensions
112110
`[log(x) for x in A]` should all work.
113111

@@ -116,21 +114,28 @@ Many functions should work, for example:
116114
* Concatenation `hcat(B, B .+ 100)` works.
117115
Note that the keys along the glued direction may not be unique afterwards.
118116

117+
* Reductions like `sum(A; dims=:channel)` can use dimension names.
118+
Likewise `prod`, `mean` etc., and `dropdims`.
119+
119120
* Some linear algebra functions like `*` and `\` will work.
120121

121122
* Getproperty returns the key vector, to allow things like
122-
`for (i,t) in enumerate(A.time); fun(A[i], t); ...`.
123+
`for (i,t) in enumerate(A.time); fun(val = A[i,:], time = t); ...`.
123124

124125
* Vectors support `push!(V, val)`, which will try to extend the key vector.
125126
There is also a method `push!(V, key => val)` which pushes in a new key.
126127

127128
To allow for this limited mutability, `V.keys isa Ref` for vectors,
128129
while `A.keys isa Tuple` for matrices & higher. But `axiskeys(A)` always returns a tuple.
129130

130-
* [LazyStack](https://github.com/mcabbott/LazyStack.jl)`.stack` is now hooked up.
131-
Stacks of named tuples like `stack((a=i, b=i^2) for i=1:5)` create axis keys.
131+
* Named tuples can be converted to and from keyed vectors,
132+
with `collect(keys(nt)) == Symbol.(axiskeys(V),1)`
133+
134+
* [LazyStack](https://github.com/mcabbott/LazyStack.jl)`.stack` understands names and keys.
135+
Stacks of named tuples like `stack((a=i, b=i^2) for i=1:5)` create a matrix with `[:a, :b]`.
132136

133-
* [NamedPlus](https://github.com/mcabbott/NamedPlus.jl) has a macro: `@named [n^pow for n=1:10, pow=0:2:4]` has names & keys.
137+
* [NamedPlus](https://github.com/mcabbott/NamedPlus.jl) has a macro which works on comprehensions:
138+
`@named [n^pow for n=1:10, pow=0:2:4]` has names and keys.
134139

135140
### Absent
136141

src/AxisKeys.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ include("wrap.jl")
1010
export wrapdims
1111

1212
include("selectors.jl")
13-
export Near, Index, Interval
13+
export Near, Index, Interval, Not
1414

1515
include("functions.jl")
1616

src/selectors.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11

2+
using InvertedIndices # needs only Base.to_indices in struct.jl to work
3+
24
using IntervalSets
35

46
findindex(s::Interval, r::AbstractVector) = findall(i -> i in s, r)
@@ -55,7 +57,7 @@ struct Index{T} <: Selector{T}
5557
ind::T
5658
end
5759

58-
Base.show(io::IO, s::Index{T}) where {T} = print(io, "Index(",s.ind, ")")
60+
Base.show(io::IO, s::Index{T}) where {T} = print(io, "Index[",s.ind, "]")
5961

6062
Base.getindex(::Type{Index}, i) = Index(i)
6163

src/struct.jl

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,9 @@ for (bget, rget, cpy) in [(:getindex, :keys_getindex, :copy), (:view, :keys_view
7171
@inbounds Base.$bget(parent(A), I)
7272
end
7373

74-
@inline @propagate_inbounds function Base.$bget(A::KeyedArray, I...)
74+
@inline @propagate_inbounds function Base.$bget(A::KeyedArray, Iraw...)
75+
I = Base.to_indices(A, Iraw) # allows InvertedIndices.jl
76+
7577
@boundscheck checkbounds(parent(A), I...)
7678
data = @inbounds Base.$bget(parent(A), I...)
7779

@@ -128,13 +130,15 @@ see `Nearest` and `Index`.
128130

129131
@inline function getkey(A, args...)
130132
if length(args) == ndims(A)
131-
inds = map(findindex, args, axiskeys(A))
133+
inds_raw = map(findindex, args, axiskeys(A))
134+
inds = Base.to_indices(A, inds_raw)
132135
@boundscheck checkbounds(A, inds...)
133136
return @inbounds get_or_view(A, inds...)
134137

135138
elseif length(args) > ndims(A) && all(args[ndims(A)+1:end] .== (:)) # trailing colons
136139
args_nd = args[1:ndims(A)]
137-
inds = map(findindex, args_nd, axiskeys(A))
140+
inds_raw = map(findindex, args_nd, axiskeys(A))
141+
inds = Base.to_indices(A, inds_raw)
138142
@boundscheck checkbounds(A, inds...)
139143
if inds isa NTuple{<:Any, Int}
140144
return @inbounds view(keyless(A), inds...) # zero-dim view of underlying

test/_packages.jl

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
using Test, AxisKeys
2-
using OffsetArrays, UniqueVectors, Tables, LazyStack, Dates
2+
using OffsetArrays, UniqueVectors, Tables, LazyStack, Dates, InvertedIndices
33

44
@testset "offset" begin
55

@@ -66,4 +66,14 @@ end
6666
# But steps of Year(1) don't work, https://github.com/JuliaLang/julia/issues/35203
6767

6868
end
69+
@testset "inverted" begin
6970

71+
K = wrapdims(rand(4,5))
72+
@test K[:, Not(4)] == K[:, vcat(1:3, 5)] == K(:, !=(4))
73+
@test K[Not(1,4), :] == K[2:3, :] == K(r -> 2<=r<=3, :)
74+
75+
N = wrapdims(rand(Int8, 2,3,4), a=[:one, :two], b='α':'γ', c=31:34)
76+
@test N[b=Not(2)] == N[:,[1,3],:] == N(b=!=('β')) == N(:,['α','γ'],:)
77+
@test N[c=Not(2,4)] == N(c=Index[Not(2,4)])
78+
79+
end

0 commit comments

Comments
 (0)