-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathreplace.jl
More file actions
101 lines (83 loc) · 3 KB
/
Copy pathreplace.jl
File metadata and controls
101 lines (83 loc) · 3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# ------------------------------------------------------------------
# Licensed under the MIT License. See LICENSE in the project root.
# ------------------------------------------------------------------
"""
Replace(cols₁ => pred₁ => new₁, pred₂ => new₂, ..., colsₙ => predₙ => newₙ)
Replaces all values where `predᵢ` predicate returns `true` with `newᵢ` value
in the the columns selected by `colsᵢ`.
Passing a column selection is optional and when omitted all columns in the table
will be selected. The column selection can be a single column identifier (index or name),
a collection of identifiers, or a regular expression (regex).
The predicate can be a function that accepts a single argument
and returns a boolean, or a value. If the predicate is a value,
it will be transformed into the following function: `x -> x === value`.
## Examples
```julia
Replace(1 => -1, 5 => -5)
Replace(2 => 0.0 => 1.5, 5.0 => 5.5)
Replace(:b => 0.0 => 1.5, 5.0 => 5.5)
Replace("b" => 0.0 => 1.5, 5.0 => 5.5)
Replace([1, 3] => >(5) => 5)
Replace([:a, :c] => isequal(2) => -2)
Replace(["a", "c"] => (x -> 4 < x < 6) => 0)
Replace(r"[abc]" => (x -> isodd(x) && x > 10) => 2)
```
## Notes
* Anonymous functions must be passed with parentheses as in the examples above.
* Replacements are applied in the sequence in which they are defined, therefore,
if there is more than one replacement for the same column, the first valid one will be applied.
"""
struct Replace <: StatelessFeatureTransform
selectors::Vector{ColumnSelector}
preds::Vector{Function}
news::Vector{Any}
end
Replace() = throw(ArgumentError("cannot create Replace transform without arguments"))
# utility functions
_replaceargs(p::Pair) = AllSelector(), _pred(first(p)), last(p)
_replaceargs(p::Pair{<:Any,<:Pair}) = selector(first(p)), _pred(first(last(p))), last(last(p))
_pred(f::Function) = f
_pred(v) = Base.Fix2(===, v)
function Replace(pairs::Pair...)
tuples = map(_replaceargs, pairs)
selectors = [t[1] for t in tuples]
preds = [t[2] for t in tuples]
news = Any[t[3] for t in tuples]
Replace(selectors, preds, news)
end
function applyfeat(transform::Replace, feat, prep)
cols = Tables.columns(feat)
names = Tables.columnnames(cols)
selectors = transform.selectors
preds = transform.preds
news = transform.news
# preprocess all replacements
prepreps = map(selectors, preds, news) do selector, pred, new
snames = selector(names)
snames => pred => new
end
# join replacements of each column
colreps = map(names) do name
pairs = filter(p -> name ∈ first(p), prepreps)
reps = isempty(pairs) ? nothing : map(last, pairs)
name => reps
end
columns = map(colreps) do (name, reps)
x = Tables.getcolumn(cols, name)
if isnothing(reps)
x
else
map(x) do v
for (pred, new) in reps
if pred(v)
return new
end
end
v
end
end
end
𝒯 = (; zip(names, columns)...)
newfeat = 𝒯 |> Tables.materializer(feat)
newfeat, nothing
end