AxisKeys.IndexType
Index[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.KeyType
Index[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.KeyedArrayMethod
(A::KeyedArray)("a", 2.0, :γ) == A[1, 2, 3]
A(:γ) == view(A, :, :, 3)

KeyedArrays 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.KeyedArrayMethod
KeyedArray(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.NearType
Near(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 Selectors, 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.SelectorType
Near(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 Selectors, 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.VectoratorType
Vectorator(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.NamedDimsArrayMethod
KeyedArray(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.findindexMethod
findindex(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.inferdimMethod
inferdim(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_axiskeysMethod
named_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!Method
AxisKeys.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.rekeyMethod
rekey(A, (1:10, [:a, :b]))
rekey(A, 2 => [:a, :b])
rekey(A, :y => [:a, :b])

Rekey a KeyedArray via Tuples or Pairs, dim => newkey. If A also has named dimensions then you can also pass dimname => newkey.

AxisKeys.select_to_indicesMethod
Base.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.sortkeysMethod
sortslices(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_winsMethod
who_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.wrapdimsFunction
wrapdims(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.wrapdimsMethod
wrapdims(A, T, keyvecs...)
wrapdims(A, T; name=keyvec...)

This applies type T to all of the keys, for example to wrap them as UniqueVectors or AcceleratedArrays (using those packages) for fast lookup.

AxisKeys.wrapdimsMethod
wrapdims(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 in axiskeys(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 that axes.(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.wrapdimsMethod
wrapdims(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.wrapdimsMethod
wrapdims(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.wrapdimsMethod
wrapdims(::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!Method
push!(A::KeyedArray, val)

This adds val to the end of pareent(A), and attempts to extend axiskeys(A,1) by one.

Base.push!Method
push!(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.sortslicesMethod
sortslices(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.