Skip to content

Commit e3c37ea

Browse files
committed
Add support for KeyedArrays as a Tables sink.
1 parent e00e0fe commit e3c37ea

File tree

2 files changed

+95
-8
lines changed

2 files changed

+95
-8
lines changed

src/tables.jl

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,65 @@ end
115115

116116
# end
117117
# end
118+
119+
"""
120+
populate!(A, table, value)
121+
122+
Populate A with the contents of the `value` column in a provided table.
123+
The provided table contain columns corresponding to the keys in A and support row iteration.
124+
"""
125+
function populate!(A, table, value::Symbol)
126+
cols = [value, dimnames(A)...]
127+
for r in Tables.rows(table)
128+
setkey!(A, (Tables.getcolumn(r, c) for c in cols)...)
129+
end
130+
return A
131+
end
132+
133+
"""
134+
KeyedArray(table, value, keys...; default=undef)
135+
NamedDimsArray(table, value, keys...; default=undef)
136+
137+
Construct a KeyedArray/NamedDimsArray from a `table` supporting column and row.
138+
`keys` columns are extracted as is, without sorting, and are assumed to uniquely identify
139+
each `value`. The `default` value is used in cases where no value is identified for a given
140+
keypair.
141+
"""
142+
function KeyedArray(table, value::Symbol, keys::Symbol...; kwargs...)
143+
return _construct_from_table(KeyedArray, table, value, keys...; kwargs...)
144+
end
145+
146+
function NamedDimsArray(table, value::Symbol, keys::Symbol...; kwargs...)
147+
return _construct_from_table(NamedDimsArray, table, value, keys...; kwargs...)
148+
end
149+
150+
151+
# Internal function for constructing the KeyedArray or NamedDimsArray.
152+
# This code doesn't care which type we produce so we just pass that along.
153+
function _construct_from_table(
154+
T::Type, table, value::Symbol, keys::Symbol...;
155+
default=undef, issorted=false
156+
)
157+
# get columns of the input table source
158+
cols = Tables.columns(table)
159+
160+
# Extract key columns
161+
kw = Tuple(k => unique(Tables.getcolumn(cols, k)) for k in keys)
162+
163+
# Extract data/value column
164+
vals = Tables.getcolumn(cols, value)
165+
166+
# Initialize the KeyedArray
167+
sz = length.(last.(kw))
168+
169+
A = if default === undef
170+
data = similar(vals, sz)
171+
else
172+
data = similar(vals, Union{eltype(vals), typeof(default)}, sz)
173+
fill!(data, default)
174+
end
175+
176+
A = T(data; kw...)
177+
populate!(A, table, value)
178+
return A
179+
end

test/_packages.jl

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,39 @@ end
2929
@testset "tables" begin
3030
using Tables
3131

32-
R = wrapdims(rand(2,3), 11:12, 21:23)
33-
N = wrapdims(rand(2,3), a=[11, 12], b=[21, 22, 23.0])
34-
35-
@test keys(first(Tables.rows(R))) == (:dim_1, :dim_2, :value)
36-
@test keys(first(Tables.rows(N))) == (:a, :b, :value)
37-
38-
@test Tables.columns(N).a == [11, 12, 11, 12, 11, 12]
39-
32+
@testset "source" begin
33+
R = wrapdims(rand(2,3), 11:12, 21:23)
34+
N = wrapdims(rand(2,3), a=[11, 12], b=[21, 22, 23.0])
35+
36+
@test keys(first(Tables.rows(R))) == (:dim_1, :dim_2, :value)
37+
@test keys(first(Tables.rows(N))) == (:a, :b, :value)
38+
39+
@test Tables.columns(N).a == [11, 12, 11, 12, 11, 12]
40+
end
41+
@testset "sink" begin
42+
A = KeyedArray(rand(24, 11, 3); :time => 0:23, :loc => -5:5, :id => ["a", "b", "c"])
43+
table = Tables.columntable(A)
44+
45+
# Test fully constructing from a table
46+
# Common when working with adhoc data
47+
B = KeyedArray(table, :value, :time, :loc, :id)
48+
@test B == A
49+
50+
# Test populating an existing array (e.g., expected data based on calculated targets/offsets)
51+
C = KeyedArray(
52+
zeros(Float64, size(A));
53+
time = unique(table.time),
54+
loc = unique(table.loc),
55+
id = unique(table.id),
56+
)
57+
@test C != A
58+
AxisKeys.populate!(C, table, :value)
59+
@test C == A
60+
61+
# Constructing a NamedDimsArray with different default value and table type
62+
D = NamedDimsArray(Tables.rowtable(A), :value, :time, :loc, :id; default=missing)
63+
@test D == A
64+
end
4065
end
4166
@testset "stack" begin
4267
using LazyStack

0 commit comments

Comments
 (0)