Mark R
Mark R

Reputation: 1021

apply() vs. sweep() in R

I was writing up notes to compare apply() and sweep() and discovered the following strange differences. In order the generate the same result, sweep() needs MARGIN = 1 while apply wants MARGIN = 2. Also the argument specifying the matrix is uppercase X in apply() but lowercase in sweep().

my.matrix <- matrix(seq(1,9,1), nrow=3)
row.sums <- rowSums(my.matrix)
apply.matrix <- apply(X = my.matrix, MARGIN = 2, FUN = function (x) x/row.sums)
sweep.matrix <- sweep(x = my.matrix, MARGIN = 1, STATS = rowSums(my.matrix), FUN="/")
apply.matrix - sweep.matrix ##yup same matrix

Isn't sweep() an "apply-type" function? Is this just another R quirk or have I lost my mind?

Upvotes: 4

Views: 3542

Answers (2)

profPlum
profPlum

Reputation: 498

I'd like to disaggree with Weihuang Wong.

Let's illustrate it with a simpler example, (using a non-square matrix as he suggests):

(A = matrix(1:12, 3)) # Non-Square Matrix
(B=sweep(A, MARGIN=1, 1:3, FUN='/')) # only difference is margin
(C=apply(A, MARGIN=2, 1:3, FUN='/')) # only difference is margin
all.equal(B, C) # they're equal, but margins are opposite

If you were to try the transpose and same margins he suggested:

(A = matrix(1:12, 3)) # Non-Square Matrix
(B=sweep(A, MARGIN=1, 1:3, FUN='/'))
(C=t(apply(A, MARGIN=1, 1:3, FUN='/'))) # warning saying 'shapes don't match'
all.equal(B, C) # not equal, transposing doesn't work

I said basically the same thing here: An intuitive way to understand MARGIN in sweep and apply

P.S. I usually use sweep for broadcasting (e.g. like numpy), if you want consistent behavior you could probably just use apply like I demonstrated.

Upvotes: 0

Weihuang Wong
Weihuang Wong

Reputation: 13128

Note that for apply,

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’

In your example, MARGIN can (and should) be set to 1 in both cases; but the returned value from apply should be transposed. This is easiest to see if the original matrix is not square:

my.matrix <- matrix(seq(1,12,1), nrow=4)
apply.matrix <- t(apply(X = my.matrix, MARGIN = 1, FUN = function(x) x/sum(x)))
sweep.matrix <- sweep(x = my.matrix, MARGIN = 1, STATS = rowSums(my.matrix), FUN="/")
all.equal(apply.matrix, sweep.matrix)
# [1] TRUE

Also see this answer to Can you implement 'sweep' using apply in R?, which says very much the same thing.

Upvotes: 6

Related Questions