library(tinycodet)
#> Run `?tinycodet::tinycodet` to open the introduction help page of 'tinycodet'.The transform_if function
“Don’t Repeat Yourself”, sometimes abbreviated as “DRY”, is the coding principle that you should try to reduce repeating patterns in your code (within reason).
Consider the following code:
object <- matrix(c(-9:8, NA, NA) , ncol=2)
y <- 0
z <- 1000
ifelse(
is.na(object>y), -z,
ifelse(
object>y, log(object), object^2
)
)
#> Warning in log(object): NaNs produced
#> [,1] [,2]
#> [1,] 81 0.0000000
#> [2,] 64 0.6931472
#> [3,] 49 1.0986123
#> [4,] 36 1.3862944
#> [5,] 25 1.6094379
#> [6,] 16 1.7917595
#> [7,] 9 1.9459101
#> [8,] 4 2.0794415
#> [9,] 1 -1000.0000000
#> [10,] 0 -1000.0000000Here a conditional subset of the object object is
transformed where the condition is using a function referring to
object itself. Consequently, reference to
object is written 4 times! This can become cumbersome
quickly. Notice also that the above code gives an unnecessary warning,
due to ifelse() requiring the entirety of
log(object).
The tinycodet package therefore adds the
transform_if(x, cond, yes, no, other) function, which will
“dry” this up. Here, in argument cond a function must be
given that returns a logical vector. For every value where
cond(x)==TRUE, function yes(x) is run, for
every value where cond(x)==FALSE, function
no(x) is run, and for every value where
cond(x)==NA, function other is run. Because a
function-based approach is used instead of directly supplying vectors,
unnecessary warnings and annoying errors are avoided (unlike the above
code).
The above code can now be re-written in a less warning/error prone and more compact manner as:
object |> transform_if(\(x)x>y, log, \(x)x^2, \(x) -z)
#> [,1] [,2]
#> [1,] 81 0.0000000
#> [2,] 64 0.6931472
#> [3,] 49 1.0986123
#> [4,] 36 1.3862944
#> [5,] 25 1.6094379
#> [6,] 16 1.7917595
#> [7,] 9 1.9459101
#> [8,] 4 2.0794415
#> [9,] 1 -1000.0000000
#> [10,] 0 -1000.0000000Instead of supplying a function for cond, one can also
directly supply a logical vector to argument cond.
Moreover, when the transformed value is an atomic scalar, you don’t
really need a function; you can just fill in the scalar (vectors are not
allowed though, as that will lead the same unnecessary warnings or even
annoying errors as occur with ifelse()).
So one can thus also re-write the original code (without warnings/errors and more compact) as:
object |> transform_if(object > y, log, \(x)x^2, -z)
#> [,1] [,2]
#> [1,] 81 0.0000000
#> [2,] 64 0.6931472
#> [3,] 49 1.0986123
#> [4,] 36 1.3862944
#> [5,] 25 1.6094379
#> [6,] 16 1.7917595
#> [7,] 9 1.9459101
#> [8,] 4 2.0794415
#> [9,] 1 -1000.0000000
#> [10,] 0 -1000.0000000
Matrix re-ordering operators
The tinycodet R package adds 2 additional matrix
operators:
- The
x %row~% matoperator re-orders the elements of every row, each row ordered independently from the other rows, of matrixx, by the ordering ranks given in matrixmat. - The
x %col~% matoperator re-orders the elements of every column, each column ordered independently from the other columns, of matrixx, by the ordering ranks given in matrixmat.
Examples with a numeric matrix:
x <- matrix(sample(1:25), nrow=5)
print(x)
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 13 15 22 14 25
#> [2,] 23 20 19 10 11
#> [3,] 12 4 8 18 17
#> [4,] 5 21 2 16 1
#> [5,] 6 9 7 24 3
x %row~% x # sort elements of every row
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 13 14 15 22 25
#> [2,] 10 11 19 20 23
#> [3,] 4 8 12 17 18
#> [4,] 1 2 5 16 21
#> [5,] 3 6 7 9 24
x %row~% -x # reverse-sort elements of every row
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 25 22 15 14 13
#> [2,] 23 20 19 11 10
#> [3,] 18 17 12 8 4
#> [4,] 21 16 5 2 1
#> [5,] 24 9 7 6 3
x %col~% x # sort elements of every column
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 5 4 2 10 1
#> [2,] 6 9 7 14 3
#> [3,] 12 15 8 16 11
#> [4,] 13 20 19 18 17
#> [5,] 23 21 22 24 25
x %col~% -x # reverse-sort elements of every column
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 23 21 22 24 25
#> [2,] 13 20 19 18 17
#> [3,] 12 15 8 16 11
#> [4,] 6 9 7 14 3
#> [5,] 5 4 2 10 1
x <- matrix(sample(1:25), nrow=5)
print(x)
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 22 9 8 3 5
#> [2,] 11 10 25 13 16
#> [3,] 7 2 1 20 15
#> [4,] 21 17 14 23 12
#> [5,] 6 4 18 24 19
rand <- sample(1:length(x)) |> matrix(ncol=ncol(x)) # matrix of random integers
x %row~% rand # random shuffle every row independent of other rows
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 8 3 9 5 22
#> [2,] 25 11 13 10 16
#> [3,] 2 20 7 1 15
#> [4,] 21 17 12 14 23
#> [5,] 6 19 24 4 18
x %col~% rand # random shuffle every column independent of other columns
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 6 17 8 20 12
#> [2,] 21 2 25 3 19
#> [3,] 7 9 14 23 5
#> [4,] 11 4 1 24 16
#> [5,] 22 10 18 13 15Examples with a character matrix:
x <- matrix(sample(letters, 25), nrow=5)
print(x)
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] "b" "n" "t" "r" "k"
#> [2,] "f" "e" "a" "z" "d"
#> [3,] "g" "x" "m" "p" "h"
#> [4,] "l" "w" "y" "s" "i"
#> [5,] "v" "q" "o" "c" "j"
mat <- stringi::stri_rank(as.vector(x)) |>
matrix(ncol=ncol(x)) # matrix of ordering ranks
x %row~% mat # sort elements of every row
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] "b" "k" "n" "r" "t"
#> [2,] "a" "d" "e" "f" "z"
#> [3,] "g" "h" "m" "p" "x"
#> [4,] "i" "l" "s" "w" "y"
#> [5,] "c" "j" "o" "q" "v"
x %row~% -mat # reverse-sort elements of every row
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] "t" "r" "n" "k" "b"
#> [2,] "z" "f" "e" "d" "a"
#> [3,] "x" "p" "m" "h" "g"
#> [4,] "y" "w" "s" "l" "i"
#> [5,] "v" "q" "o" "j" "c"
x %col~% mat # sort elements of every column
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] "b" "e" "a" "c" "d"
#> [2,] "f" "n" "m" "p" "h"
#> [3,] "g" "q" "o" "r" "i"
#> [4,] "l" "w" "t" "s" "j"
#> [5,] "v" "x" "y" "z" "k"
x %col~% -mat # reverse-sort elements of every column
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] "v" "x" "y" "z" "k"
#> [2,] "l" "w" "t" "s" "j"
#> [3,] "g" "q" "o" "r" "i"
#> [4,] "f" "n" "m" "p" "h"
#> [5,] "b" "e" "a" "c" "d"
x <- matrix(sample(letters, 25), nrow=5)
print(x)
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] "b" "f" "z" "p" "l"
#> [2,] "h" "m" "g" "v" "o"
#> [3,] "i" "t" "d" "q" "y"
#> [4,] "s" "c" "k" "u" "a"
#> [5,] "x" "w" "n" "r" "j"
rand <- sample(1:length(x)) |> matrix(ncol=ncol(x)) # matrix of random integers
x %row~% rand # random shuffle every row independent of other rows
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] "f" "z" "l" "p" "b"
#> [2,] "m" "v" "g" "h" "o"
#> [3,] "y" "t" "q" "i" "d"
#> [4,] "a" "s" "k" "c" "u"
#> [5,] "n" "w" "r" "x" "j"
x %col~% rand # random shuffle every column independent of other columns
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] "s" "m" "z" "v" "y"
#> [2,] "b" "t" "g" "p" "a"
#> [3,] "h" "f" "n" "q" "l"
#> [4,] "i" "w" "k" "r" "o"
#> [5,] "x" "c" "d" "u" "j"