Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unexpected behaviour of sapply() within vectorized function

I wanted to play around with modular arithmetic and programmed some innocently looking function... but got totally surprised by the following unexpected behaviour:

crt <- function(x, mods = c(5, 7)) {
  sapply(mods, \(y) x %% y)
}
crt <- Vectorize(crt)

crt(20)
##      [,1]
## [1,]    0
## [2,]    6

crt(55)
##      [,1]
## [1,]    0
## [2,]    6

crt(1:100)
##      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14]
## [1,]    1    2    3    4    0    1    2    3    4     0     1     2     3     4
## [2,]    1    2    3    4    5    6    0    1    2     3     4     5     6     0
##      [,15] [,16] [,17] [,18] [,19] [,20] [,21] [,22] [,23] [,24] [,25] [,26]
## [1,]     0     1     2     3     4     0     1     2     3     4     0     1
## [2,]     1     2     3     4     5     6     0     1     2     3     4     5
##      [,27] [,28] [,29] [,30] [,31] [,32] [,33] [,34] [,35] [,36] [,37] [,38]
## [1,]     2     3     4     0     1     2     3     4     0     1     2     3
## [2,]     6     0     1     2     3     4     5     6     0     1     2     3
##      [,39] [,40] [,41] [,42] [,43] [,44] [,45] [,46] [,47] [,48] [,49] [,50]
## [1,]     4     0     1     2     3     4     0     1     2     3     4     0
## [2,]     4     5     6     0     1     2     3     4     5     6     0     1
##      [,51] [,52] [,53] [,54] [,55] [,56] [,57] [,58] [,59] [,60] [,61] [,62]
## [1,]     1     2     3     4     0     1     2     3     4     0     1     2
## [2,]     2     3     4     5     6     0     1     2     3     4     5     6
##      [,63] [,64] [,65] [,66] [,67] [,68] [,69] [,70] [,71] [,72] [,73] [,74]
## [1,]     3     4     0     1     2     3     4     0     1     2     3     4
## [2,]     0     1     2     3     4     5     6     0     1     2     3     4
##      [,75] [,76] [,77] [,78] [,79] [,80] [,81] [,82] [,83] [,84] [,85] [,86]
## [1,]     0     1     2     3     4     0     1     2     3     4     0     1
## [2,]     5     6     0     1     2     3     4     5     6     0     1     2
##      [,87] [,88] [,89] [,90] [,91] [,92] [,93] [,94] [,95] [,96] [,97] [,98]
## [1,]     2     3     4     0     1     2     3     4     0     1     2     3
## [2,]     3     4     5     6     0     1     2     3     4     5     6     0
##      [,99] [,100]
## [1,]     4      0
## [2,]     1      2

crt(x = 1:100, mods = c(12, 60))
##   [1]  1  2  3  4  5  6  7  8  9 10 11 12  1 14  3 16  5 18  7 20  9 22 11 24  1
##  [26] 26  3 28  5 30  7 32  9 34 11 36  1 38  3 40  5 42  7 44  9 46 11 48  1 50
##  [51]  3 52  5 54  7 56  9 58 11  0  1  2  3  4  5  6  7  8  9 10 11 12  1 14  3
##  [76] 16  5 18  7 20  9 22 11 24  1 26  3 28  5 30  7 32  9 34 11 36  1 38  3 40

Why is the last function call crt(x = 1:100, mods = c(12, 60)) giving a totally different output? The first vectorized output crt(1:100) is what I wanted and expected, the last one doesn't seem structurally different but the result is... why? And how do I fix this to get the same output as the first?

like image 724
vonjd Avatar asked Dec 05 '25 14:12

vonjd


1 Answers

According to ?Vectorize

The arguments named in the vectorize.args argument to Vectorize are the arguments passed in the ... list to mapply. Only those that are actually passed will be vectorized; default values will not.

Here, in the OP's function, there is default value for 'mods'. If we remove it

crt <- function(x, mods) {
   sapply(mods, \(y) x %% y)
 }
crt <- Vectorize(crt)

-testing

> crt(1:100, mods = c(5, 7))
  [1] 1 2 3 4 0 6 2 1 4 3 1 5 3 0 0 2 2 4 4 6 1 1 3 3 0 5 2 0 4 2 1 4 3 6 0 1 2 3 4 5 1 0 3 2 0 4 2 6 4 1 1 3 3 5 0 0 2 2 4 4 1 6 3 1 0 3 2 5 4 0 1 2 3 4
 [75] 0 6 2 1 4 3 1 5 3 0 0 2 2 4 4 6 1 1 3 3 0 5 2 0 4 2
> crt(1:100, mods = c(12, 60))
  [1]  1  2  3  4  5  6  7  8  9 10 11 12  1 14  3 16  5 18  7 20  9 22 11 24  1 26  3 28  5 30  7 32  9 34 11 36  1 38  3 40  5 42  7 44  9 46 11 48  1
 [50] 50  3 52  5 54  7 56  9 58 11  0  1  2  3  4  5  6  7  8  9 10 11 12  1 14  3 16  5 18  7 20  9 22 11 24  1 26  3 28  5 30  7 32  9 34 11 36  1 38
 [99]  3 40

The output format is determined at two levels here - 1) sapply which by default uses simplify = TRUE and Vectorize which also by default have SIMPLIFY = TRUE

Also, based on the function defined, Vectorize is not really needed as internally, it does the looping with *apply functions and we already have the crt defined with sapply which loop over the 'mods'. The function applied %% on those parameters is %% which is already a vectorized function by default.

like image 60
akrun Avatar answered Dec 07 '25 03:12

akrun



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!