Как применить функцию к индексам каждого элемента матрицы - PullRequest
48 голосов
/ 13 сентября 2011

Мне интересно, есть ли встроенная функция в R, которая применяет функцию к каждому элементу матрицы (конечно, функция должна быть вычислена на основе индексов матрицы).Эквивалент будет выглядеть примерно так:

matrix_apply <- function(m, f) {
  m2 <- m
  for (r in seq(nrow(m2)))
    for (c in seq(ncol(m2)))
      m2[[r, c]] <- f(r, c)
  return(m2)
}

Если такой встроенной функции нет, каков наилучший способ инициализации матрицы для хранения значений, полученных путем вычисления произвольной функции с индексами матрицы какпараметры?

Ответы [ 5 ]

28 голосов
/ 13 сентября 2011

Я подозреваю, что вы хотите outer:

> mat <- matrix(NA, nrow=5, ncol=3)

> outer(1:nrow(mat), 1:ncol(mat) , FUN="*")
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    2    4    6
[3,]    3    6    9
[4,]    4    8   12
[5,]    5   10   15

> outer(1:nrow(mat), 1:ncol(mat) , FUN=function(r,c) log(r+c) )
          [,1]     [,2]     [,3]
[1,] 0.6931472 1.098612 1.386294
[2,] 1.0986123 1.386294 1.609438
[3,] 1.3862944 1.609438 1.791759
[4,] 1.6094379 1.791759 1.945910
[5,] 1.7917595 1.945910 2.079442

Это дает хороший компактный вывод.но возможно, что mapply будет полезен в других ситуациях.Полезно думать о mapply как о еще одном способе выполнения той же операции, для которой другие пользователи на этой странице используют Vectorize.mapply является более общим из-за невозможности Vectorize использовать «примитивные» функции.

data.frame(mrow=c(row(mat)),   # straightens out the arguments
           mcol=c(col(mat)), 
           m.f.res= mapply(function(r,c) log(r+c), row(mat), col(mat)  ) )
#   mrow mcol   m.f.res
1     1    1 0.6931472
2     2    1 1.0986123
3     3    1 1.3862944
4     4    1 1.6094379
5     5    1 1.7917595
6     1    2 1.0986123
7     2    2 1.3862944
8     3    2 1.6094379
9     4    2 1.7917595
10    5    2 1.9459101
11    1    3 1.3862944
12    2    3 1.6094379
13    3    3 1.7917595
14    4    3 1.9459101
15    5    3 2.0794415

Вы, вероятно, на самом деле не хотели предоставлять функции то, что row () и col ()функции вернулись бы: это создает массив из 15 (несколько избыточных) матриц 3 x 5:

> outer(row(mat), col(mat) , FUN=function(r,c) log(r+c) )
18 голосов
/ 13 сентября 2011

Самым простым подходом является просто использование f(), которое может быть применено непосредственно к элементам матрицы.Например, используя матрицу m из ответа @ adamleerich

m <- matrix(c(1,2,3,4,5,6,7,8), nrow = 2)

Нет смысла использовать apply() в случае примера as.character().Вместо этого мы можем оперировать элементами m, как если бы это был вектор (это на самом деле - это один) и заменить на месте :

> m[] <- as.character(m)
> m
     [,1] [,2] [,3] [,4]
[1,] "1"  "3"  "5"  "7" 
[2,] "2"  "4"  "6"  "8"

Первая часть этого блока является ключом здесь.m[] заставляет элементы m заменяться выводом из as.character() вместо перезаписи m вектором символов.

Так что является общимрешение применить функцию к каждому элементу матрицы.

Если действительно нужно использовать f(), который работает с индексами строк и столбцов, тогда я бы написал f(), используя row() и col():

> m <- matrix(c(1,2,3,4,5,6,7,8), nrow = 2)
> row(m)
     [,1] [,2] [,3] [,4]
[1,]    1    1    1    1
[2,]    2    2    2    2
> col(m)
     [,1] [,2] [,3] [,4]
[1,]    1    2    3    4
[2,]    1    2    3    4
> row(m) * col(m) ## `*`(row(m), col(m)) to see this is just f()
     [,1] [,2] [,3] [,4]
[1,]    1    2    3    4
[2,]    2    4    6    8

или тот, который использует outer(), как показали другие.Если f() не векторизовано, то я бы переосмыслил свою стратегию настолько, насколько это возможно, поскольку там я), вероятно, это способ написать действительно векторизованную версию, и ii) функция, которая не векторизована, не собираетсяочень хорошо масштабируется.

13 голосов
/ 13 сентября 2011

