From e3c37ea11c41e73557c4d9649f77a3ba219b9cef Mon Sep 17 00:00:00 2001 From: rofinn Date: Mon, 14 Sep 2020 21:10:30 -0500 Subject: [PATCH 1/6] Add support for KeyedArrays as a Tables sink. --- src/tables.jl | 62 +++++++++++++++++++++++++++++++++++++++++++++++ test/_packages.jl | 41 +++++++++++++++++++++++++------ 2 files changed, 95 insertions(+), 8 deletions(-) diff --git a/src/tables.jl b/src/tables.jl index ec03c3e..4850eb0 100644 --- a/src/tables.jl +++ b/src/tables.jl @@ -115,3 +115,65 @@ end # end # end + +""" + populate!(A, table, value) + +Populate A with the contents of the `value` column in a provided table. +The provided table contain columns corresponding to the keys in A and support row iteration. +""" +function populate!(A, table, value::Symbol) + cols = [value, dimnames(A)...] + for r in Tables.rows(table) + setkey!(A, (Tables.getcolumn(r, c) for c in cols)...) + end + return A +end + +""" + KeyedArray(table, value, keys...; default=undef) + NamedDimsArray(table, value, keys...; default=undef) + +Construct a KeyedArray/NamedDimsArray from a `table` supporting column and row. +`keys` columns are extracted as is, without sorting, and are assumed to uniquely identify +each `value`. The `default` value is used in cases where no value is identified for a given +keypair. +""" +function KeyedArray(table, value::Symbol, keys::Symbol...; kwargs...) + return _construct_from_table(KeyedArray, table, value, keys...; kwargs...) +end + +function NamedDimsArray(table, value::Symbol, keys::Symbol...; kwargs...) + return _construct_from_table(NamedDimsArray, table, value, keys...; kwargs...) +end + + +# Internal function for constructing the KeyedArray or NamedDimsArray. +# This code doesn't care which type we produce so we just pass that along. +function _construct_from_table( + T::Type, table, value::Symbol, keys::Symbol...; + default=undef, issorted=false +) + # get columns of the input table source + cols = Tables.columns(table) + + # Extract key columns + kw = Tuple(k => unique(Tables.getcolumn(cols, k)) for k in keys) + + # Extract data/value column + vals = Tables.getcolumn(cols, value) + + # Initialize the KeyedArray + sz = length.(last.(kw)) + + A = if default === undef + data = similar(vals, sz) + else + data = similar(vals, Union{eltype(vals), typeof(default)}, sz) + fill!(data, default) + end + + A = T(data; kw...) + populate!(A, table, value) + return A +end diff --git a/test/_packages.jl b/test/_packages.jl index 5d90449..5fc5a37 100644 --- a/test/_packages.jl +++ b/test/_packages.jl @@ -29,14 +29,39 @@ end @testset "tables" begin using Tables - R = wrapdims(rand(2,3), 11:12, 21:23) - N = wrapdims(rand(2,3), a=[11, 12], b=[21, 22, 23.0]) - - @test keys(first(Tables.rows(R))) == (:dim_1, :dim_2, :value) - @test keys(first(Tables.rows(N))) == (:a, :b, :value) - - @test Tables.columns(N).a == [11, 12, 11, 12, 11, 12] - + @testset "source" begin + R = wrapdims(rand(2,3), 11:12, 21:23) + N = wrapdims(rand(2,3), a=[11, 12], b=[21, 22, 23.0]) + + @test keys(first(Tables.rows(R))) == (:dim_1, :dim_2, :value) + @test keys(first(Tables.rows(N))) == (:a, :b, :value) + + @test Tables.columns(N).a == [11, 12, 11, 12, 11, 12] + end + @testset "sink" begin + A = KeyedArray(rand(24, 11, 3); :time => 0:23, :loc => -5:5, :id => ["a", "b", "c"]) + table = Tables.columntable(A) + + # Test fully constructing from a table + # Common when working with adhoc data + B = KeyedArray(table, :value, :time, :loc, :id) + @test B == A + + # Test populating an existing array (e.g., expected data based on calculated targets/offsets) + C = KeyedArray( + zeros(Float64, size(A)); + time = unique(table.time), + loc = unique(table.loc), + id = unique(table.id), + ) + @test C != A + AxisKeys.populate!(C, table, :value) + @test C == A + + # Constructing a NamedDimsArray with different default value and table type + D = NamedDimsArray(Tables.rowtable(A), :value, :time, :loc, :id; default=missing) + @test D == A + end end @testset "stack" begin using LazyStack From 5b5ce44276fcac818ba57d637bfce0405a79c2cf Mon Sep 17 00:00:00 2001 From: rofinn Date: Tue, 15 Sep 2020 13:20:20 -0500 Subject: [PATCH 2/6] Updates from code review --- src/tables.jl | 77 ++++++++++++++++++++++++++++++----------------- test/_packages.jl | 28 +++++++++++++++-- 2 files changed, 74 insertions(+), 31 deletions(-) diff --git a/src/tables.jl b/src/tables.jl index 4850eb0..8291732 100644 --- a/src/tables.jl +++ b/src/tables.jl @@ -117,54 +117,75 @@ end # end """ - populate!(A, table, value) + AxisKeys.populate!(A, table, value; force=false, quiet=false) -Populate A with the contents of the `value` column in a provided table. -The provided table contain columns corresponding to the keys in A and support row iteration. +Populate `A` with the contents of the `value` column in a provided `table`. +The `table` must contain columns corresponding to the keys in `A` and support row iteration. +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. Setting `quiet` to true will avoid any non-unique warnings. """ -function populate!(A, table, value::Symbol) - cols = [value, dimnames(A)...] +function populate!(A, table, value::Symbol; force=false, quiet=false) + # Use a BitArray mask to detect duplicates and error instead of overwriting. + mask = falses(size(A)) + overwritten = false + for r in Tables.rows(table) - setkey!(A, (Tables.getcolumn(r, c) for c in cols)...) + vals = Tuple(Tables.getcolumn(r, c) for c in dimnames(A)) + inds = map(findindex, vals, axiskeys(A)) + + # Handle duplicate error if applicable + if mask[inds...] + if force + overwritten = true + else + throw(ArgumentError("Key $vals is not unique")) + end + end + + # Set or mask marking that we've set this index + setindex!(mask, true, inds...) + + # Insert our value into the data array + setindex!(A, Tables.getcolumn(r, value), inds...) + end + + if overwritten && !quiet + @warn "Columns $(dimnames(A)) do not uniquely identify rows in the table" end + return A end """ - KeyedArray(table, value, keys...; default=undef) - NamedDimsArray(table, value, keys...; default=undef) + wrapdims(table, [Type,] value, keys...; default=undef, sort=false, force=false, quiet=false) Construct a KeyedArray/NamedDimsArray from a `table` supporting column and row. -`keys` columns are extracted as is, without sorting, and are assumed to uniquely identify -each `value`. The `default` value is used in cases where no value is identified for a given -keypair. +The `default` value is used in cases where no value is identified for a given keypair. +If the `keys` columns 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. Setting `quiet` to true will avoid any non-unique warnings. """ -function KeyedArray(table, value::Symbol, keys::Symbol...; kwargs...) - return _construct_from_table(KeyedArray, table, value, keys...; kwargs...) +function wrapdims(table, value::Symbol, keys::Symbol...; kwargs...) + wrapdims(table, KeyedArray, value, keys...; kwargs...) end -function NamedDimsArray(table, value::Symbol, keys::Symbol...; kwargs...) - return _construct_from_table(NamedDimsArray, table, value, keys...; kwargs...) -end - - -# Internal function for constructing the KeyedArray or NamedDimsArray. -# This code doesn't care which type we produce so we just pass that along. -function _construct_from_table( - T::Type, table, value::Symbol, keys::Symbol...; - default=undef, issorted=false -) +function wrapdims(table, T::Type, value::Symbol, keys::Symbol...; default=undef, sort::Bool=false, kwargs...) # get columns of the input table source cols = Tables.columns(table) # Extract key columns - kw = Tuple(k => unique(Tables.getcolumn(cols, k)) for k in keys) + pairs = map(keys) do k + col = unique(Tables.getcolumn(cols, k)) + sort && Base.sort!(col) + return k => col + end # Extract data/value column vals = Tables.getcolumn(cols, value) # Initialize the KeyedArray - sz = length.(last.(kw)) + sz = length.(last.(pairs)) A = if default === undef data = similar(vals, sz) @@ -173,7 +194,7 @@ function _construct_from_table( fill!(data, default) end - A = T(data; kw...) - populate!(A, table, value) + A = T(data; pairs...) + populate!(A, table, value; kwargs...) return A end diff --git a/test/_packages.jl b/test/_packages.jl index 5fc5a37..80e0735 100644 --- a/test/_packages.jl +++ b/test/_packages.jl @@ -44,7 +44,7 @@ end # Test fully constructing from a table # Common when working with adhoc data - B = KeyedArray(table, :value, :time, :loc, :id) + B = wrapdims(table, :value, :time, :loc, :id) @test B == A # Test populating an existing array (e.g., expected data based on calculated targets/offsets) @@ -59,8 +59,30 @@ end @test C == A # Constructing a NamedDimsArray with different default value and table type - D = NamedDimsArray(Tables.rowtable(A), :value, :time, :loc, :id; default=missing) - @test D == A + # Partial populating + table = Tables.rowtable(A) + n = length(table) + idx = rand(Bool, n) + D = wrapdims(table[idx], :value, :time, :loc, :id; default=missing) + # dimnames should still match, but we'll have missing values + @test dimnames(D) == dimnames(A) + @test any(ismissing, D) + + # Construction with invalid columns error as expected, but the specific error is + # dependent on the table type. + # ERROR: ArgumentError: wrong number of names, got (:q, :time, :loc, :id) with ndims(A) == 1 + @test_throws ArgumentError wrapdims(Tables.rowtable(A), :q, :time, :loc, :id) + # ERROR: ArgumentError: wrong number of names, got (:value, :p, :loc, :id) with ndims(A) == 1 + @test_throws ArgumentError wrapdims(Tables.rowtable(A), :value, :p, :loc, :id) + # ERROR: type NamedTuple has no field q + @test_throws ErrorException wrapdims(Tables.columntable(A), :q, :time, :loc, :id) + # ERROR: type NamedTuple has no field p + @test_throws ErrorException wrapdims(Tables.columntable(A), :value, :p, :loc, :id) + + # Construction with duplicates + # ERROR: ArgumentError: Key (Date("2019-01-01"), -5) is not unique + @test_throws ArgumentError wrapdims(table, :value, :time, :loc) + @test wrapdims(table, :value, :time, :loc; force=true) == C(:, :, Key("c")) end end @testset "stack" begin From f15618ec9da38a2fbdef831ba89ea215b039d225 Mon Sep 17 00:00:00 2001 From: rofinn Date: Thu, 17 Sep 2020 10:49:24 -0500 Subject: [PATCH 3/6] Address Milan's comments/suggestions. --- src/tables.jl | 57 ++++++++++++++++++++++----------------------------- 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/src/tables.jl b/src/tables.jl index 8291732..59a3c1a 100644 --- a/src/tables.jl +++ b/src/tables.jl @@ -117,60 +117,53 @@ end # end """ - AxisKeys.populate!(A, table, value; force=false, quiet=false) + AxisKeys.populate!(A, table, value; force=false) -Populate `A` with the contents of the `value` column in a provided `table`. -The `table` must contain columns corresponding to the keys in `A` and support row iteration. -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. Setting `quiet` to true will avoid any non-unique warnings. +Populate `A` with the contents of the `value` column in a provided `table`, matching the +[Tables.jl](https://github.com/JuliaData/Tables.jl) API. The `table` must contain columns +corresponding to the keys in `A` and implements `Tables.rows`. 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. """ -function populate!(A, table, value::Symbol; force=false, quiet=false) +function populate!(A, table, value::Symbol; force=false) # Use a BitArray mask to detect duplicates and error instead of overwriting. - mask = falses(size(A)) - overwritten = false + mask = force ? falses() : falses(size(A)) for r in Tables.rows(table) vals = Tuple(Tables.getcolumn(r, c) for c in dimnames(A)) inds = map(findindex, vals, axiskeys(A)) - # Handle duplicate error if applicable - if mask[inds...] - if force - overwritten = true - else - throw(ArgumentError("Key $vals is not unique")) - end + # Handle duplicate error checking if applicable + if !force + # Error if mask already set. + mask[inds...] && throw(ArgumentError("Key $vals is not unique")) + # Set mask, marking that we've set this index + setindex!(mask, true, inds...) end - # Set or mask marking that we've set this index - setindex!(mask, true, inds...) - # Insert our value into the data array setindex!(A, Tables.getcolumn(r, value), inds...) end - if overwritten && !quiet - @warn "Columns $(dimnames(A)) do not uniquely identify rows in the table" - end - return A end """ - wrapdims(table, [Type,] value, keys...; default=undef, sort=false, force=false, quiet=false) - -Construct a KeyedArray/NamedDimsArray from a `table` supporting column and row. -The `default` value is used in cases where no value is identified for a given keypair. -If the `keys` columns 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. Setting `quiet` to true will avoid any non-unique warnings. + wrapdims(table, value, keys...; default=undef, sort=false, force=false) -> KeyedArray + wrapdims(T, table, value, keys...; default=undef, sort=false, force=false) -> T + +Construct a `KeyedArray`/`NamedDimsArray` (specified by type `T`) from a `table` matching +the [Tables.jl](https://github.com/JuliaData/Tables.jl) API. The `table` should support both +`Tables.columns` and `Tables.rows`. The `default` value is used in cases where no +value is identified for a given keypair. If the `keys` columns 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. """ function wrapdims(table, value::Symbol, keys::Symbol...; kwargs...) - wrapdims(table, KeyedArray, value, keys...; kwargs...) + wrapdims(KeyedArray, table, value, keys...; kwargs...) end -function wrapdims(table, T::Type, value::Symbol, keys::Symbol...; default=undef, sort::Bool=false, kwargs...) +function wrapdims(T::Type, table, value::Symbol, keys::Symbol...; default=undef, sort::Bool=false, kwargs...) # get columns of the input table source cols = Tables.columns(table) From 23fafbd87f868b5e40e94b1ffe751fcfc82af9d1 Mon Sep 17 00:00:00 2001 From: Michael Abbott Date: Sun, 4 Oct 2020 10:34:08 +0200 Subject: [PATCH 4/6] edits to PR... see if this works --- src/tables.jl | 56 ++++++++++++++++++++++++++++++++++------------- test/_packages.jl | 19 +++++++++++----- test/runtests.jl | 6 ++--- 3 files changed, 58 insertions(+), 23 deletions(-) diff --git a/src/tables.jl b/src/tables.jl index 59a3c1a..42bd614 100644 --- a/src/tables.jl +++ b/src/tables.jl @@ -149,29 +149,55 @@ function populate!(A, table, value::Symbol; force=false) end """ - wrapdims(table, value, keys...; default=undef, sort=false, force=false) -> KeyedArray - wrapdims(T, table, value, keys...; default=undef, sort=false, force=false) -> T - -Construct a `KeyedArray`/`NamedDimsArray` (specified by type `T`) from a `table` matching -the [Tables.jl](https://github.com/JuliaData/Tables.jl) API. The `table` should support both -`Tables.columns` and `Tables.rows`. The `default` value is used in cases where no -value is identified for a given keypair. If the `keys` columns 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. + wrapdims(table, value, names...; default=undef, sort=false, force=false) + +Construct `KeyedArray(NamedDimsArray(A,names),keys)` from a `table` matching +the [Tables.jl](https://github.com/JuliaData/Tables.jl) API. +(This which 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. + +Setting `AxisKeys.nameouter() = false` will reverse the order of wrappers produced. """ -function wrapdims(table, value::Symbol, keys::Symbol...; kwargs...) - wrapdims(KeyedArray, table, value, keys...; kwargs...) +function wrapdims(table, value::Symbol, names::Symbol...; kw...) + if nameouter() == false + _wrap_table(KeyedArray, identity, table, value, names...; kw...) + else + _wrap_table(NamedDimsArray, identity, table, value, names...; kw...) + end +end + +""" + 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. +""" +function wrapdims(table, ::Type{KT}, value::Symbol, names::Symbol...; kw...) where {KT} + if nameouter() == false + _wrap_table(KeyedArray, KT, table, value, names...; kw...) + else + _wrap_table(NamedDimsArray, KT, table, value, names...; kw...) + end end -function wrapdims(T::Type, table, value::Symbol, keys::Symbol...; default=undef, sort::Bool=false, kwargs...) +function _wrap_table(::Type{AT}, KT, table, value::Symbol, names::Symbol...; default=undef, sort::Bool=false, kwargs...) where {AT} # get columns of the input table source cols = Tables.columns(table) # Extract key columns - pairs = map(keys) do k + pairs = map(names) do k col = unique(Tables.getcolumn(cols, k)) sort && Base.sort!(col) - return k => col + return k => KT(col) end # Extract data/value column @@ -187,7 +213,7 @@ function wrapdims(T::Type, table, value::Symbol, keys::Symbol...; default=undef, fill!(data, default) end - A = T(data; pairs...) + A = AT(data; pairs...) populate!(A, table, value; kwargs...) return A end diff --git a/test/_packages.jl b/test/_packages.jl index 80e0735..5826558 100644 --- a/test/_packages.jl +++ b/test/_packages.jl @@ -39,7 +39,7 @@ end @test Tables.columns(N).a == [11, 12, 11, 12, 11, 12] end @testset "sink" begin - A = KeyedArray(rand(24, 11, 3); :time => 0:23, :loc => -5:5, :id => ["a", "b", "c"]) + A = KeyedArray(rand(24, 11, 3); time = 0:23, loc = -5:5, id = ["a", "b", "c"]) table = Tables.columntable(A) # Test fully constructing from a table @@ -47,6 +47,11 @@ end B = wrapdims(table, :value, :time, :loc, :id) @test B == A + # Test wrapping of key vectors, and wrong order: + U = wrapdims(table, UniqueVector, :value, :id, :time, :loc) + @test axiskeys(U, :time) isa UniqueVector + @test U(time=3, id="b") == A(time=3, id="b") + # Test populating an existing array (e.g., expected data based on calculated targets/offsets) C = KeyedArray( zeros(Float64, size(A)); @@ -60,14 +65,18 @@ end # Constructing a NamedDimsArray with different default value and table type # Partial populating - table = Tables.rowtable(A) - n = length(table) + r_table = Tables.rowtable(A) + n = length(r_table) idx = rand(Bool, n) - D = wrapdims(table[idx], :value, :time, :loc, :id; default=missing) + D = wrapdims(r_table[idx], :value, :time, :loc, :id; default=missing) # dimnames should still match, but we'll have missing values @test dimnames(D) == dimnames(A) @test any(ismissing, D) + # BTW, this is why it's a method of wrapdims, not KeyedArray: + # @code_warntype wrapdims(table, :value, :time, :loc, :id) # ::Any + # @code_warntype wrapdims(r_table[idx], :value, :time, :loc, :id; default=missing) + # Construction with invalid columns error as expected, but the specific error is # dependent on the table type. # ERROR: ArgumentError: wrong number of names, got (:q, :time, :loc, :id) with ndims(A) == 1 @@ -82,7 +91,7 @@ end # Construction with duplicates # ERROR: ArgumentError: Key (Date("2019-01-01"), -5) is not unique @test_throws ArgumentError wrapdims(table, :value, :time, :loc) - @test wrapdims(table, :value, :time, :loc; force=true) == C(:, :, Key("c")) + @test wrapdims(r_table, :value, :time, :loc; force=true) == C(:, :, Key("c")) end end @testset "stack" begin diff --git a/test/runtests.jl b/test/runtests.jl index 8cc664f..d050bf3 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -9,13 +9,13 @@ using Statistics, OffsetArrays, Tables, UniqueVectors, LazyStack AxisKeys.nameouter() = false end - # include("_basic.jl") + include("_basic.jl") include("_functions.jl") - # include("_fast.jl") + include("_fast.jl") - # include("_packages.jl") + include("_packages.jl") end @testset "fast findfirst & findall" begin From a5c4021dcf84f4129869a1c89a5be1579fc92e69 Mon Sep 17 00:00:00 2001 From: Michael Abbott Date: Sun, 4 Oct 2020 10:58:00 +0200 Subject: [PATCH 5/6] add readme note --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 481311b..7b86108 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,9 @@ while `A.keys isa Tuple` for matrices & higher. But `axiskeys(A)` always returns * Named tuples can be converted to and from keyed vectors, with `collect(keys(nt)) == Symbol.(axiskeys(V),1)` +* The [Tables.jl](https://github.com/JuliaData/Tables.jl) interface is supported, + with `wrapdims(df, :val, :x, :y)` creating a matrix from 3 columns. + * [FFTW](https://github.com/JuliaMath/FFTW.jl)`.fft` transforms the keys; if these are times such as [Unitful](https://github.com/PainterQubits/Unitful.jl)`.s` then the results are fequency labels. ([PR#15](https://github.com/mcabbott/AxisKeys.jl/pull/15).) From ab143952051da2481e746be9ad91914a5e98ca8b Mon Sep 17 00:00:00 2001 From: Michael Abbott Date: Sun, 4 Oct 2020 12:53:42 +0200 Subject: [PATCH 6/6] tweaks --- src/tables.jl | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/tables.jl b/src/tables.jl index 42bd614..6fb910f 100644 --- a/src/tables.jl +++ b/src/tables.jl @@ -153,7 +153,7 @@ end Construct `KeyedArray(NamedDimsArray(A,names),keys)` from a `table` matching the [Tables.jl](https://github.com/JuliaData/Tables.jl) API. -(This which must support both `Tables.columns` and `Tables.rows`.) +(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 @@ -181,7 +181,7 @@ 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. """ -function wrapdims(table, ::Type{KT}, value::Symbol, names::Symbol...; kw...) where {KT} +function wrapdims(table, KT::Type, value::Symbol, names::Symbol...; kw...) if nameouter() == false _wrap_table(KeyedArray, KT, table, value, names...; kw...) else @@ -189,7 +189,7 @@ function wrapdims(table, ::Type{KT}, value::Symbol, names::Symbol...; kw...) whe end end -function _wrap_table(::Type{AT}, KT, table, value::Symbol, names::Symbol...; default=undef, sort::Bool=false, kwargs...) where {AT} +function _wrap_table(AT::Type, KT, table, value::Symbol, names::Symbol...; default=undef, sort::Bool=false, kwargs...) # get columns of the input table source cols = Tables.columns(table) @@ -205,15 +205,14 @@ function _wrap_table(::Type{AT}, KT, table, value::Symbol, names::Symbol...; def # Initialize the KeyedArray sz = length.(last.(pairs)) - - A = if default === undef + if default === undef data = similar(vals, sz) else data = similar(vals, Union{eltype(vals), typeof(default)}, sz) fill!(data, default) end - A = AT(data; pairs...) + populate!(A, table, value; kwargs...) return A end