I've discovered a surprising behaviour by apply
that I wonder if anyone can explain. Lets take a simple matrix:
> (m = matrix(1:8,ncol=4))
[,1] [,2] [,3] [,4]
[1,] 1 3 5 7
[2,] 2 4 6 8
We can flip it vertically thus:
> apply(m, MARGIN=2, rev)
[,1] [,2] [,3] [,4]
[1,] 2 4 6 8
[2,] 1 3 5 7
This applies the rev()
vector reversal function iteratively to each column. But when we try to apply rev by row we get:
> apply(m, MARGIN=1, rev)
[,1] [,2]
[1,] 7 8
[2,] 5 6
[3,] 3 4
[4,] 1 2
.. a 90 degree anti-clockwise rotation! Apply delivers the same result using FUN=function(v) {v[length(v):1]}
so it is definitely not rev's fault.
Any explanation for this?
This is because apply
returns a matrix that is defined column-wise, and you're iterating over the rows.
The first application of apply
presents each row, which is then a column in the result.
Presenting the function print
shows what's being passed to rev
at each iteration:
x <- apply(m, 1, print)
[1] 1 3 5 7
[1] 2 4 6 8
That is, each call to print is passed a vector. Two calls, and c(1,3,5,7)
and c(2,4,6,8)
are being passed to the function.
Reversing these gives c(7,5,3,1)
and c(8,6,4,2)
, then these are used as the columns of the return matrix, giving the result that you see.
The documentation states that
If each call to FUN returns a vector of length n, then apply returns an array of dimension c(n, dim(X)[MARGIN]) if n > 1.
From that perspective, this behaviour is not a bug whatsoever, that's how it intended to work.
One may wonder why this is chosen to be a default setting, instead of preserving the structure of the original matrix. Consider the following example:
> apply(m, 1, quantile)
[,1] [,2]
0% 1.0 2.0
25% 2.5 3.5
50% 4.0 5.0
75% 5.5 6.5
100% 7.0 8.0
> apply(m, 2, quantile)
[,1] [,2] [,3] [,4]
0% 1.00 3.00 5.00 7.00
25% 1.25 3.25 5.25 7.25
50% 1.50 3.50 5.50 7.50
75% 1.75 3.75 5.75 7.75
100% 2.00 4.00 6.00 8.00
> all(rownames(apply(m, 2, quantile)) == rownames(apply(m, 1, quantile)))
[1] TRUE
Consistent? Indeed, why would we expect anything else?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With