From 4be43d9f7be75ac14f0ec529252d88db6803fb04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bogumi=C5=82=20Kami=C5=84ski?= Date: Thu, 7 Sep 2023 10:51:32 +0200 Subject: [PATCH 1/9] Add nrow and ncol fallback implementations --- src/fallbacks.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/fallbacks.jl b/src/fallbacks.jl index f0621c0..7890000 100644 --- a/src/fallbacks.jl +++ b/src/fallbacks.jl @@ -11,6 +11,10 @@ function rowcount(cols) return length(getcolumn(cols, names[1])) end +# implement default methods for DataAPI.jl +DataAPI.nrow(table) = rowcount(table) +DataAPI.ncol(table) = length(columnnames(cols)) + # a lazy row view into a AbstractColumns object struct ColumnsRow{T} <: AbstractRow columns::T # an `AbstractColumns`-compatible object From 2d54a916418c8aa63feca6b1efb2d02dc853077d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bogumi=C5=82=20Kami=C5=84ski?= Date: Sun, 17 Sep 2023 11:12:11 +0200 Subject: [PATCH 2/9] improve implementation of nrow and ncol --- src/fallbacks.jl | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/fallbacks.jl b/src/fallbacks.jl index 7890000..0aba903 100644 --- a/src/fallbacks.jl +++ b/src/fallbacks.jl @@ -11,10 +11,6 @@ function rowcount(cols) return length(getcolumn(cols, names[1])) end -# implement default methods for DataAPI.jl -DataAPI.nrow(table) = rowcount(table) -DataAPI.ncol(table) = length(columnnames(cols)) - # a lazy row view into a AbstractColumns object struct ColumnsRow{T} <: AbstractRow columns::T # an `AbstractColumns`-compatible object @@ -279,3 +275,24 @@ columnnames(x::CopiedColumns) = columnnames(source(x)) end throw(ArgumentError("no default `Tables.columns` implementation for type: $T")) end + +# implement default nrow and ncol methods for DataAPI.jl + +# this covers also MatrixTable +DataAPI.nrow(table::AbstractColumns) = rowcount(table) +DataAPI.ncol(table::AbstractColumns) = length(columnnames(table)) + +DataAPI.nrow(table::ColumnTable) = isempty(table) ? 0 : length(first(table)) +DataAPI.ncol(table::ColumnTable) = length(table) + +DataAPI.nrow(table::AbstractRowTable) = length(table) +DataAPI.ncol(table::AbstractRowTable) = isempty(table) ? 0 : length(columnnames(first(table))) + +DataAPI.nrow(table::RowTable) = length(table) +DataAPI.ncol(table::RowTable) = isempty(table) ? 0 : length(first(table)) + +DataAPI.nrow(table::MatrixRowTable) = length(table) +DataAPI.ncol(table::MatrixRowTable) = size(getfield(m, :matrix), 2) # this is correct even if m is a vector + +DataAPI.nrow(table::DictRowTable) = length(table) +DataAPI.ncol(table::DictRowTable) = length(columnnames(table)) From 2bdac717656146d318cb1271894d14a8e63cb9e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bogumi=C5=82=20Kami=C5=84ski?= Date: Sun, 17 Sep 2023 11:17:14 +0200 Subject: [PATCH 3/9] update documentation --- docs/src/index.md | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/docs/src/index.md b/docs/src/index.md index 4765d69..49ae82f 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -229,19 +229,21 @@ make my custom type valid for Tables.jl consumers? For a type `MyTable`, the interface to becoming a proper table is straightforward: -| Required Methods | Default Definition | Brief Description | -|----------------------------------------|------------------------------|---------------------------------------------------------------------------------------------------------------------------------| -| `Tables.istable(::Type{MyTable})` | | Declare that your table type implements the interface | -| **One of:** | | | -| `Tables.rowaccess(::Type{MyTable})` | | Declare that your table type defines a `Tables.rows(::MyTable)` method | -| `Tables.rows(x::MyTable)` | | Return an `Tables.AbstractRow`-compatible iterator from your table | -| **Or:** | | | -| `Tables.columnaccess(::Type{MyTable})` | | Declare that your table type defines a `Tables.columns(::MyTable)` method | -| `Tables.columns(x::MyTable)` | | Return an `Tables.AbstractColumns`-compatible object from your table | -| **Optional methods** | | | -| `Tables.schema(x::MyTable)` | `Tables.schema(x) = nothing` | Return a [`Tables.Schema`](@ref) object from your `Tables.AbstractRow` iterator or `Tables.AbstractColumns` object; or `nothing` for unknown schema | -| `Tables.materializer(::Type{MyTable})` | `Tables.columntable` | Declare a "materializer" sink function for your table type that can construct an instance of your type from any Tables.jl input | -| `Tables.subset(x::MyTable, inds; viewhint)` | | Return a row or a sub-table of the original table +| Required Methods | Default Definition | Brief Description | +|----------------------------------------------|------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------| +| `Tables.istable(::Type{MyTable})` | | Declare that your table type implements the interface | +| **One of:** | | | +| `Tables.rowaccess(::Type{MyTable})` | | Declare that your table type defines a `Tables.rows(::MyTable)` method | +| `Tables.rows(x::MyTable)` | | Return an `Tables.AbstractRow`-compatible iterator from your table | +| **Or:** | | | +| `Tables.columnaccess(::Type{MyTable})` | | Declare that your table type defines a `Tables.columns(::MyTable)` method | +| `Tables.columns(x::MyTable)` | | Return an `Tables.AbstractColumns`-compatible object from your table | +| **Optional methods** | | | +| `Tables.schema(x::MyTable)` | `Tables.schema(x) = nothing` | Return a [`Tables.Schema`](@ref) object from your `Tables.AbstractRow` iterator or `Tables.AbstractColumns` object; or `nothing` for unknown schema | +| `Tables.materializer(::Type{MyTable})` | `Tables.columntable` | Declare a "materializer" sink function for your table type that can construct an instance of your type from any Tables.jl input | +| `Tables.subset(x::MyTable, inds; viewhint)` | | Return a row or a sub-table of the original table | +| `DataAPI.nrow(x::MyTable)` | | Return number of rows of table `x` | +| `DataAPI.ncol(x::MyTable)` | | Return number of columns of table `x` | Based on whether your table type has defined `Tables.rows` or `Tables.columns`, you then ensure that the `Tables.AbstractRow` iterator or `Tables.AbstractColumns` object satisfies the respective interface. From 7f504e9d4dda9906bd256881afa42f6a188cd59d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bogumi=C5=82=20Kami=C5=84ski?= Date: Tue, 19 Sep 2023 17:24:03 +0200 Subject: [PATCH 4/9] add tests --- test/runtests.jl | 55 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index 25d03c8..76001b7 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -957,7 +957,7 @@ end @test Tables.istable(Vector{Dict{String}}) @test Tables.istable(Vector{Dict{SubString}}) @test Tables.istable(Vector{Dict{AbstractString}}) - + @test Set(Tables.columnnames(Dict(:a=>1, :b=>2))) == Set([:a, :b]) @test Set(Tables.columnnames(Dict("a"=>1, "b"=>2))) == Set([:a, :b]) @test Set(Tables.columnnames(Dict("a"=>1, SubString("b")=>2))) == Set([:a, :b]) @@ -976,4 +976,55 @@ end @test Tables.getcolumn(Dict("a"=>1, SubString("b")=>2), Int, 1, :a) == 1 @test Tables.getcolumn(Dict(SubString("a")=>1, SubString("b")=>2), Int, 1, :a) == 1 end - + +@testset "test nrow and ncol" begin + # AbstractColumns + @test nrow(Tables.CopiedColumns((;))) == 0 + @test ncol(Tables.CopiedColumns((;))) == 0 + @test nrow(Tables.CopiedColumns((a=1:3, b=2:4))) == 3 + @test ncol(Tables.CopiedColumns((a=1:3, b=2:4))) == 2 + + # ColumnTable + @test nrow((;)) == 0 + @test ncol((;)) == 0 + @test nrow((a=1:3, b=2:4)) == 3 + @test ncol((a=1:3, b=2:4)) == 2 + + # AbstractRowTable + @test nrow(collect(Tables.rows(Tables.table(ones(0, 0))))) == 0 + @test ncol(collect(Tables.rows(Tables.table(ones(0, 0))))) == 0 + @test nrow(collect(Tables.rows(Tables.table(ones(2, 3))))) == 2 + @test ncol(collect(Tables.rows(Tables.table(ones(2, 3))))) == 3 + + # RowTable + @test nrow(NamedTuple[]) == 0 + @test ncol(NamedTuple[]) == 0 + @test nrow([(a=1,b=2), (a=3, b=4), (a=5, b=6)]) == 3 + @test ncol([(a=1, b=2), (a=3, b=4), (a=5, b=6)]) == 2 + + # MatrixTable + @test nrow(Tables.table(ones(0, 0))) == 0 + @test ncol(Tables.table(ones(0, 0))) == 0 + @test nrow(Tables.table(ones(2, 3))) == 2 + @test ncol(Tables.table(ones(2, 3))) == 3 + @test nrow(Tables.table([])) == 0 + @test ncol(Tables.table([])) == 1 + @test nrow(Tables.table([1, 2])) == 2 + @test ncol(Tables.table([1, 2])) == 1 + + # MatrixRowTable + @test nrow(Tables.rows(Tables.table(ones(0, 0)))) == 0 + @test ncol(Tables.rows(Tables.table(ones(0, 0)))) == 0 + @test nrow(Tables.rows(Tables.table(ones(2, 3)))) == 2 + @test ncol(Tables.rows(Tables.table(ones(2, 3)))) == 3 + @test nrow(Tables.rows(Tables.table([]))) == 0 + @test ncol(Tables.rows(Tables.table([]))) == 1 + @test nrow(Tables.rows(Tables.table([1, 2]))) == 2 + @test ncol(Tables.rows(Tables.table([1, 2]))) == 1 + + # DictRowTable + @test nrow(Tables.dictrowtable(NamedTuple[])) == 0 + @test ncol(Tables.dictrowtable(NamedTuple[])) == 0 + @test nrow(Tables.dictrowtable([(a=1, b=2), (a=3, b=4), (a=5, b=6)])) == 3 + @test ncol(Tables.dictrowtable([(a=1, b=2), (a=3, b=4), (a=5, b=6)])) == 2 +end \ No newline at end of file From be0165c42b53844fe5b615cdca9ce8c486ed9cf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bogumi=C5=82=20Kami=C5=84ski?= Date: Tue, 19 Sep 2023 17:24:40 +0200 Subject: [PATCH 5/9] update version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 25d6cb2..d330373 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Tables" uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" authors = ["quinnj "] -version = "1.10.1" +version = "1.11.0" [deps] DataAPI = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" From d712af5f564ff1f9d935a98c7ace2611b994cc43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bogumi=C5=82=20Kami=C5=84ski?= Date: Tue, 19 Sep 2023 19:20:59 +0200 Subject: [PATCH 6/9] fix locations of method definitions --- src/dicts.jl | 5 +++++ src/fallbacks.jl | 12 ------------ src/matrix.jl | 5 +++++ src/namedtuples.jl | 8 ++++++++ 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/dicts.jl b/src/dicts.jl index d5d1bf9..5e73bbf 100644 --- a/src/dicts.jl +++ b/src/dicts.jl @@ -197,3 +197,8 @@ function dictrowtable(x) end return DictRowTable(names, types, out) end + +# implement default nrow and ncol methods for DataAPI.jl + +DataAPI.nrow(table::DictRowTable) = length(table) +DataAPI.ncol(table::DictRowTable) = length(columnnames(table)) diff --git a/src/fallbacks.jl b/src/fallbacks.jl index 0aba903..d911ac1 100644 --- a/src/fallbacks.jl +++ b/src/fallbacks.jl @@ -282,17 +282,5 @@ end DataAPI.nrow(table::AbstractColumns) = rowcount(table) DataAPI.ncol(table::AbstractColumns) = length(columnnames(table)) -DataAPI.nrow(table::ColumnTable) = isempty(table) ? 0 : length(first(table)) -DataAPI.ncol(table::ColumnTable) = length(table) - DataAPI.nrow(table::AbstractRowTable) = length(table) DataAPI.ncol(table::AbstractRowTable) = isempty(table) ? 0 : length(columnnames(first(table))) - -DataAPI.nrow(table::RowTable) = length(table) -DataAPI.ncol(table::RowTable) = isempty(table) ? 0 : length(first(table)) - -DataAPI.nrow(table::MatrixRowTable) = length(table) -DataAPI.ncol(table::MatrixRowTable) = size(getfield(m, :matrix), 2) # this is correct even if m is a vector - -DataAPI.nrow(table::DictRowTable) = length(table) -DataAPI.ncol(table::DictRowTable) = length(columnnames(table)) diff --git a/src/matrix.jl b/src/matrix.jl index b8d0fdb..c6127fe 100644 --- a/src/matrix.jl +++ b/src/matrix.jl @@ -111,3 +111,8 @@ function matrix(table::MatrixTable; transpose::Bool=false) return matrix end end + +# implement default nrow and ncol methods for DataAPI.jl + +DataAPI.nrow(table::MatrixRowTable) = length(table) +DataAPI.ncol(table::MatrixRowTable) = size(getfield(m, :matrix), 2) # this is correct even if m is a vector diff --git a/src/namedtuples.jl b/src/namedtuples.jl index 309b669..8bdc793 100644 --- a/src/namedtuples.jl +++ b/src/namedtuples.jl @@ -190,3 +190,11 @@ function columntable(itr::T) where {T} return columntable(schema(cols), cols) end columntable(x::ColumnTable) = x + +# implement default nrow and ncol methods for DataAPI.jl + +DataAPI.nrow(table::ColumnTable) = isempty(table) ? 0 : length(first(table)) +DataAPI.ncol(table::ColumnTable) = length(table) + +DataAPI.nrow(table::RowTable) = length(table) +DataAPI.ncol(table::RowTable) = isempty(table) ? 0 : length(first(table)) From 7c811beca729b893138440ec0bc9958c6e6c4aad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bogumi=C5=82=20Kami=C5=84ski?= Date: Tue, 19 Sep 2023 19:26:53 +0200 Subject: [PATCH 7/9] add import --- test/runtests.jl | 74 ++++++++++++++++++++++++------------------------ 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index 76001b7..c757719 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,4 +1,4 @@ -using Test, Tables, OrderedCollections, TableTraits, DataValues, QueryOperators, IteratorInterfaceExtensions, SparseArrays +using Test, Tables, OrderedCollections, TableTraits, DataValues, QueryOperators, IteratorInterfaceExtensions, SparseArrays, DataAPI @testset "utils.jl" begin @@ -979,52 +979,52 @@ end @testset "test nrow and ncol" begin # AbstractColumns - @test nrow(Tables.CopiedColumns((;))) == 0 - @test ncol(Tables.CopiedColumns((;))) == 0 - @test nrow(Tables.CopiedColumns((a=1:3, b=2:4))) == 3 - @test ncol(Tables.CopiedColumns((a=1:3, b=2:4))) == 2 + @test DataAPI.nrow(Tables.CopiedColumns((;))) == 0 + @test DataAPI.ncol(Tables.CopiedColumns((;))) == 0 + @test DataAPI.nrow(Tables.CopiedColumns((a=1:3, b=2:4))) == 3 + @test DataAPI.ncol(Tables.CopiedColumns((a=1:3, b=2:4))) == 2 # ColumnTable - @test nrow((;)) == 0 - @test ncol((;)) == 0 - @test nrow((a=1:3, b=2:4)) == 3 - @test ncol((a=1:3, b=2:4)) == 2 + @test DataAPI.nrow((;)) == 0 + @test DataAPI.ncol((;)) == 0 + @test DataAPI.nrow((a=1:3, b=2:4)) == 3 + @test DataAPI.ncol((a=1:3, b=2:4)) == 2 # AbstractRowTable - @test nrow(collect(Tables.rows(Tables.table(ones(0, 0))))) == 0 - @test ncol(collect(Tables.rows(Tables.table(ones(0, 0))))) == 0 - @test nrow(collect(Tables.rows(Tables.table(ones(2, 3))))) == 2 - @test ncol(collect(Tables.rows(Tables.table(ones(2, 3))))) == 3 + @test DataAPI.nrow(collect(Tables.rows(Tables.table(ones(0, 0))))) == 0 + @test DataAPI.ncol(collect(Tables.rows(Tables.table(ones(0, 0))))) == 0 + @test DataAPI.nrow(collect(Tables.rows(Tables.table(ones(2, 3))))) == 2 + @test DataAPI.ncol(collect(Tables.rows(Tables.table(ones(2, 3))))) == 3 # RowTable - @test nrow(NamedTuple[]) == 0 - @test ncol(NamedTuple[]) == 0 - @test nrow([(a=1,b=2), (a=3, b=4), (a=5, b=6)]) == 3 - @test ncol([(a=1, b=2), (a=3, b=4), (a=5, b=6)]) == 2 + @test DataAPI.nrow(NamedTuple[]) == 0 + @test DataAPI.ncol(NamedTuple[]) == 0 + @test DataAPI.nrow([(a=1,b=2), (a=3, b=4), (a=5, b=6)]) == 3 + @test DataAPI.ncol([(a=1, b=2), (a=3, b=4), (a=5, b=6)]) == 2 # MatrixTable - @test nrow(Tables.table(ones(0, 0))) == 0 - @test ncol(Tables.table(ones(0, 0))) == 0 - @test nrow(Tables.table(ones(2, 3))) == 2 - @test ncol(Tables.table(ones(2, 3))) == 3 - @test nrow(Tables.table([])) == 0 - @test ncol(Tables.table([])) == 1 - @test nrow(Tables.table([1, 2])) == 2 - @test ncol(Tables.table([1, 2])) == 1 + @test DataAPI.nrow(Tables.table(ones(0, 0))) == 0 + @test DataAPI.ncol(Tables.table(ones(0, 0))) == 0 + @test DataAPI.nrow(Tables.table(ones(2, 3))) == 2 + @test DataAPI.ncol(Tables.table(ones(2, 3))) == 3 + @test DataAPI.nrow(Tables.table([])) == 0 + @test DataAPI.ncol(Tables.table([])) == 1 + @test DataAPI.nrow(Tables.table([1, 2])) == 2 + @test DataAPI.ncol(Tables.table([1, 2])) == 1 # MatrixRowTable - @test nrow(Tables.rows(Tables.table(ones(0, 0)))) == 0 - @test ncol(Tables.rows(Tables.table(ones(0, 0)))) == 0 - @test nrow(Tables.rows(Tables.table(ones(2, 3)))) == 2 - @test ncol(Tables.rows(Tables.table(ones(2, 3)))) == 3 - @test nrow(Tables.rows(Tables.table([]))) == 0 - @test ncol(Tables.rows(Tables.table([]))) == 1 - @test nrow(Tables.rows(Tables.table([1, 2]))) == 2 - @test ncol(Tables.rows(Tables.table([1, 2]))) == 1 + @test DataAPI.nrow(Tables.rows(Tables.table(ones(0, 0)))) == 0 + @test DataAPI.ncol(Tables.rows(Tables.table(ones(0, 0)))) == 0 + @test DataAPI.nrow(Tables.rows(Tables.table(ones(2, 3)))) == 2 + @test DataAPI.ncol(Tables.rows(Tables.table(ones(2, 3)))) == 3 + @test DataAPI.nrow(Tables.rows(Tables.table([]))) == 0 + @test DataAPI.ncol(Tables.rows(Tables.table([]))) == 1 + @test DataAPI.nrow(Tables.rows(Tables.table([1, 2]))) == 2 + @test DataAPI.ncol(Tables.rows(Tables.table([1, 2]))) == 1 # DictRowTable - @test nrow(Tables.dictrowtable(NamedTuple[])) == 0 - @test ncol(Tables.dictrowtable(NamedTuple[])) == 0 - @test nrow(Tables.dictrowtable([(a=1, b=2), (a=3, b=4), (a=5, b=6)])) == 3 - @test ncol(Tables.dictrowtable([(a=1, b=2), (a=3, b=4), (a=5, b=6)])) == 2 + @test DataAPI.nrow(Tables.dictrowtable(NamedTuple[])) == 0 + @test DataAPI.ncol(Tables.dictrowtable(NamedTuple[])) == 0 + @test DataAPI.nrow(Tables.dictrowtable([(a=1, b=2), (a=3, b=4), (a=5, b=6)])) == 3 + @test DataAPI.ncol(Tables.dictrowtable([(a=1, b=2), (a=3, b=4), (a=5, b=6)])) == 2 end \ No newline at end of file From ec7fb67721223617b7697735fbd1cd4f539a9939 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bogumi=C5=82=20Kami=C5=84ski?= Date: Tue, 19 Sep 2023 19:43:02 +0200 Subject: [PATCH 8/9] fix implementation --- src/dicts.jl | 2 +- src/matrix.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dicts.jl b/src/dicts.jl index 5e73bbf..addc2fd 100644 --- a/src/dicts.jl +++ b/src/dicts.jl @@ -201,4 +201,4 @@ end # implement default nrow and ncol methods for DataAPI.jl DataAPI.nrow(table::DictRowTable) = length(table) -DataAPI.ncol(table::DictRowTable) = length(columnnames(table)) +DataAPI.ncol(table::DictRowTable) = length(getfield(table, :names)) diff --git a/src/matrix.jl b/src/matrix.jl index c6127fe..e4d98b3 100644 --- a/src/matrix.jl +++ b/src/matrix.jl @@ -115,4 +115,4 @@ end # implement default nrow and ncol methods for DataAPI.jl DataAPI.nrow(table::MatrixRowTable) = length(table) -DataAPI.ncol(table::MatrixRowTable) = size(getfield(m, :matrix), 2) # this is correct even if m is a vector +DataAPI.ncol(table::MatrixRowTable) = size(getfield(table, :matrix), 2) # this is correct even if m is a vector From 88a9a791b4d6c357f383157989da2f5931c37d93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bogumi=C5=82=20Kami=C5=84ski?= Date: Tue, 19 Sep 2023 19:48:52 +0200 Subject: [PATCH 9/9] fix Julia 1.0 tests --- test/runtests.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index c757719..7eef20a 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -979,14 +979,14 @@ end @testset "test nrow and ncol" begin # AbstractColumns - @test DataAPI.nrow(Tables.CopiedColumns((;))) == 0 - @test DataAPI.ncol(Tables.CopiedColumns((;))) == 0 + @test DataAPI.nrow(Tables.CopiedColumns(NamedTuple())) == 0 + @test DataAPI.ncol(Tables.CopiedColumns(NamedTuple())) == 0 @test DataAPI.nrow(Tables.CopiedColumns((a=1:3, b=2:4))) == 3 @test DataAPI.ncol(Tables.CopiedColumns((a=1:3, b=2:4))) == 2 # ColumnTable - @test DataAPI.nrow((;)) == 0 - @test DataAPI.ncol((;)) == 0 + @test DataAPI.nrow(NamedTuple()) == 0 + @test DataAPI.ncol(NamedTuple()) == 0 @test DataAPI.nrow((a=1:3, b=2:4)) == 3 @test DataAPI.ncol((a=1:3, b=2:4)) == 2