Repository: JuliaML/TableTransforms.jl (julia)

Issue:
Add `Coerce(SciType)` option
 `Coerce(Continuous)` currently fails because the transform expects pairs like `:z => Continuous`. We need a new constructor that takes a single scitype for all columns.

Pull request:
closes #270

Patch:
diff --git a/src/transforms/coerce.jl b/src/transforms/coerce.jl
index a822f1b7..75fcd848 100644
--- a/src/transforms/coerce.jl
+++ b/src/transforms/coerce.jl
@@ -7,6 +7,10 @@
 
 Return a copy of the table, ensuring that the scientific types of the columns match the new specification.
 
+    Coerce(S)
+
+Coerce all columns of the table with scientific type `S`.
+
 This transform uses the `DataScienceTraits.coerce` function. Please see their docstring for more details.
 
 # Examples
@@ -18,23 +22,28 @@ Coerce(:a => DST.Continuous, :b => DST.Continuous)
 Coerce("a" => DST.Continuous, "b" => DST.Continuous)
 ```
 """
-struct Coerce{S<:ColumnSelector} <: StatelessFeatureTransform
+struct Coerce{S<:ColumnSelector,T} <: StatelessFeatureTransform
   selector::S
-  scitypes::Vector{DataType}
+  scitypes::T
 end
 
 Coerce() = throw(ArgumentError("cannot create Coerce transform without arguments"))
 
+Coerce(scitype::Type{<:SciType}) = Coerce(AllSelector(), scitype)
+
 Coerce(pairs::Pair{C,DataType}...) where {C<:Column} = Coerce(selector(first.(pairs)), collect(last.(pairs)))
 
 isrevertible(::Type{<:Coerce}) = true
 
+_typedict(scitype::Type{<:SciType}, snames) = Dict(nm => scitype for nm in snames)
+_typedict(scitypes::AbstractVector, snames) = Dict(zip(snames, scitypes))
+
 function applyfeat(transform::Coerce, feat, prep)
   cols = Tables.columns(feat)
   names = Tables.columnnames(cols)
   types = Tables.schema(feat).types
   snames = transform.selector(names)
-  typedict = Dict(zip(snames, transform.scitypes))
+  typedict = _typedict(transform.scitypes, snames)
 
   columns = map(names) do name
     x = Tables.getcolumn(cols, name)
diff --git a/src/transforms/rename.jl b/src/transforms/rename.jl
index fc08fbc3..b4681b3a 100644
--- a/src/transforms/rename.jl
+++ b/src/transforms/rename.jl
@@ -36,6 +36,8 @@ struct Rename{S<:ColumnSelector,N} <: StatelessFeatureTransform
   end
 end
 
+Rename() = throw(ArgumentError("cannot create Rename transform without arguments"))
+
 Rename(fun) = Rename(AllSelector(), fun)
 
 Rename(pairs::Pair{C,Symbol}...) where {C<:Column} = Rename(selector(first.(pairs)), collect(last.(pairs)))


Test patch:
diff --git a/test/transforms/coerce.jl b/test/transforms/coerce.jl
index eb89b997..ff548cd7 100644
--- a/test/transforms/coerce.jl
+++ b/test/transforms/coerce.jl
@@ -30,6 +30,24 @@
   @test eltype(tₒ.a) == eltype(t.a)
   @test eltype(tₒ.b) == eltype(t.b)
 
+  T = Coerce(DST.Continuous)
+  n, c = apply(T, t)
+  @test eltype(n.a) <: Float64
+  @test eltype(n.b) <: Float64
+  n, c = apply(T, t)
+  tₒ = revert(T, n, c)
+  @test eltype(tₒ.a) == eltype(t.a)
+  @test eltype(tₒ.b) == eltype(t.b)
+
+  T = Coerce(DST.Categorical)
+  n, c = apply(T, t)
+  @test eltype(n.a) <: Int
+  @test eltype(n.b) <: Int
+  n, c = apply(T, t)
+  tₒ = revert(T, n, c)
+  @test eltype(tₒ.a) == eltype(t.a)
+  @test eltype(tₒ.b) == eltype(t.b)
+
   # row table
   rt = Tables.rowtable(t)
   T = Coerce(:a => DST.Continuous, :b => DST.Categorical)
@@ -37,4 +55,7 @@
   @test Tables.isrowtable(n)
   rtₒ = revert(T, n, c)
   @test rt == rtₒ
+
+  # error: cannot create Coerce transform without arguments
+  @test_throws ArgumentError Coerce()
 end
diff --git a/test/transforms/rename.jl b/test/transforms/rename.jl
index 0489f95f..121c0a3f 100644
--- a/test/transforms/rename.jl
+++ b/test/transforms/rename.jl
@@ -152,7 +152,9 @@
   tₒ = revert(T, n, c)
   @test t == tₒ
 
-  # throws
+  # error: cannot create Rename transform without arguments
+  @test_throws ArgumentError Rename()
+  # error: new names must be unique
   @test_throws AssertionError Rename(:a => :x, :b => :x)
   @test_throws AssertionError apply(Rename(:a => :c, :b => :d), t)
 end