Skip to content

Commit f67d0be

Browse files
authored
Add Unitify transform (#256)
* Add 'Unitify' transform * Fix typo * Fix test * Apply suggestions * Apply suggestions
1 parent 2c53837 commit f67d0be

File tree

7 files changed

+147
-0
lines changed

7 files changed

+147
-0
lines changed

docs/src/transforms.md

+6
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,12 @@ DropConstant
104104
AbsoluteUnits
105105
```
106106

107+
## Unitify
108+
109+
```@docs
110+
Unitify
111+
```
112+
107113
## Map
108114

109115
```@docs

src/TableTransforms.jl

+1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ export
6565
DropExtrema,
6666
DropUnits,
6767
AbsoluteUnits,
68+
Unitify,
6869
Map,
6970
Replace,
7071
Coalesce,

src/transforms.jl

+1
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ include("transforms/dropextrema.jl")
279279
include("transforms/dropunits.jl")
280280
include("transforms/dropconstant.jl")
281281
include("transforms/absoluteunits.jl")
282+
include("transforms/unitify.jl")
282283
include("transforms/map.jl")
283284
include("transforms/replace.jl")
284285
include("transforms/coalesce.jl")

src/transforms/unitify.jl

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# ------------------------------------------------------------------
2+
# Licensed under the MIT License. See LICENSE in the project root.
3+
# ------------------------------------------------------------------
4+
5+
"""
6+
Unitify()
7+
8+
Add units to columns of the table using bracket syntax.
9+
A column named `col [unit]` will be renamed to a unitful
10+
column `col` with a valid `unit` from Unitful.jl.
11+
12+
In the case that the `unit` is not recognized by Unitful.jl,
13+
no units are added. Empty brackets are also allowed to represent
14+
columns without units, e.g. `col []`.
15+
"""
16+
struct Unitify <: StatelessFeatureTransform end
17+
18+
isrevertible(::Type{Unitify}) = true
19+
20+
function _unitify(name)
21+
m = match(r"(.*)\[(.*)\]", string(name))
22+
if !isnothing(m)
23+
namestr, unitstr = m.captures
24+
newname = Symbol(strip(namestr))
25+
unit = if !isempty(unitstr)
26+
try
27+
uparse(unitstr)
28+
catch
29+
@warn "the unit \"$unitstr\" is not valid"
30+
NoUnits
31+
end
32+
else
33+
NoUnits
34+
end
35+
newname, unit
36+
else
37+
name, NoUnits
38+
end
39+
end
40+
41+
function applyfeat(::Unitify, feat, prep)
42+
cols = Tables.columns(feat)
43+
names = Tables.columnnames(cols)
44+
45+
pairs = map(names) do name
46+
x = Tables.getcolumn(cols, name)
47+
newname, unit = _unitify(name)
48+
newname => x * unit
49+
end
50+
51+
newfeat = (; pairs...) |> Tables.materializer(feat)
52+
newfeat, names
53+
end
54+
55+
function revertfeat(::Unitify, newfeat, fcache)
56+
cols = Tables.columns(newfeat)
57+
names = Tables.columnnames(cols)
58+
59+
onames = fcache
60+
ocolumns = map(names) do name
61+
x = Tables.getcolumn(cols, name)
62+
ustrip.(x)
63+
end
64+
65+
𝒯 = (; zip(onames, ocolumns)...)
66+
𝒯 |> Tables.materializer(newfeat)
67+
end

test/shows.jl

+26
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,32 @@
217217
@test iostr == "DropConstant transform"
218218
end
219219

220+
@testset "AbsoluteUnits" begin
221+
T = AbsoluteUnits(:a, :b, :c)
222+
223+
# compact mode
224+
iostr = sprint(show, T)
225+
@test iostr == "AbsoluteUnits([:a, :b, :c])"
226+
227+
# full mode
228+
iostr = sprint(show, MIME("text/plain"), T)
229+
@test iostr == """
230+
AbsoluteUnits transform
231+
└─ selector = [:a, :b, :c]"""
232+
end
233+
234+
@testset "Unitify" begin
235+
T = Unitify()
236+
237+
# compact mode
238+
iostr = sprint(show, T)
239+
@test iostr == "Unitify()"
240+
241+
# full mode
242+
iostr = sprint(show, MIME("text/plain"), T)
243+
@test iostr == "Unitify transform"
244+
end
245+
220246
@testset "Map" begin
221247
fun = (a, b) -> 2a + b
222248
T = Map(:a => sin, [:a, :b] => fun => :c)

test/transforms.jl

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ transformfiles = [
1313
"dropunits.jl",
1414
"dropconstant.jl",
1515
"absoluteunits.jl",
16+
"unitify.jl",
1617
"map.jl",
1718
"replace.jl",
1819
"coalesce.jl",

test/transforms/unitify.jl

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
@testset "Unitify" begin
2+
@test isrevertible(Unitify())
3+
4+
anm = Symbol("a [m]")
5+
bnm = :b
6+
cnm = Symbol("c [km/hr]")
7+
dnm = :d
8+
enm = Symbol("e [°C]")
9+
a = rand(10)
10+
b = rand(10)
11+
c = rand(10)
12+
d = rand(10)
13+
e = rand(10)
14+
t = Table(; anm => a, bnm => b, cnm => c, dnm => d, enm => e)
15+
16+
T = Unitify()
17+
n, c = apply(T, t)
18+
@test Tables.schema(n).names == (:a, :b, :c, :d, :e)
19+
@test unit(eltype(n.a)) === u"m"
20+
@test unit(eltype(n.b)) === NoUnits
21+
@test unit(eltype(n.c)) === u"km/hr"
22+
@test unit(eltype(n.d)) === NoUnits
23+
@test unit(eltype(n.e)) === u"°C"
24+
tₒ = revert(T, n, c)
25+
@test tₒ == t
26+
27+
# no units
28+
t = Table(; Symbol("a []") => rand(10), Symbol("b [NoUnits]") => rand(10))
29+
T = Unitify()
30+
n, c = apply(T, t)
31+
@test Tables.schema(n).names == (:a, :b)
32+
@test unit(eltype(n.a)) === NoUnits
33+
@test unit(eltype(n.b)) === NoUnits
34+
tₒ = revert(T, n, c)
35+
@test tₒ == t
36+
37+
# invalid unit name
38+
t = Table(; Symbol("a [test]") => rand(10))
39+
T = Unitify()
40+
n, c = apply(T, t)
41+
@test Tables.schema(n).names == (:a,)
42+
@test unit(eltype(n.a)) === NoUnits
43+
tₒ = revert(T, n, c)
44+
@test tₒ == t
45+
end

0 commit comments

Comments
 (0)