Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add nrow and ncol fallback implementations #343

Merged
merged 9 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "Tables"
uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
authors = ["quinnj <[email protected]>"]
version = "1.10.1"
version = "1.11.0"

[deps]
DataAPI = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
Expand Down
28 changes: 15 additions & 13 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
bkamins marked this conversation as resolved.
Show resolved Hide resolved
|----------------------------------------------|------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------|
| `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.
Expand Down
5 changes: 5 additions & 0 deletions src/dicts.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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(getfield(table, :names))
9 changes: 9 additions & 0 deletions src/fallbacks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -275,3 +275,12 @@ 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::AbstractRowTable) = length(table)
DataAPI.ncol(table::AbstractRowTable) = isempty(table) ? 0 : length(columnnames(first(table)))
5 changes: 5 additions & 0 deletions src/matrix.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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(table, :matrix), 2) # this is correct even if m is a vector
8 changes: 8 additions & 0 deletions src/namedtuples.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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))
57 changes: 54 additions & 3 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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])
Expand All @@ -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 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(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

# AbstractRowTable
@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 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 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 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 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
Loading