web-dev-qa-db-fra.com

Comment appliquer une fonction sur les indices de chaque élément de la matrice

Je me demande s’il existe une fonction intégrée dans R qui applique une fonction à chaque élément de la matrice (bien entendu, la fonction devrait être calculée à partir des indices de la matrice). L'équivalent serait quelque chose comme ceci:

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)
}

En l'absence d'une telle fonction intégrée, quel est le meilleur moyen d'initialiser une matrice pour qu'elle contienne des valeurs obtenues en calculant une fonction arbitraire ayant pour paramètres des indices de matrice?

47
eold

Je pense que vous voulez 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

Cela donne une belle sortie compacte. mais il est possible que mapply soit utile dans d’autres situations. Il est utile de penser à mapply comme à un autre moyen d'effectuer la même opération que celle utilisée par les autres utilisateurs de cette page sur Vectorize. mapply est plus général en raison de l'incapacité Vectorize à utiliser des fonctions "primitives".

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

Vous n'avez probablement pas réellement voulu dire de fournir à la fonction ce que les fonctions row () et col () auraient renvoyé: Ceci produit un tableau de 15 matrices 3 x 5 (quelque peu redondantes):

> outer(row(mat), col(mat) , FUN=function(r,c) log(r+c) )
27
42-

L’approche la plus simple est consiste simplement à utiliser une f() pouvant être appliquée directement aux éléments de la matrice. Par exemple, en utilisant la matrice m de la réponse de @ adamleerich

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

Il n'y a aucune raison d'utiliser apply() dans le cas de l'exemple as.character(). Au lieu de cela, nous pouvons utiliser les éléments de m comme s’il s’agissait d’un vecteur (it vraiment _ en est un) et remplacer in-situ:

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

La première partie de ce bloc est la clé ici. m[] force les éléments de m à être remplacés par la sortie de as.character(), plutôt que d'écraser m par un vecteur de caractères.

Alors que est la solution générale pour appliquer une fonction à chaque élément d’une matrice.

S'il faut vraiment utiliser une f() qui fonctionne avec les index de ligne et de colonne, j'écrirai une f() à l'aide de row() et de 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

ou ceux qui utilisent outer() comme les autres l'ont montré. Si f() n'est pas vectorisé, alors je repenserais ma stratégie autant que possible car i) est probablement un moyen d'écrire une version véritablement vectorisée, et ii) une fonction qui n'est pas vectorisée ne va pas très évoluer bien.

18
Gavin Simpson

Vous ne nous avez pas indiqué le type de fonction que vous souhaitez appliquer à chaque élément, mais je pense que la seule raison pour laquelle les exemples des autres réponses fonctionnent est que les fonctions sont déjà vectorisées. Si vous voulez vraiment appliquer une fonction à chaque élément, outer ne vous donnera rien de spécial que la fonction ne vous a pas déjà donné. Vous remarquerez que les réponses ne passent même pas une matrice à outer!

Pourquoi ne pas suivre le commentaire de @ Chase et utiliser apply

Par exemple, j'ai la matrice

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

Si je veux le transformer en matrice de caractères, élément par élément (à titre d'exemple), je pourrais le faire.

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

Bien sûr, as.character est déjà vectorisé, mais ma fonction spéciale my.special.function ne l’est pas. Cela ne prend qu'un argument, un élément. Il n’existe aucun moyen simple d’obtenir outer avec. Mais ça marche

apply(m, c(1,2), my.special.function)
13
adamleerich

Vous pensez peut-être à 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

C'est clairement un exemple de fonction assez trivial, mais vous pouvez également fournir votre propre fonction personnalisée. Voir ?outer.

Modifier

Contrairement au commentaire ci-dessous, vous pouvez également utiliser outer avec des fonctions non vectorisées en .... les vectorisant!

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
8
joran

Cela ne répond pas exactement à votre question, mais je l’ai trouvée en essayant de trouver une question similaire, alors je vais vous montrer quelque chose.

Supposons que vous ayez une fonction que vous souhaitez appliquer à chaque élément d'une matrice qui ne nécessite qu'une partie. 

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

Et dis que tu as une matrice X, 

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

alors vous faites ceci:

res=mydouble(X)

Ensuite, il fera un double élément par élément de chaque valeur.

Cependant, si vous utilisez la logique dans la fonction comme ci-dessous, vous recevrez un avertissement indiquant qu'elle n'est pas paramétrée et qu'elle ne se comporte pas comme prévu.

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

Mais si vous utilisez la fonction apply (), vous pouvez l'utiliser.

Par exemple:

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

Donc c'est génial, non? Eh bien, ça tombe en panne si vous avez une fonction avec deux parmes ou plus. Disons par exemple que vous avez ceci:

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

Dans ce cas, vous utilisez la fonction apply (). Cependant, la matrice sera perdue mais les résultats sont calculés correctement. Ils peuvent être réformés si vous le souhaitez.

> 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
0
netskink