R: применение функции над матрицей и сохранение размеров матрицы
Итак, я хочу применить функцию по матрице в R. Это действительно работает интуитивно для простых функций:
> (function(x)x*x)(matrix(1:10, nrow=2))
[,1] [,2] [,3] [,4] [,5]
[1,] 1 9 25 49 81
[2,] 4 16 36 64 100
... но ясно, что я не понимаю все его работы:
> m = (matrix(1:10, nrow=2))
> (function(x) if (x %% 3 == 0) { return(NA) } else { return(x+1) })(m)
[,1] [,2] [,3] [,4] [,5]
[1,] 2 4 6 8 10
[2,] 3 5 7 9 11
Warning message:
In if (x == 3) { :
the condition has length > 1 and only the first element will be used
Я прочитал об этом и узнал об Vectorize и sapply, которые оба казались отличными и похожими на то, что я хотел, за исключением того, что оба они преобразуют мою матрицу в список:
> y = (function(x) if (x %% 3 == 0) { return(NA) } else { return(x+1) })
> sapply(m, y)
[1] 2 3 NA 5 6 NA 8 9 NA 11
> Vectorize(y)(m)
[1] 2 3 NA 5 6 NA 8 9 NA 11
... в то время как я хотел бы сохранить его в матрице с текущими размерами. Как я могу это сделать? Спасибо!
Ответы
Ответ 1
@Джошуа Ульрих (и Дасон) имеет отличный ответ. И сделать это напрямую без функции y
- лучшее решение. Но если вам действительно нужно вызвать функцию, вы можете сделать ее быстрее, используя vapply
. Он создает вектор без измерений (как sapply
, но быстрее), но затем вы можете добавить их обратно с помощью structure
:
# Your function (optimized)
y = function(x) if (x %% 3) x+1 else NA
m <- matrix(1:1e6,1e3)
system.time( r1 <- apply(m,1:2,y) ) # 4.89 secs
system.time( r2 <- structure(sapply(m, y), dim=dim(m)) ) # 2.89 secs
system.time( r3 <- structure(vapply(m, y, numeric(1)), dim=dim(m)) ) # 1.66 secs
identical(r1, r2) # TRUE
identical(r1, r3) # TRUE
... Как вы можете видеть, подход vapply
примерно в 3 раза быстрее, чем apply
... И причина vapply
выше, чем sapply
заключается в том, что sapply
должен анализировать результат на цифру что его можно упростить для числового вектора. С помощью vapply
вы указали тип результата (numeric(1)
), поэтому он не должен угадывать...
UPDATE Я выяснил другой (более короткий) способ сохранения структуры матрицы:
m <- matrix(1:10, nrow=2)
m[] <- vapply(m, y, numeric(1))
Вы просто назначаете новые значения объекту с помощью m[] <-
. Затем сохраняются все остальные атрибуты (например, dim
, dimnames
, class
и т.д.).
Ответ 2
Один из способов - использовать apply
для обеих строк и столбцов:
apply(m,1:2,y)
[,1] [,2] [,3] [,4] [,5]
[1,] 2 NA 6 8 NA
[2,] 3 5 NA 9 11
Вы также можете сделать это с помощью подписи, потому что ==
уже векторизован:
m[m %% 3 == 0] <- NA
m <- m+1
m
[,1] [,2] [,3] [,4] [,5]
[1,] 2 NA 6 8 NA
[2,] 3 5 NA 9 11
Ответ 3
Для этого конкретного примера вы можете просто сделать что-то вроде этого
> # Create some fake data
> mat <- matrix(1:16, 4, 4)
> # Set all elements divisible by 3 to NA
> mat[mat %% 3 == 0] <- NA
> # Add 1 to all non NA elements
> mat <- mat + 1
> mat
[,1] [,2] [,3] [,4]
[1,] 2 6 NA 14
[2,] 3 NA 11 15
[3,] NA 8 12 NA
[4,] 5 9 NA 17
Ответ 4
Там небольшое усовершенствование решения Dason и Josh с использованием ifelse
.
mat <- matrix(1:16, 4, 4)
ifelse(mat %% 3 == 0, NA, mat + 1)
[,1] [,2] [,3] [,4]
[1,] 2 6 NA 14
[2,] 3 NA 11 15
[3,] NA 8 12 NA
[4,] 5 9 NA 17