AxisKeys.Index
— TypeIndex[i]
This exists to let you mix in square-bracket indexing, like A(:b, Near(3.14), Index[4:5], "f")
. You may also write Index[end]
, although not yet Index[end-2]
.
Key(val)
This exists to perform lookup inside indexing, to allow A[Key(:b), Near(3.14), 4:5, Key("f")]
.
Writing Key(isequal(:b))
is equivalent to just isequal(:b)
, and will find all matches, while Key(:b)
finds only the first (and drops the dimension).
Examples
julia> v = KeyedArray(Symbol.('a':'e'), 10:10:50)
1-dimensional KeyedArray(...) with keys:
↓ 5-element StepRange{Int64,...}
And data, 5-element Vector{Symbol}:
(10) :a
(20) :b
(30) :c
(40) :d
(50) :e
julia> v[Key(30)] == v(30) == v(Index[3]) == v[3]
true
julia> v[==(30)] == v(Index[3:3]) == v[3:3]
true
AxisKeys.Key
— TypeIndex[i]
This exists to let you mix in square-bracket indexing, like A(:b, Near(3.14), Index[4:5], "f")
. You may also write Index[end]
, although not yet Index[end-2]
.
Key(val)
This exists to perform lookup inside indexing, to allow A[Key(:b), Near(3.14), 4:5, Key("f")]
.
Writing Key(isequal(:b))
is equivalent to just isequal(:b)
, and will find all matches, while Key(:b)
finds only the first (and drops the dimension).
Examples
julia> v = KeyedArray(Symbol.('a':'e'), 10:10:50)
1-dimensional KeyedArray(...) with keys:
↓ 5-element StepRange{Int64,...}
And data, 5-element Vector{Symbol}:
(10) :a
(20) :b
(30) :c
(40) :d
(50) :e
julia> v[Key(30)] == v(30) == v(Index[3]) == v[3]
true
julia> v[==(30)] == v(Index[3:3]) == v[3:3]
true
AxisKeys.KeyedArray
— Method(A::KeyedArray)("a", 2.0, :γ) == A[1, 2, 3]
A(:γ) == view(A, :, :, 3)
KeyedArray
s are callable, and this behaves much like indexing, except that it searches for the given keys in axiskeys(A)
, instead of axes(A)
for indices.
A single key may be used to indicate a slice, provided that its type only matches the eltype of one axiskeys(A,d)
. You can also slice explicitly with A("a", :, :)
, both of these return a view
.
An extra trailing colon (when all other indices are fixed) will return a zero-dimensional view
. This allows setting one value by writing A("a", 2.0, :γ, :) .= 100
.
Also accepts functions like A(<=(2.0))
and selectors, see Nearest
and Index
.
AxisKeys.KeyedArray
— MethodKeyedArray(A; i=2:3, j=["a", "b"])
NamedDimsArray(A; i=2:3, j=["a", "b"])
These constructors make KeyedArray(NamedDimsArray(A, names), keys)
or NamedDimsArray(KeyedArray(A, keys), names)
, which should be equivalent.
These perform less sanity checking than wrapdims(A; kw...)
.
Examples
julia> KeyedArray(reshape(1:12,3,4), row=[:a, :b, :c], iter=10:10:40)
2-dimensional KeyedArray(NamedDimsArray(...)) with keys:
↓ row ∈ 3-element Vector{Symbol}
→ iter ∈ 4-element StepRange{Int64,...}
And data, 3×4 reshape(::UnitRange{Int64}, 3, 4) with eltype Int64:
(10) (20) (30) (40)
(:a) 1 4 7 10
(:b) 2 5 8 11
(:c) 3 6 9 12
julia> ans[iter=3]
1-dimensional KeyedArray(NamedDimsArray(...)) with keys:
↓ row ∈ 3-element Vector{Symbol}
And data, 3-element Vector{Int64}:
(:a) 7
(:b) 8
(:c) 9
AxisKeys.Near
— TypeNear(val)
Interval(lo, hi)
These selectors modify lookup using axiskeys(A)
: B(time = Near(3))
matches one entry with minimum abs(t-3)
of named dimension :time
. C("cat", Interval(10,20))
matches all entries with 10 <= iter <= 20
).
Interval
is from IntervalSets.jl, and using that you may also write lo .. hi
, as well as mid ± δ
.
==(val)
<(val)
Any functions can be used similarly, like C(!=("dog"), <=(33)). They ultimately call findall(==(val), axiskeys(A,d))
.
Functions of type Base.Fix2
, and Selector
s, also allow a dimension to be chosen by type: A(<=(3.1))
will work provided that only one of map(eltype, axiskeys(A))
matches typeof(3.1)
.
Examples
julia> v = KeyedArray(Symbol.('a':'e'), 10:10:50)
1-dimensional KeyedArray(...) with keys:
↓ 5-element StepRange{Int64,...}
And data, 5-element Vector{Symbol}:
(10) :a
(20) :b
(30) :c
(40) :d
(50) :e
julia> v[Near(33)]
:c
julia> v[==(30)] # all matching this key
1-dimensional KeyedArray(...) with keys:
↓ 1-element Vector{Int64}
And data, 1-element Vector{Symbol}:
(30) :c
julia> v[Interval(17, 31)]
1-dimensional KeyedArray(...) with keys:
↓ 2-element StepRange{Int64,...}
And data, 2-element Vector{Symbol}:
(20) :b
(30) :c
julia> m = wrapdims(hcat(v,v), x=nothing, y=[:left, :right]);
julia> m(<(30)) # selects 1st dim by type, and makes a view
2-dimensional KeyedArray(NamedDimsArray(...)) with keys:
↓ x ∈ 2-element StepRange{Int64,...}
→ y ∈ 2-element Vector{Symbol}
And data, 2×2 view(::Matrix{Symbol}, 1:2, :) with eltype Symbol:
(:left) (:right)
(10) :a :a
(20) :b :b
AxisKeys.Selector
— TypeNear(val)
Interval(lo, hi)
These selectors modify lookup using axiskeys(A)
: B(time = Near(3))
matches one entry with minimum abs(t-3)
of named dimension :time
. C("cat", Interval(10,20))
matches all entries with 10 <= iter <= 20
).
Interval
is from IntervalSets.jl, and using that you may also write lo .. hi
, as well as mid ± δ
.
==(val)
<(val)
Any functions can be used similarly, like C(!=("dog"), <=(33)). They ultimately call findall(==(val), axiskeys(A,d))
.
Functions of type Base.Fix2
, and Selector
s, also allow a dimension to be chosen by type: A(<=(3.1))
will work provided that only one of map(eltype, axiskeys(A))
matches typeof(3.1)
.
Examples
julia> v = KeyedArray(Symbol.('a':'e'), 10:10:50)
1-dimensional KeyedArray(...) with keys:
↓ 5-element StepRange{Int64,...}
And data, 5-element Vector{Symbol}:
(10) :a
(20) :b
(30) :c
(40) :d
(50) :e
julia> v[Near(33)]
:c
julia> v[==(30)] # all matching this key
1-dimensional KeyedArray(...) with keys:
↓ 1-element Vector{Int64}
And data, 1-element Vector{Symbol}:
(30) :c
julia> v[Interval(17, 31)]
1-dimensional KeyedArray(...) with keys:
↓ 2-element StepRange{Int64,...}
And data, 2-element Vector{Symbol}:
(20) :b
(30) :c
julia> m = wrapdims(hcat(v,v), x=nothing, y=[:left, :right]);
julia> m(<(30)) # selects 1st dim by type, and makes a view
2-dimensional KeyedArray(NamedDimsArray(...)) with keys:
↓ x ∈ 2-element StepRange{Int64,...}
→ y ∈ 2-element Vector{Symbol}
And data, 2×2 view(::Matrix{Symbol}, 1:2, :) with eltype Symbol:
(:left) (:right)
(10) :a :a
(20) :b :b
AxisKeys.Vectorator
— TypeVectorator(iter)
Wrapper for iterators which ensures they do not have an n-dimensional size. Tries to ensure that collect(Vectorator(iter)) == vec(collect(iter))
.
NamedDims.NamedDimsArray
— MethodKeyedArray(A; i=2:3, j=["a", "b"])
NamedDimsArray(A; i=2:3, j=["a", "b"])
These constructors make KeyedArray(NamedDimsArray(A, names), keys)
or NamedDimsArray(KeyedArray(A, keys), names)
, which should be equivalent.
These perform less sanity checking than wrapdims(A; kw...)
.
Examples
julia> KeyedArray(reshape(1:12,3,4), row=[:a, :b, :c], iter=10:10:40)
2-dimensional KeyedArray(NamedDimsArray(...)) with keys:
↓ row ∈ 3-element Vector{Symbol}
→ iter ∈ 4-element StepRange{Int64,...}
And data, 3×4 reshape(::UnitRange{Int64}, 3, 4) with eltype Int64:
(10) (20) (30) (40)
(:a) 1 4 7 10
(:b) 2 5 8 11
(:c) 3 6 9 12
julia> ans[iter=3]
1-dimensional KeyedArray(NamedDimsArray(...)) with keys:
↓ row ∈ 3-element Vector{Symbol}
And data, 3-element Vector{Int64}:
(:a) 7
(:b) 8
(:c) 9
AxisKeys.findindex
— Methodfindindex(key, vec)
This is usually findfirst(isequal(key), vec)
, and will error if it finds nothing
. But it also understands findindex(:, vec) = (:)
, and findindex(array, vec) = vcat((findindex(x, vec) for x in array)...)
.
It also understands functions findindex(<(4), vec) = findall(x -> x<4, vec)
, and selectors like Nearest(key)
and Interval(lo,hi)
.
AxisKeys.inferdim
— Methodinferdim(key, axiskeys::Tuple)
When you call A(key)
for ndims(A) > 1
, this returns which d
you meant, if unambigous, by comparing types & gradually widening.
AxisKeys.named_axiskeys
— Methodnamed_axiskeys(arr)::NamedTuple
Return the axiskeys
along with their names. If there are duplicate names or unnamed axes, an error is thrown.
julia> using AxisKeys
julia> arr = KeyedArray(rand(1,3), x=[1], y=[2,3,4]);
julia> named_axiskeys(arr)
(x = [1], y = [2, 3, 4])
AxisKeys.populate!
— MethodAxisKeys.populate!(A, table, value; force=false)
Populate A
with the contents of the value
column in a provided table
, matching the Tables.jl API. The table
must contain columns corresponding to the keys in A
and implement Tables.columns
. If the keys in A
do not uniquely identify rows in the table
then an ArgumentError
is throw. If force
is true then the duplicate (non-unique) entries will be overwritten.
AxisKeys.rekey
— Methodrekey(A, (1:10, [:a, :b]))
rekey(A, 2 => [:a, :b])
rekey(A, :y => [:a, :b])
Rekey a KeyedArray via Tuple
s or Pair
s, dim => newkey
. If A
also has named dimensions then you can also pass dimname => newkey
.
AxisKeys.select_to_indices
— MethodBase.to_indices(A, axes, inds)
select_to_indices(A, axes, inds)
This recursively peels off the indices & axes, select_to_indices
gets called when the first remaining index is a Selector, Interval, or a Function.
AxisKeys.sortkeys
— Methodsortslices(A; dims)
sortkeys(A; dims=1:ndims(A))
Base.sortslices
sorts the corresponding keys too, along one dimension. Calls its own implementation, roughly p = sortperm(eachslice(A))
, with default keyword by=vec
to make this work on slices of any shape.
sortkeys(A)
instead sorts everything by the keys. Works along any number of dimensions, by detault all of them.
AxisKeys.who_wins
— Methodwho_wins(axisranges(A,1), axisranges(B,1))
who_wins(r, s, t, ...)
For broadcasting, but also map
etc, this compares individual key vectors and returns the one to keep. In general they must agree ==
, and the simpler type will be returned (e.g. Vector + UnitRange -> UnitRange
).
However default key vectors Base.OneTo(n)
are regarded as wildcards. They need not agree with anyone, and are always discarded in favour of other types.
If the keys disagree it returns nothing
. Call unify_one()
to have an error instead.
AxisKeys.wrapdims
— Functionwrapdims(A::NamedArray)
wrapdims(A::AxisArray)
Converts the wrapper from packages NamedArrays.jl or AxisArrays.jl. (Really it just guesses based on field names, since these packages are not loaded.)
Examples
julia> using FreqTables, AxisKeys
julia> xs = vcat(repeat([1,2,3],4), [2,2,2,3]);
julia> ys = repeat('a':'d', 4);
julia> freqtable(xs, ys)
3×4 Named Matrix{Int64}
Dim1 ╲ Dim2 │ 'a' 'b' 'c' 'd'
────────────┼───────────────────
1 │ 1 1 1 1
2 │ 2 2 2 1
3 │ 1 1 1 2
julia> wrapdims(ans)
2-dimensional KeyedArray(NamedDimsArray(...)) with keys:
↓ Dim1 ∈ 3-element Vector{Int64}
→ Dim2 ∈ 4-element Vector{Char}
And data, 3×4 Matrix{Int64}:
('a') ('b') ('c') ('d')
(1) 1 1 1 1
(2) 2 2 2 1
(3) 1 1 1 2
AxisKeys.wrapdims
— Methodwrapdims(A, T, keyvecs...)
wrapdims(A, T; name=keyvec...)
This applies type T
to all of the keys, for example to wrap them as UniqueVector
s or AcceleratedArray
s (using those packages) for fast lookup.
AxisKeys.wrapdims
— Methodwrapdims(A, :i, :j)
wrapdims(A, 1:10, ['a', 'b', 'c'])
wrapdims(A, i=1:10, j=['a', 'b', 'c'])
Convenience function for constructing either a NamedDimsArray
, a KeyedArray
, or a nested pair of both.
Performs some sanity checks which are skipped by KeyedArray
constructor:
- Giving
nothing
instead of keys will result inaxiskeys(A,d) == axes(A,d)
. - Given an
AbstractRange
of the wrong length, it will adjust the end of this, and give a warning. - Given
A::OffsetArray
and key vectors which are not, it will wrap them so thataxes.(axiskeys(A_wrapped)) == axes(A)
.
By default it wraps in this order: KeyedArray{...,NamedDimsArray{...}}
, which you can change by re-defining AxisKeys.nameouter() == true
.
Examples
julia> wrapdims([1,10,100], pow=0:99)
┌ Warning: range 0:99 replaced by 0:2, to match size(A, 1) == 3
└ @ AxisKeys ~/.julia/dev/AxisKeys/src/wrap.jl:50
1-dimensional KeyedArray(NamedDimsArray(...)) with keys:
↓ pow ∈ 3-element UnitRange{Int64}
And data, 3-element Vector{Int64}:
(0) 1
(1) 10
(2) 100
julia> push!(ans, 1000)
1-dimensional KeyedArray(NamedDimsArray(...)) with keys:
↓ pow ∈ 4-element UnitRange{Int64}
And data, 4-element Vector{Int64}:
(0) 1
(1) 10
(2) 100
(3) 1000
AxisKeys.wrapdims
— Methodwrapdims(table, value, names...; default=undef, sort=false, force=false)
Construct KeyedArray(NamedDimsArray(A,names),keys)
from a table
matching the Tables.jl API. (It must support both Tables.columns
and Tables.rows
.)
The contents of the array is taken from the column value::Symbol
of the table. Each symbol in names
specifies a column whose unique entries become the keys along a dimenension of the array.
If there is no row in the table matching a possible set of keys, then this element of the array is undefined, unless you provide the default
keyword. If several rows share the same set of keys, then by default an ArgumentError
is thrown. Keyword force=true
will instead cause these non-unique entries to be overwritten.
See also populate!
to fill an existing array in the same manner.
Setting AxisKeys.nameouter() = false
will reverse the order of wrappers produced.
Examples
julia> using DataFrames, AxisKeys
julia> df = DataFrame("a" => 1:3, "b" => 10:12.0, "c" => ["cat", "dog", "cat"])
3×3 DataFrame
Row │ a b c
│ Int64 Float64 String
─────┼────────────────────────
1 │ 1 10.0 cat
2 │ 2 11.0 dog
3 │ 3 12.0 cat
julia> wrapdims(df, :a, :b, :c; default=missing)
2-dimensional KeyedArray(NamedDimsArray(...)) with keys:
↓ b ∈ 3-element Vector{Float64}
→ c ∈ 2-element Vector{String}
And data, 3×2 Matrix{Union{Missing, Int64}}:
("cat") ("dog")
(10.0) 1 missing
(11.0) missing 2
(12.0) 3 missing
julia> wrapdims(df, :a, :b)
1-dimensional NamedDimsArray(KeyedArray(...)) with keys:
↓ b ∈ 3-element Vector{Float64}
And data, 3-element Vector{Union{Missing, Int64}}:
(10.0) 1
(11.0) 2
(12.0) 3
julia> wrapdims(df, :a, :c)
ERROR: ArgumentError: Key ("cat",) is not unique
julia> wrapdims(df, :a, :c, force=true)
1-dimensional NamedDimsArray(KeyedArray(...)) with keys:
↓ c ∈ 2-element Vector{String}
And data, 2-element Vector{Int64}:
("cat") 3
("dog") 2
AxisKeys.wrapdims
— Methodwrapdims(df, UniqueVector, :val, :x, :y)
Converts at Tables.jl table to a KeyedArray
+ NamedDimsArray
pair, using column :val
for values, and columns :x, :y
for names & keys. Optional 2nd argument applies this type to all the key-vectors.
AxisKeys.wrapdims
— Methodwrapdims(::NamedTuple)
wrapdims(::NamedTuple, ::Symbol)
Converts the NamedTuple
's keys into those of a one-dimensional KeyedArray
. If a dimension name is provided, the this adds a NamedDimsArray
wrapper too.
Examples
julia> wrapdims((alpha=1, beta=20))
1-dimensional KeyedArray(...) with keys:
↓ 2-element Vector{Symbol}
And data, 2-element Vector{Int64}:
(:alpha) 1
(:beta) 20
julia> push!(ans, :gamma => 300)
1-dimensional KeyedArray(...) with keys:
↓ 3-element Vector{Symbol}
And data, 3-element Vector{Int64}:
(:alpha) 1
(:beta) 20
(:gamma) 300
Base.push!
— Methodpush!(A::KeyedArray, val)
This adds val
to the end of pareent(A)
, and attempts to extend axiskeys(A,1)
by one.
Base.push!
— Methodpush!(A::KeyedArray; key = val)
push!(A::KeyedArray, key => val)
This pushes val
into A.data
, and pushes key
to axiskeys(A,1)
. Both of these must be legal operations, e.g. A = wrapdims([1], ["a"]); push!(A, b=2)
.
Base.sortslices
— Methodsortslices(A; dims)
sortkeys(A; dims=1:ndims(A))
Base.sortslices
sorts the corresponding keys too, along one dimension. Calls its own implementation, roughly p = sortperm(eachslice(A))
, with default keyword by=vec
to make this work on slices of any shape.
sortkeys(A)
instead sorts everything by the keys. Works along any number of dimensions, by detault all of them.