Multidimensional array recycling
array_recycling.Rd
In base R, arithmetic and other binary operations between a matrix or
array and a vector don't let the user control how the latter should
be recycled: it's always recycled along the first dimension of the
matrix or array. This has led users to use various tricks when they
need recycling along the second dimension (a.k.a. "horizontal recycling"),
like the popular "double-transposition" trick: t(t(m) / colSums(m))
.
However this is not only inelegant, it's also inefficient.
as_tile()
is meant to address that.
It also allows arithmetic and other binary operations between two arrays of distinct dimensions (rejected as "non-conformable" by arithmetic operations in base R), typically between a small one (the tile) and a bigger one, as long as their geometries are compatible.
Arguments
- x
An array-like object or a vector.
- along
Can only be used when
x
is a vector. Must be a single positive integer indicating the "orientation" of the tile to be created, that is, the dimension along whichx
will be recycled by arithmetic operations and other binary operations.- dim
NULL
or the dimensions (supplied as an integer vector) of the tile to be created.
See also
base::colSums
in the base package.
Examples
## ---------------------------------------------------------------------
## 2D EXAMPLES
## ---------------------------------------------------------------------
m0 <- matrix(1:54, nrow=6)
x <- c(-1, 0, 100)
## Arithmetic operations in base R recycle 'x' along the first dimension
## of the matrix ("vertical recycling"):
m0 * x
#> [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
#> [1,] -1 -7 -13 -19 -25 -31 -37 -43 -49
#> [2,] 0 0 0 0 0 0 0 0 0
#> [3,] 300 900 1500 2100 2700 3300 3900 4500 5100
#> [4,] -4 -10 -16 -22 -28 -34 -40 -46 -52
#> [5,] 0 0 0 0 0 0 0 0 0
#> [6,] 600 1200 1800 2400 3000 3600 4200 4800 5400
## To recycle 'x' along the second dimension of the matrix ("horizontal
## recycling"), we turn it into an "horizontal" tile:
t <- as_tile(x, along=2)
t
#> An object of class "tile"
#> [,1] [,2] [,3]
#> [1,] -1 0 100
m0 * t
#> [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
#> [1,] -1 0 1300 -19 0 3100 -37 0 4900
#> [2,] -2 0 1400 -20 0 3200 -38 0 5000
#> [3,] -3 0 1500 -21 0 3300 -39 0 5100
#> [4,] -4 0 1600 -22 0 3400 -40 0 5200
#> [5,] -5 0 1700 -23 0 3500 -41 0 5300
#> [6,] -6 0 1800 -24 0 3600 -42 0 5400
## The above produces the same result as the double-transposition trick
## but is more efficient (and also makes code easier to read):
stopifnot(identical(m0 * t, t(t(m0) * x)))
## A less artificial example:
cs0 <- colSums(m0)
m <- m0 / as_tile(cs0, along=2)
stopifnot(all.equal(colSums(m), rep(1, ncol(m)))) # sanity check
## Using an arbitrary 2D tile:
t <- m0[1:2, 1:3]
t
#> [,1] [,2] [,3]
#> [1,] 1 7 13
#> [2,] 2 8 14
## Unfortunately arithmetic operations in base R refuse to operate on
## arrays that don't have the same dimensions:
if (FALSE) { # \dontrun{
m0 / t # ERROR! (non-conformable arrays)
} # }
## Wrapping 't' in a tile object makes this work:
m0 / as_tile(t)
#> [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
#> [1,] 1 1.000000 1.000000 19 3.571429 2.384615 37 6.142857 3.769231
#> [2,] 1 1.000000 1.000000 10 3.250000 2.285714 19 5.500000 3.571429
#> [3,] 3 1.285714 1.153846 21 3.857143 2.538462 39 6.428571 3.923077
#> [4,] 2 1.250000 1.142857 11 3.500000 2.428571 20 5.750000 3.714286
#> [5,] 5 1.571429 1.307692 23 4.142857 2.692308 41 6.714286 4.076923
#> [6,] 3 1.500000 1.285714 12 3.750000 2.571429 21 6.000000 3.857143
## ---------------------------------------------------------------------
## 3D EXAMPLES
## ---------------------------------------------------------------------
## Note that colSums() supports multidimensional arrays. In this case
## the user can use the 'dims' argument to control how the array should
## be sliced into "columns". See '?base::colSums' for the details.
a <- array(runif(300), dim=c(10, 5, 6))
## Using 'dims=2' indicates that the columns are the 2D slices obtained
## by slicing the array along its 3rd dimension. With this slicing, each
## column is a 10 x 5 matrix.
cs2 <- colSums(a, dims=2)
cs2 # vector of length 6 (one value per 2D slice)
#> [1] 25.67506 23.10812 28.01204 24.69427 25.85462 24.16648
t <- as_tile(cs2, along=3)
a / t
#> , , 1
#>
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 0.004905041 0.0313820275 0.013504781 0.034340070 0.031273211
#> [2,] 0.008819007 0.0030672134 0.023402695 0.028673615 0.034067962
#> [3,] 0.018539037 0.0188968499 0.036230123 0.009703260 0.020476150
#> [4,] 0.024183572 0.0061101572 0.035186878 0.032761256 0.037393265
#> [5,] 0.032485544 0.0328140273 0.003701757 0.021304701 0.011897596
#> [6,] 0.025992551 0.0205676668 0.025613195 0.005299261 0.015897284
#> [7,] 0.012352795 0.0005404554 0.021111480 0.011601741 0.019647763
#> [8,] 0.025103104 0.0309366446 0.003090926 0.025802346 0.009830089
#> [9,] 0.035149483 0.0381608824 0.022512773 0.007216479 0.000637245
#> [10,] 0.003971493 0.0382604699 0.008932281 0.022494866 0.014156928
#>
#> , , 2
#>
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 0.023408212 0.032013481 0.030976016 0.008872240 0.0017316806
#> [2,] 0.024658085 0.036715993 0.012625268 0.003163934 0.0382629819
#> [3,] 0.010510984 0.010414673 0.003472925 0.030776679 0.0088235694
#> [4,] 0.009080977 0.022375306 0.006696857 0.022659372 0.0083567068
#> [5,] 0.020014801 0.042445576 0.040491944 0.014215597 0.0358868522
#> [6,] 0.009445305 0.015871686 0.023190731 0.039719416 0.0005240762
#> [7,] 0.027874263 0.017434323 0.033030399 0.007488815 0.0371117158
#> [8,] 0.012258396 0.019354319 0.022418431 0.010978887 0.0051842433
#> [9,] 0.013212255 0.005340906 0.027456543 0.027242309 0.0367408961
#> [10,] 0.034816406 0.026625446 0.001295415 0.014497050 0.0322370546
#>
#> , , 3
#>
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 0.001948557 0.02856611 0.011266349 0.010631152 0.028324850
#> [2,] 0.026138822 0.02758221 0.033262982 0.004908784 0.015541477
#> [3,] 0.032675188 0.01170009 0.030916963 0.004226368 0.034859926
#> [4,] 0.021075356 0.03440793 0.008024639 0.004072719 0.019072370
#> [5,] 0.019220120 0.02982018 0.022671785 0.004484221 0.020914476
#> [6,] 0.006163747 0.03304549 0.029655188 0.005361134 0.003804231
#> [7,] 0.026391863 0.03542315 0.028097012 0.021148515 0.031938430
#> [8,] 0.008511148 0.03180742 0.026509085 0.032851818 0.028484426
#> [9,] 0.009836137 0.01932545 0.002237154 0.007912922 0.019250926
#> [10,] 0.017782804 0.01107234 0.032161136 0.022025014 0.022889834
#>
#> , , 4
#>
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 0.005586718 0.022945555 0.012158407 0.0067547344 0.017493634
#> [2,] 0.011761964 0.007327147 0.027619932 0.0022988334 0.027918303
#> [3,] 0.002529781 0.036250662 0.010508280 0.0349325524 0.013548310
#> [4,] 0.006043344 0.038171599 0.005111655 0.0205060362 0.035833769
#> [5,] 0.004125217 0.001665289 0.022182446 0.0343519157 0.039221271
#> [6,] 0.038906949 0.033747213 0.038420071 0.0011768958 0.007884861
#> [7,] 0.011035337 0.014176877 0.002164358 0.0402784480 0.036875389
#> [8,] 0.015000204 0.031158541 0.012522312 0.0288194156 0.009289926
#> [9,] 0.031394000 0.020684723 0.007403017 0.0003554224 0.010057287
#> [10,] 0.038474964 0.037374334 0.015329147 0.0353502877 0.035272666
#>
#> , , 5
#>
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 0.026177998 0.008248017 0.0008341776 0.010643768 0.03763645
#> [2,] 0.033850226 0.006680275 0.0256754885 0.014674892 0.01370559
#> [3,] 0.022480395 0.008466281 0.0385737968 0.038205423 0.03048280
#> [4,] 0.028457592 0.026971177 0.0386170017 0.002105223 0.02303327
#> [5,] 0.023587985 0.017854163 0.0294484226 0.006122665 0.02668107
#> [6,] 0.022971921 0.017822885 0.0117014790 0.005083239 0.02228533
#> [7,] 0.011198484 0.030615857 0.0324622404 0.034273374 0.03094888
#> [8,] 0.017246720 0.001433584 0.0160490507 0.013622107 0.01679579
#> [9,] 0.009529030 0.037539444 0.0060242378 0.002891859 0.02435357
#> [10,] 0.003982721 0.021416922 0.0114698328 0.028069420 0.03099787
#>
#> , , 6
#>
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 0.020988167 0.003358426 0.0187098136 0.038191692 0.026030911
#> [2,] 0.012139254 0.027485065 0.0241590253 0.029499412 0.014044658
#> [3,] 0.015681482 0.020468527 0.0167251797 0.038019307 0.007021392
#> [4,] 0.033874197 0.018380591 0.0002364385 0.001956707 0.013529513
#> [5,] 0.020244401 0.033606924 0.0053315623 0.021603269 0.012742241
#> [6,] 0.040669675 0.035812935 0.0061836003 0.024161318 0.008351151
#> [7,] 0.036576751 0.007860447 0.0165381814 0.014215748 0.039023867
#> [8,] 0.022030294 0.021839530 0.0160825688 0.010802439 0.002915852
#> [9,] 0.007730449 0.016434692 0.0257347023 0.031703360 0.027747372
#> [10,] 0.027079695 0.029374078 0.0122446292 0.041230229 0.003628278
#>
stopifnot(all.equal(colSums(a / t, dims=2), rep(1, 6))) # sanity check
## By default (i.e. when 'dims=1') the array is considered to be made
## of 5*6 columns of length 10.
cs1 <- colSums(a)
cs1 # 5 x 6 matrix
#> [,1] [,2] [,3] [,4] [,5] [,6]
#> [1,] 4.916816 4.281466 4.754868 4.071059 5.157559 5.727804
#> [2,] 5.667421 5.282326 7.360174 6.013102 4.577524 5.186640
#> [3,] 4.962653 4.659858 6.297171 3.788585 5.451594 3.430328
#> [4,] 5.114411 4.150550 3.294850 5.057992 4.025356 6.075055
#> [5,] 5.013762 4.733925 6.304976 5.763529 6.642584 3.746656
t <- as_tile(cs1, dim=c(1L, dim(cs1)))
a / t
#> , , 1
#>
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 0.02561358 0.14216970 0.06986910 0.17239199 0.160147542
#> [2,] 0.04605186 0.01389537 0.12107751 0.14394559 0.174459234
#> [3,] 0.09680877 0.08560822 0.18744222 0.04871173 0.104856679
#> [4,] 0.12628390 0.02768079 0.18204483 0.16446612 0.191487837
#> [5,] 0.16963586 0.14865708 0.01915162 0.10695260 0.060926613
#> [6,] 0.13573018 0.09317751 0.13251388 0.02660304 0.081408687
#> [7,] 0.06450491 0.00244842 0.10922355 0.05824238 0.100614582
#> [8,] 0.13108559 0.14015199 0.01599139 0.12953141 0.050339080
#> [9,] 0.18354665 0.17287988 0.11647336 0.03622774 0.003263279
#> [10,] 0.02073869 0.17333104 0.04621256 0.11292740 0.072496467
#>
#> , , 2
#>
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 0.12633988 0.14004655 0.15360933 0.04939607 0.008453005
#> [2,] 0.13308574 0.16061822 0.06260840 0.01761516 0.186776450
#> [3,] 0.05673037 0.04556015 0.01722215 0.17134871 0.043071263
#> [4,] 0.04901227 0.09788328 0.03320955 0.12615573 0.040792326
#> [5,] 0.10802480 0.18568292 0.20079858 0.07914513 0.175177640
#> [6,] 0.05097863 0.06943247 0.11500228 0.22113727 0.002558219
#> [7,] 0.15044425 0.07626840 0.16379696 0.04169387 0.181156674
#> [8,] 0.06616158 0.08466764 0.11117247 0.06112479 0.025306302
#> [9,] 0.07130979 0.02336439 0.13615634 0.15167116 0.179346559
#> [10,] 0.18791270 0.11647599 0.00642393 0.08071211 0.157361563
#>
#> , , 3
#>
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 0.01147941 0.10871960 0.050116701 0.09038355 0.12584295
#> [2,] 0.15398990 0.10497497 0.147965492 0.04173333 0.06904839
#> [3,] 0.19249716 0.04452928 0.137529573 0.03593158 0.15487729
#> [4,] 0.12415984 0.13095294 0.035696429 0.03462530 0.08473560
#> [5,] 0.11323021 0.11349243 0.100852106 0.03812379 0.09291980
#> [6,] 0.03631207 0.12576762 0.131916749 0.04557910 0.01690161
#> [7,] 0.15548062 0.13481676 0.124985434 0.17979969 0.14189753
#> [8,] 0.05014116 0.12105565 0.117921773 0.27929841 0.12655192
#> [9,] 0.05794698 0.07355060 0.009951649 0.06727379 0.08552890
#> [10,] 0.10476265 0.04214013 0.143064093 0.18725148 0.10169601
#>
#> , , 4
#>
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 0.03388797 0.094231507 0.07924936 0.032978150 0.07495278
#> [2,] 0.07134583 0.030090714 0.18002868 0.011223427 0.11961804
#> [3,] 0.01534517 0.148872169 0.06849371 0.170548667 0.05804874
#> [4,] 0.03665777 0.156760965 0.03331813 0.100115133 0.15353245
#> [5,] 0.02502277 0.006838915 0.14458676 0.167713866 0.16804645
#> [6,] 0.23600211 0.138591148 0.25042475 0.005745873 0.03378327
#> [7,] 0.06693824 0.058220796 0.01410744 0.196648544 0.15799534
#> [8,] 0.09098837 0.127960136 0.08162132 0.140702942 0.03980338
#> [9,] 0.19043000 0.084946850 0.04825339 0.001735253 0.04309119
#> [10,] 0.23338177 0.153486801 0.09991647 0.172588145 0.15112836
#>
#> , , 5
#>
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 0.13122917 0.046586175 0.003956153 0.06836427 0.14649056
#> [2,] 0.16968972 0.037731307 0.121768039 0.09425594 0.05334563
#> [3,] 0.11269325 0.047818963 0.182939288 0.24539109 0.11864676
#> [4,] 0.14265667 0.152337699 0.183144191 0.01352172 0.08965130
#> [5,] 0.11824555 0.100843286 0.139661478 0.03932550 0.10384948
#> [6,] 0.11515724 0.100666622 0.055495192 0.03264933 0.08674015
#> [7,] 0.05613751 0.172923454 0.153954748 0.22013578 0.12046088
#> [8,] 0.08645706 0.008097123 0.076113895 0.08749396 0.06537346
#> [9,] 0.04776861 0.212029029 0.028570425 0.01857424 0.09479025
#> [10,] 0.01996521 0.120966342 0.054396591 0.18028817 0.12065154
#>
#> , , 6
#>
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 0.08855230 0.01564816 0.131809653 0.151926021 0.16790319
#> [2,] 0.05121738 0.12806313 0.170199063 0.117348253 0.09059011
#> [3,] 0.06616258 0.09537047 0.117828011 0.151240276 0.04528901
#> [4,] 0.14292044 0.08564200 0.001665696 0.007783754 0.08726734
#> [5,] 0.08541424 0.15658715 0.037560576 0.085937505 0.08218932
#> [6,] 0.17159160 0.16686577 0.043563139 0.096113386 0.05386615
#> [7,] 0.15432293 0.03662475 0.116510618 0.056550047 0.25170967
#> [8,] 0.09294919 0.10175849 0.113300851 0.042971954 0.01880767
#> [9,] 0.03261595 0.07657534 0.181299624 0.126115526 0.17897462
#> [10,] 0.11425339 0.13686475 0.086262769 0.164013279 0.02340292
#>
## Sanity check:
stopifnot(all.equal(colSums(a / t), matrix(1, nrow=5, ncol=6)))