Вы не сказали нам, какую функцию вы хотите применить к каждому элементу, но я думаю, что единственная причина, по которой примеры в других ответах работают, заключается в том, что функции уже векторизованы. Если вы действительно хотите применить функцию к каждому элементу, outer не даст вам ничего особенного, что функция еще не дала вам. Вы заметите, что ответы даже не прошли матрицу на outer!

Как насчет следования комментарию @ Chase и использования apply.

Например, у меня есть матрица

m <- matrix(c(1,2,3,4,5,6,7,8), nrow = 2)

Если я хочу превратить ее в матрицу символов, элемент за элементом (просто в качестве примера), я мог бы сделать это

apply(m, c(1,2), as.character)

Конечно, as.character уже векторизован, но моя специальная функция my.special.function - нет. Требуется только один аргумент, элемент. Нет простого способа заставить outer работать с ним. Но это работает

apply(m, c(1,2), my.special.function)
8 голосов
/ 13 сентября 2011

Возможно, вы думаете о outer:

rows <- 1:10
cols <- 1:10

outer(rows,cols,"+")

      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
 [1,]    2    3    4    5    6    7    8    9   10    11
 [2,]    3    4    5    6    7    8    9   10   11    12
 [3,]    4    5    6    7    8    9   10   11   12    13
 [4,]    5    6    7    8    9   10   11   12   13    14
 [5,]    6    7    8    9   10   11   12   13   14    15
 [6,]    7    8    9   10   11   12   13   14   15    16
 [7,]    8    9   10   11   12   13   14   15   16    17
 [8,]    9   10   11   12   13   14   15   16   17    18
 [9,]   10   11   12   13   14   15   16   17   18    19
[10,]   11   12   13   14   15   16   17   18   19    20

Это явно довольно тривиальная примерная функция, но вы также можете предоставить и свою собственную. Смотри ?outer.

Редактировать

Вопреки комментарию ниже, вы также можете использовать outer с не векторизованными функциями путем .... векторизации их!

m <- matrix(1:16,4,4)

#A non-vectorized function 
myFun <- function(x,y,M){
     M[x,y] + (x*y)
}

#Oh noes! 
outer(1:4,1:4,myFun,m)
Error in dim(robj) <- c(dX, dY) : 
  dims [product 16] do not match the length of object [256]

#Oh ho! Vectorize()! 
myVecFun <- Vectorize(myFun,vectorize.args = c('x','y'))

#Voila! 
outer(1:4,1:4,myVecFun,m)
     [,1] [,2] [,3] [,4]
[1,]    2    7   12   17
[2,]    4   10   16   22
[3,]    6   13   20   27
[4,]    8   16   24   32
0 голосов
/ 23 ноября 2016

Это не дает точного ответа на ваш вопрос, но я нашел его, пытаясь выяснить аналогичный вопрос, поэтому я покажу вам кое-что.

Скажем, у вас есть функция, которую вы хотите применить к каждому элементу матрицы, которая требует только одну часть.

mydouble <- function(x) {
   return(x+x)
}

И скажем, у вас есть матрица X,

> x=c(1,-2,-3,4)
> X=matrix(x,2,2)
> X
     [,1] [,2]
[1,]    1   -3
[2,]   -2    4

тогда вы делаете это:

res=mydouble(X)

Тогда он будет делать поэлементное удвоение каждого значения.

Однако, если вы выполните логику в функции, как показано ниже, вы получите предупреждение о том, что она не параметризована и ведет себя не так, как вы ожидаете.

myabs <- function(x) {
  if (x<0) {
      return (-x)
  } else {
      return (x)
  }
}

> myabs(X)
     [,1] [,2]
[1,]    1   -3
[2,]   -2    4
Warning message:
In if (x < 0) { :
  the condition has length > 1 and only the first element will be used

Но если вы используете функцию apply (), вы можете использовать ее.

Например:

> apply(X,c(1,2),myabs)
     [,1] [,2]
[1,]    1    3
[2,]    2    4

Так что это здорово, верно? Ну, это ломается, если у вас есть функция с двумя или более параметрами. Скажем, например, у вас есть это:

mymath <- function(x,y) {
    if(x<0) {
        return(-x*y)
    } else {
        return(x*y)
    }
}

В этом случае вы используете функцию apply (). Тем не менее, он потеряет матрицу, но результаты рассчитаны правильно. Они могут быть исправлены, если вы так склонны.

> mapply(mymath,X,X)
[1]  1 -4 -9 16
> mapply(mymath,X,2)
[1] 2 4 6 8
> matrix(mapply(mymath,X,2),c(2,2))
     [,1] [,2]
[1,]    2    6
[2,]    4    8
...