Skip to content

Commit b1dc549

Browse files
authored
Add the colspec(col::Col) method & Update ColSpec interface error messages (#189)
* Add the 'colspec(col::Col)' method & Update 'ColSpec' interface error messages * Update docstring * Update docstring * Fix tests
1 parent 7c3fb5b commit b1dc549

File tree

8 files changed

+94
-40
lines changed

8 files changed

+94
-40
lines changed

src/colspec.jl

+20-6
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ abstract type ColSpec end
6161
6262
Converts the `spec` argument to a `ColSpec` object.
6363
64+
colspec(col::Col)
65+
66+
Converts the `col` to a single column selection,
67+
this is equivalent to calling `colspec([col])`.
68+
6469
# Examples
6570
6671
```julia
@@ -75,13 +80,18 @@ colspec(:) # AllSpec
7580
colspec(nothing) # NoneSpec
7681
# if the argument is a ColSpec, return it
7782
colspec(NoneSpec()) # NoneSpec
83+
# single column selection
84+
colspec(:a) # NameSpec
85+
colspec("a") # NameSpec
86+
colspec(1) # IndexSpec
7887
```
7988
"""
8089
colspec(spec::ColSpec) = spec
90+
colspec(col::Col) = colspec([col])
8191

8292
# argument errors
83-
colspec(::Tuple{}) = throw(ArgumentError("Invalid column spec."))
84-
colspec(::Any) = throw(ArgumentError("Invalid column spec."))
93+
colspec(::Any) = throw(ArgumentError("invalid column selection"))
94+
colspec(::Tuple{}) = throw(ArgumentError("column selection cannot be empty"))
8595

8696
"""
8797
choose(colspec::ColSpec, names) -> Vector{Symbol}
@@ -130,7 +140,9 @@ function choose end
130140
struct NameSpec <: ColSpec
131141
names::Vector{Symbol}
132142
function NameSpec(names)
133-
@assert !isempty(names) "Invalid column selection."
143+
if isempty(names)
144+
throw(ArgumentError("column selection cannot be empty"))
145+
end
134146
new(names)
135147
end
136148
end
@@ -148,7 +160,9 @@ choose(colspec::NameSpec, names) = _choose(colspec.names, names)
148160
struct IndexSpec <: ColSpec
149161
inds::Vector{Int}
150162
function IndexSpec(inds)
151-
@assert !isempty(inds) "Invalid column selection."
163+
if isempty(inds)
164+
throw(ArgumentError("column selection cannot be empty"))
165+
end
152166
new(inds)
153167
end
154168
end
@@ -174,7 +188,7 @@ choose(colspec::RegexSpec, names::Tuple) = choose(colspec, collect(names))
174188
function choose(colspec::RegexSpec, names::Vector)
175189
regex = colspec.regex
176190
snames = filter(nm -> occursin(regex, String(nm)), names)
177-
@assert !isempty(snames) "Invalid column selection."
191+
@assert !isempty(snames) "regex doesn't match any names in input table"
178192
_choose(snames, names)
179193
end
180194

@@ -200,6 +214,6 @@ choose(::NoneSpec, names) = Symbol[]
200214
# helper functions
201215
function _choose(snames::Vector{Symbol}, names)
202216
# validate columns
203-
@assert snames names "Invalid column selection."
217+
@assert snames names "names not present in input table"
204218
return snames
205219
end

src/transforms/indicator.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ struct Indicator{S<:ColSpec} <: StatelessFeatureTransform
3131
scale::Symbol
3232
categ::Bool
3333

34-
function Indicator(col, k, scale, categ)
34+
function Indicator(col::Col, k, scale, categ)
3535
if k < 1
3636
throw(ArgumentError("`k` must be greater than or equal to 1"))
3737
end
@@ -40,7 +40,7 @@ struct Indicator{S<:ColSpec} <: StatelessFeatureTransform
4040
throw(ArgumentError("invalid `scale` option, use `:quantile` or `:linear`"))
4141
end
4242

43-
cs = colspec([col])
43+
cs = colspec(col)
4444
new{typeof(cs)}(cs, k, scale, categ)
4545
end
4646
end

src/transforms/map.jl

+1-4
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,13 @@ const MapPair = Union{PairWithTarget,PairWithoutTarget}
4343
_extract(p::PairWithTarget) = first(p), first(last(p)), last(last(p))
4444
_extract(p::PairWithoutTarget) = first(p), last(p), nothing
4545

46-
_colspec(spec) = colspec(spec)
47-
_colspec(col::Col) = colspec([col])
48-
4946
_target(name) = name
5047
_target(name::AbstractString) = Symbol(name)
5148

5249
function Map(pairs::MapPair...)
5350
tuples = map(pairs) do p
5451
spec, fun, name = _extract(p)
55-
(_colspec(spec), fun, _target(name))
52+
(colspec(spec), fun, _target(name))
5653
end
5754
colspecs = map(t -> t[1], tuples) |> collect
5855
funs = map(t -> t[2], tuples) |> collect

src/transforms/onehot.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ OneHot("a", categ=true)
2222
struct OneHot{S<:ColSpec} <: StatelessFeatureTransform
2323
colspec::S
2424
categ::Bool
25-
function OneHot(col, categ)
26-
cs = colspec([col])
25+
function OneHot(col::Col, categ)
26+
cs = colspec(col)
2727
new{typeof(cs)}(cs, categ)
2828
end
2929
end

test/colspec.jl

+42-5
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,43 @@
6565
snames = TT.choose(colspec, tupnames)
6666
@test snames == Symbol[]
6767

68+
# single symbol
69+
colspec = TT.colspec(:a)
70+
snames = TT.choose(colspec, vecnames)
71+
@test snames == [:a]
72+
snames = TT.choose(colspec, tupnames)
73+
@test snames == [:a]
74+
75+
# single string
76+
colspec = TT.colspec("b")
77+
snames = TT.choose(colspec, vecnames)
78+
@test snames == [:b]
79+
snames = TT.choose(colspec, tupnames)
80+
@test snames == [:b]
81+
82+
# single integer
83+
colspec = TT.colspec(3)
84+
snames = TT.choose(colspec, vecnames)
85+
@test snames == [:c]
86+
snames = TT.choose(colspec, tupnames)
87+
@test snames == [:c]
88+
6889
# throws
90+
# invalid colspec
91+
@test_throws ArgumentError TT.colspec(missing)
92+
# empty selection
93+
@test_throws ArgumentError TT.colspec(Symbol[])
94+
@test_throws ArgumentError TT.colspec(String[])
95+
@test_throws ArgumentError TT.colspec(Int[])
96+
@test_throws ArgumentError TT.colspec(())
97+
# regex doesn't match any names in input table
6998
colspec = TT.colspec(r"x")
7099
@test_throws AssertionError TT.choose(colspec, vecnames)
71100
@test_throws AssertionError TT.choose(colspec, tupnames)
72-
@test_throws AssertionError TT.colspec(Symbol[])
73-
@test_throws AssertionError TT.colspec(String[])
74-
@test_throws AssertionError TT.colspec(Int[])
75-
@test_throws ArgumentError TT.colspec(())
76-
@test_throws ArgumentError TT.colspec(missing)
101+
# names not present in input table
102+
colspec = TT.colspec([:x, :a])
103+
@test_throws AssertionError TT.choose(colspec, vecnames)
104+
@test_throws AssertionError TT.choose(colspec, tupnames)
77105

78106
# type stability
79107
colspec = TT.colspec([:a, :b])
@@ -103,4 +131,13 @@
103131
colspec = TT.colspec(nothing)
104132
@inferred TT.choose(colspec, vecnames)
105133
@inferred TT.choose(colspec, tupnames)
134+
colspec = TT.colspec(:a)
135+
@inferred TT.choose(colspec, vecnames)
136+
@inferred TT.choose(colspec, tupnames)
137+
colspec = TT.colspec("b")
138+
@inferred TT.choose(colspec, vecnames)
139+
@inferred TT.choose(colspec, tupnames)
140+
colspec = TT.colspec(3)
141+
@inferred TT.choose(colspec, vecnames)
142+
@inferred TT.choose(colspec, tupnames)
106143
end

test/transforms/filter.jl

+4-4
Original file line numberDiff line numberDiff line change
@@ -397,13 +397,13 @@ end
397397
@test ntypes[6] == Missing
398398
@test ttypes == Tables.schema(tₒ).types
399399

400-
# throws: empty tuple
400+
# throws: empty selection
401401
@test_throws ArgumentError DropMissing(())
402+
@test_throws ArgumentError DropMissing(Symbol[])
403+
@test_throws ArgumentError DropMissing(String[])
402404

403-
# throws: empty selection
405+
# throws: regex doesn't match any names in input table
404406
@test_throws AssertionError apply(DropMissing(r"g"), t)
405-
@test_throws AssertionError DropMissing(Symbol[])
406-
@test_throws AssertionError DropMissing(String[])
407407

408408
# throws: columns that do not exist in the original table
409409
@test_throws AssertionError apply(DropMissing(:g, :h), t)

test/transforms/select.jl

+18-14
Original file line numberDiff line numberDiff line change
@@ -224,12 +224,14 @@
224224

225225
# throws: Select without arguments
226226
@test_throws ArgumentError Select()
227-
@test_throws ArgumentError Select(())
228-
227+
229228
# throws: empty selection
229+
@test_throws ArgumentError Select(())
230+
@test_throws ArgumentError Select(Symbol[])
231+
@test_throws ArgumentError Select(String[])
232+
233+
# throws: regex doesn't match any names in input table
230234
@test_throws AssertionError apply(Select(r"a"), t)
231-
@test_throws AssertionError Select(Symbol[])
232-
@test_throws AssertionError Select(String[])
233235

234236
# throws: columns that do not exist in the original table
235237
@test_throws AssertionError apply(Select(:x3, :y3), t)
@@ -372,19 +374,21 @@ end
372374

373375
# throws: Reject without arguments
374376
@test_throws ArgumentError Reject()
377+
378+
# throws: empty rejection
375379
@test_throws ArgumentError Reject(())
380+
@test_throws ArgumentError Reject(Symbol[])
381+
@test_throws ArgumentError Reject(String[])
376382

377-
# throws: empty rejection
383+
# throws: regex doesn't match any names in input table
378384
@test_throws AssertionError apply(Reject(r"a"), t)
379-
@test_throws AssertionError Reject(Symbol[])
380-
@test_throws AssertionError Reject(String[])
381385

382386
# throws: reject all columns
383-
@test_throws AssertionError apply(Reject(r"[xy]"), t)
384-
@test_throws AssertionError apply(Reject(:x1, :x2, :y1, :y2), t)
385-
@test_throws AssertionError apply(Reject([:x1, :x2, :y1, :y2]), t)
386-
@test_throws AssertionError apply(Reject((:x1, :x2, :y1, :y2)), t)
387-
@test_throws AssertionError apply(Reject("x1", "x2", "y1", "y2"), t)
388-
@test_throws AssertionError apply(Reject(["x1", "x2", "y1", "y2"]), t)
389-
@test_throws AssertionError apply(Reject(["x1", "x2", "y1", "y2"]), t)
387+
@test_throws ArgumentError apply(Reject(r"[xy]"), t)
388+
@test_throws ArgumentError apply(Reject(:x1, :x2, :y1, :y2), t)
389+
@test_throws ArgumentError apply(Reject([:x1, :x2, :y1, :y2]), t)
390+
@test_throws ArgumentError apply(Reject((:x1, :x2, :y1, :y2)), t)
391+
@test_throws ArgumentError apply(Reject("x1", "x2", "y1", "y2"), t)
392+
@test_throws ArgumentError apply(Reject(["x1", "x2", "y1", "y2"]), t)
393+
@test_throws ArgumentError apply(Reject(["x1", "x2", "y1", "y2"]), t)
390394
end

test/transforms/sort.jl

+5-3
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,14 @@
7575

7676
# throws: Sort without arguments
7777
@test_throws ArgumentError Sort()
78-
@test_throws ArgumentError Sort(())
7978

8079
# throws: empty selection
80+
@test_throws ArgumentError Sort(())
81+
@test_throws ArgumentError Sort(Symbol[])
82+
@test_throws ArgumentError Sort(String[])
83+
84+
# throws: regex doesn't match any names in input table
8185
@test_throws AssertionError apply(Sort(r"x"), t)
82-
@test_throws AssertionError Sort(Symbol[])
83-
@test_throws AssertionError Sort(String[])
8486

8587
# throws: columns that do not exist in the original table
8688
@test_throws AssertionError apply(Sort([:d, :e]), t)

0 commit comments

Comments
 (0)