Skip to content

Commit ee9f36e

Browse files
authored
Merge pull request #42 from eliascarv/replace
Add Replace
2 parents af0128a + 902944c commit ee9f36e

File tree

5 files changed

+115
-1
lines changed

5 files changed

+115
-1
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ Please check the docstrings for additional information.
150150
| `Filter` | Row filtering |
151151
| `DropMissing` | Drop missings |
152152
| `Rename` | Column renaming |
153+
| `Replace` | Replace values |
153154
| `Coalesce` | Replace missings |
154155
| `Coerce` | Coerce scientific types |
155156
| `Identity` | Identity transform |

src/TableTransforms.jl

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export
3636
Filter,
3737
DropMissing,
3838
Rename,
39+
Replace,
3940
Coalesce,
4041
Coerce,
4142
Identity,

src/transforms.jl

+1
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ end
214214
include("transforms/select.jl")
215215
include("transforms/filter.jl")
216216
include("transforms/rename.jl")
217+
include("transforms/replace.jl")
217218
include("transforms/coalesce.jl")
218219
include("transforms/coerce.jl")
219220
include("transforms/identity.jl")

src/transforms/replace.jl

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# ------------------------------------------------------------------
2+
# Licensed under the MIT License. See LICENSE in the project root.
3+
# ------------------------------------------------------------------
4+
5+
"""
6+
Replace(old₁ => new₁, old₂ => new₂, ..., oldₙ => newₙ)
7+
8+
Replaces `oldᵢ` value with `newᵢ` value in the table.
9+
"""
10+
struct Replace{K,V} <: Colwise
11+
pairs::IdDict{K,V}
12+
end
13+
14+
Replace() = throw(ArgumentError("Cannot create a Replace object without arguments."))
15+
16+
Replace(pairs::Pair...) = Replace(IdDict(values(pairs)))
17+
18+
isrevertible(::Type{<:Replace}) = true
19+
20+
function colcache(transform::Replace, x)
21+
olds = keys(transform.pairs)
22+
inds = [findall(v -> v === old, x) .=> old for old in olds]
23+
Dict(reduce(vcat, inds))
24+
end
25+
26+
colapply(transform::Replace, x, c) = [get(transform.pairs, xᵢ, xᵢ) for xᵢ in x]
27+
28+
colrevert(transform::Replace, x, c) = [get(c, i, x[i]) for i in 1:length(x)]

test/transforms.jl

+84-1
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,89 @@
628628
@test n1 == n2
629629
end
630630

631+
@testset "Replace" begin
632+
a = [3, 2, 1, 4, 5, 3]
633+
b = [2, 4, 4, 5, 8, 5]
634+
c = [1, 1, 6, 2, 4, 1]
635+
d = [4, 3, 7, 5, 4, 1]
636+
e = [5, 5, 2, 6, 5, 2]
637+
f = [4, 4, 3, 4, 5, 2]
638+
t = Table(; a, b, c, d, e, f)
639+
640+
# replace with a value of the same type
641+
T = Replace(1 => -1, 5 => -5)
642+
n, c = apply(T, t)
643+
@test n.a == [3, 2, -1, 4, -5, 3]
644+
@test n.b == [2, 4, 4, -5, 8, -5]
645+
@test n.c == [-1, -1, 6, 2, 4, -1]
646+
@test n.d == [4, 3, 7, -5, 4, -1]
647+
@test n.e == [-5, -5, 2, 6, -5, 2]
648+
@test n.f == [4, 4, 3, 4, -5, 2]
649+
@test isrevertible(T) == true
650+
tₒ = revert(T, n, c)
651+
@test t == tₒ
652+
653+
# table schema after apply and revert
654+
T = Replace(1 => -1, 5 => -5)
655+
n, c = apply(T, t)
656+
types = Tables.schema(t).types
657+
@test types == Tables.schema(n).types
658+
tₒ = revert(T, n, c)
659+
@test types == Tables.schema(tₒ).types
660+
661+
# replace with a value of another type
662+
T = Replace(1 => 1.5, 5 => 5.5, 4 => true)
663+
n, c = apply(T, t)
664+
@test n.a == Real[3, 2, 1.5, true, 5.5, 3]
665+
@test n.b == Real[2, true, true, 5.5, 8, 5.5]
666+
@test n.c == Real[1.5, 1.5, 6, 2, true, 1.5]
667+
@test n.d == Real[true, 3, 7, 5.5, true, 1.5]
668+
@test n.e == Real[5.5, 5.5, 2, 6, 5.5, 2]
669+
@test n.f == Real[true, true, 3, true, 5.5, 2]
670+
tₒ = revert(T, n, c)
671+
@test t == tₒ
672+
673+
# table schema after apply and revert
674+
T = Replace(1 => 1.5, 5 => 5.5, 4 => true)
675+
n, c = apply(T, t)
676+
tₒ = revert(T, n, c)
677+
ttypes = Tables.schema(t).types
678+
ntypes = Tables.schema(n).types
679+
@test ntypes[1] == Real
680+
@test ntypes[2] == Real
681+
@test ntypes[3] == Real
682+
@test ntypes[4] == Real
683+
@test ntypes[5] == Real
684+
@test ntypes[6] == Real
685+
@test ttypes == Tables.schema(tₒ).types
686+
687+
# no occurrences
688+
T = Replace(10 => 11, 20 => 30)
689+
n, c = apply(T, t)
690+
@test t == n
691+
tₒ = revert(T, n, c)
692+
@test t == tₒ
693+
694+
# collumns with diferent types
695+
a = [3, 2, 1, 4, 5, 3]
696+
b = [2.5, 4.5, 4.7, 2.5, 2.5, 5.3]
697+
c = [true, false, false, false, true, false]
698+
d = ['a', 'b', 'c', 'd', 'e', 'a']
699+
t = Table(; a, b, c, d)
700+
701+
T = Replace(3 => -3, 2.5 => 2.0, true => false, 'a' => 'A')
702+
n, c = apply(T, t)
703+
@test n.a == [-3, 2, 1, 4, 5, -3]
704+
@test n.b == [2.0, 4.5, 4.7, 2.0, 2.0, 5.3]
705+
@test n.c == [false, false, false, false, false, false]
706+
@test n.d == ['A', 'b', 'c', 'd', 'e', 'A']
707+
tₒ = revert(T, n, c)
708+
@test t == tₒ
709+
710+
# throws
711+
@test_throws ArgumentError Replace()
712+
end
713+
631714
@testset "Coalesce" begin
632715
a = [3, 2, missing, 4, 5, 3]
633716
b = [missing, 4, 4, 5, 8, 5]
@@ -672,7 +755,7 @@
672755
@test ntypes[6] == Int
673756
@test ttypes == Tables.schema(tₒ).types
674757
end
675-
758+
676759
@testset "Coerce" begin
677760
x1 = [1.0, 2.0, 3.0, 4.0, 5.0]
678761
x2 = [1.0, 2.0, 3.0, 4.0, 5.0]

0 commit comments

Comments
 (0)