Skip to content

Commit 877cc80

Browse files
authored
Merge pull request #1 from mcabbott/pull21
PR to PR#21
2 parents f15618e + ab14395 commit 877cc80

File tree

4 files changed

+62
-25
lines changed

4 files changed

+62
-25
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,9 @@ while `A.keys isa Tuple` for matrices & higher. But `axiskeys(A)` always returns
140140
* Named tuples can be converted to and from keyed vectors,
141141
with `collect(keys(nt)) == Symbol.(axiskeys(V),1)`
142142

143+
* The [Tables.jl](https://github.com/JuliaData/Tables.jl) interface is supported,
144+
with `wrapdims(df, :val, :x, :y)` creating a matrix from 3 columns.
145+
143146
* [FFTW](https://github.com/JuliaMath/FFTW.jl)`.fft` transforms the keys;
144147
if these are times such as [Unitful](https://github.com/PainterQubits/Unitful.jl)`.s`
145148
then the results are fequency labels. ([PR#15](https://github.com/mcabbott/AxisKeys.jl/pull/15).)

src/tables.jl

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -149,45 +149,70 @@ function populate!(A, table, value::Symbol; force=false)
149149
end
150150

151151
"""
152-
wrapdims(table, value, keys...; default=undef, sort=false, force=false) -> KeyedArray
153-
wrapdims(T, table, value, keys...; default=undef, sort=false, force=false) -> T
154-
155-
Construct a `KeyedArray`/`NamedDimsArray` (specified by type `T`) from a `table` matching
156-
the [Tables.jl](https://github.com/JuliaData/Tables.jl) API. The `table` should support both
157-
`Tables.columns` and `Tables.rows`. The `default` value is used in cases where no
158-
value is identified for a given keypair. If the `keys` columns do not uniquely identify
159-
rows in the table then an `ArgumentError` is throw. If `force` is true then the duplicate
160-
(non-unique) entries will be overwritten.
152+
wrapdims(table, value, names...; default=undef, sort=false, force=false)
153+
154+
Construct `KeyedArray(NamedDimsArray(A,names),keys)` from a `table` matching
155+
the [Tables.jl](https://github.com/JuliaData/Tables.jl) API.
156+
(It must support both `Tables.columns` and `Tables.rows`.)
157+
158+
The contents of the array is taken from the column `value::Symbol` of the table.
159+
Each symbol in `names` specifies a column whose unique entries
160+
become the keys along a dimenension of the array.
161+
162+
If there is no row in the table matching a possible set of keys,
163+
then this element of the array is undefined, unless you provide the `default` keyword.
164+
If several rows share the same set of keys, then by default an `ArgumentError` is thrown.
165+
Keyword `force=true` will instead cause these non-unique entries to be overwritten.
166+
167+
Setting `AxisKeys.nameouter() = false` will reverse the order of wrappers produced.
168+
"""
169+
function wrapdims(table, value::Symbol, names::Symbol...; kw...)
170+
if nameouter() == false
171+
_wrap_table(KeyedArray, identity, table, value, names...; kw...)
172+
else
173+
_wrap_table(NamedDimsArray, identity, table, value, names...; kw...)
174+
end
175+
end
176+
177+
"""
178+
wrapdims(df, UniqueVector, :val, :x, :y)
179+
180+
Converts at Tables.jl table to a `KeyedArray` + `NamedDimsArray` pair,
181+
using column `:val` for values, and columns `:x, :y` for names & keys.
182+
Optional 2nd argument applies this type to all the key-vectors.
161183
"""
162-
function wrapdims(table, value::Symbol, keys::Symbol...; kwargs...)
163-
wrapdims(KeyedArray, table, value, keys...; kwargs...)
184+
function wrapdims(table, KT::Type, value::Symbol, names::Symbol...; kw...)
185+
if nameouter() == false
186+
_wrap_table(KeyedArray, KT, table, value, names...; kw...)
187+
else
188+
_wrap_table(NamedDimsArray, KT, table, value, names...; kw...)
189+
end
164190
end
165191

166-
function wrapdims(T::Type, table, value::Symbol, keys::Symbol...; default=undef, sort::Bool=false, kwargs...)
192+
function _wrap_table(AT::Type, KT, table, value::Symbol, names::Symbol...; default=undef, sort::Bool=false, kwargs...)
167193
# get columns of the input table source
168194
cols = Tables.columns(table)
169195

170196
# Extract key columns
171-
pairs = map(keys) do k
197+
pairs = map(names) do k
172198
col = unique(Tables.getcolumn(cols, k))
173199
sort && Base.sort!(col)
174-
return k => col
200+
return k => KT(col)
175201
end
176202

177203
# Extract data/value column
178204
vals = Tables.getcolumn(cols, value)
179205

180206
# Initialize the KeyedArray
181207
sz = length.(last.(pairs))
182-
183-
A = if default === undef
208+
if default === undef
184209
data = similar(vals, sz)
185210
else
186211
data = similar(vals, Union{eltype(vals), typeof(default)}, sz)
187212
fill!(data, default)
188213
end
214+
A = AT(data; pairs...)
189215

190-
A = T(data; pairs...)
191216
populate!(A, table, value; kwargs...)
192217
return A
193218
end

test/_packages.jl

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,19 @@ end
3939
@test Tables.columns(N).a == [11, 12, 11, 12, 11, 12]
4040
end
4141
@testset "sink" begin
42-
A = KeyedArray(rand(24, 11, 3); :time => 0:23, :loc => -5:5, :id => ["a", "b", "c"])
42+
A = KeyedArray(rand(24, 11, 3); time = 0:23, loc = -5:5, id = ["a", "b", "c"])
4343
table = Tables.columntable(A)
4444

4545
# Test fully constructing from a table
4646
# Common when working with adhoc data
4747
B = wrapdims(table, :value, :time, :loc, :id)
4848
@test B == A
4949

50+
# Test wrapping of key vectors, and wrong order:
51+
U = wrapdims(table, UniqueVector, :value, :id, :time, :loc)
52+
@test axiskeys(U, :time) isa UniqueVector
53+
@test U(time=3, id="b") == A(time=3, id="b")
54+
5055
# Test populating an existing array (e.g., expected data based on calculated targets/offsets)
5156
C = KeyedArray(
5257
zeros(Float64, size(A));
@@ -60,14 +65,18 @@ end
6065

6166
# Constructing a NamedDimsArray with different default value and table type
6267
# Partial populating
63-
table = Tables.rowtable(A)
64-
n = length(table)
68+
r_table = Tables.rowtable(A)
69+
n = length(r_table)
6570
idx = rand(Bool, n)
66-
D = wrapdims(table[idx], :value, :time, :loc, :id; default=missing)
71+
D = wrapdims(r_table[idx], :value, :time, :loc, :id; default=missing)
6772
# dimnames should still match, but we'll have missing values
6873
@test dimnames(D) == dimnames(A)
6974
@test any(ismissing, D)
7075

76+
# BTW, this is why it's a method of wrapdims, not KeyedArray:
77+
# @code_warntype wrapdims(table, :value, :time, :loc, :id) # ::Any
78+
# @code_warntype wrapdims(r_table[idx], :value, :time, :loc, :id; default=missing)
79+
7180
# Construction with invalid columns error as expected, but the specific error is
7281
# dependent on the table type.
7382
# ERROR: ArgumentError: wrong number of names, got (:q, :time, :loc, :id) with ndims(A) == 1
@@ -82,7 +91,7 @@ end
8291
# Construction with duplicates
8392
# ERROR: ArgumentError: Key (Date("2019-01-01"), -5) is not unique
8493
@test_throws ArgumentError wrapdims(table, :value, :time, :loc)
85-
@test wrapdims(table, :value, :time, :loc; force=true) == C(:, :, Key("c"))
94+
@test wrapdims(r_table, :value, :time, :loc; force=true) == C(:, :, Key("c"))
8695
end
8796
end
8897
@testset "stack" begin

test/runtests.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ using Statistics, OffsetArrays, Tables, UniqueVectors, LazyStack
99
AxisKeys.nameouter() = false
1010
end
1111

12-
# include("_basic.jl")
12+
include("_basic.jl")
1313

1414
include("_functions.jl")
1515

16-
# include("_fast.jl")
16+
include("_fast.jl")
1717

18-
# include("_packages.jl")
18+
include("_packages.jl")
1919

2020
end
2121
@testset "fast findfirst & findall" begin

0 commit comments

Comments
 (0)