it-swarm.it

Come applicare la funzione sugli indici di ciascun elemento della matrice

Mi chiedo se esiste una funzione incorporata in R che applica una funzione a ciascun elemento della matrice (ovviamente, la funzione dovrebbe essere calcolata in base agli indici di matrice). L'equivalente sarebbe qualcosa del genere:

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

Se non esiste una tale funzione incorporata, qual è il modo migliore per inizializzare una matrice per contenere i valori ottenuti calcolando una funzione arbitraria che ha indici di matrici come parametri?

47
eold

Sospetto che tu voglia 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

Ciò produce una bella uscita compatta. ma è possibile che mapply sia utile in altre situazioni. È utile pensare a mapply come solo un altro modo per fare la stessa operazione che gli altri in questa pagina usano Vectorize per. mapply è più generale a causa dell'incapacità Vectorize di utilizzare le funzioni "primitive".

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

Probabilmente non intendevi davvero fornire alla funzione quali sarebbero state le funzioni row () e col (): questo produce una matrice di 15 (in qualche modo ridondanti) matrici 3 x 5:

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

L'approccio più semplice è solo per usare un f() che può essere applicato direttamente agli elementi della matrice. Ad esempio, utilizzando la matrice m dalla risposta di @ adamleerich

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

Non vi è alcun motivo per utilizzare apply() nel caso dell'esempio as.character(). Possiamo invece operare sugli elementi di m come se fosse un vettore (it really è uno) e sostituire in-place :

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

La prima parte di quel blocco è la chiave qui. m[] forza gli elementi di m a essere sostituiti dall'output da as.character(), piuttosto che sovrascrivere m con un vettore di caratteri.

In modo che is la soluzione generale per applicare una funzione a ciascun elemento di una matrice.

Se si ha davvero bisogno di usare un f() che funzioni su indici di righe e colonne, allora scriverei un f() usando row() e 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

o uno che usa outer() come gli altri hanno mostrato. Se f() non è vettorizzato, ripenserò la mia strategia il più a lungo possibile in quanto i) probabilmente è un modo per scrivere una versione veramente vettoriale, e ii) una funzione che non è vettorizzata non sta andando in scala molto bene.

18
Gavin Simpson

Non ci hai detto che tipo di funzione vuoi applicare a ciascun elemento, ma penso che l'unica ragione per cui gli esempi nelle altre risposte funzionano è perché le funzioni sono già vettorizzate. Se vuoi veramente applicare una funzione a ciascun elemento, outer non ti darà nulla di speciale che la funzione non ti ha già dato. Noterai che le risposte non hanno nemmeno passato una matrice a outer!

Che ne dici di seguire il commento di @ Chase e utilizzare apply

Ad esempio, ho la matrice

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

Se voglio trasformarlo in una matrice di caratteri, elemento per elemento (solo come esempio), potrei farlo

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

Certo, as.character è già vettorizzato, ma la mia funzione speciale my.special.function non lo è. Prende solo un argomento, un elemento. Non esiste un modo semplice per ottenere outer per lavorare con esso. Ma questo funziona

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

Potresti pensare a 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

Questa è chiaramente una funzione di esempio abbastanza banale, ma puoi anche fornirne una personalizzata. Vedi ?outer.

Modificare

Contrariamente al commento qui sotto, puoi anche usare outer con funzioni non-vettorizzate per .... vettorializzare!

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

Questo non risponde esattamente alla tua domanda, ma l'ho trovato mentre cercavo di capire una domanda simile, quindi ti mostrerò qualcosa.

Supponiamo che tu abbia una funzione che vuoi applicare a ciascun elemento di una matrice che richiede solo una parte. 

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

E dì che hai una matrice X, 

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

allora fai questo:

res=mydouble(X)

Quindi eseguirà un doppio elemento-saggio di ogni valore.

Tuttavia, se esegui la logica nella funzione come sotto, riceverai un avviso che non è parametrizzato e non si comporta come ti aspetti.

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

Ma se usi la funzione apply () puoi usarla.

Per esempio:

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

Quindi è grandioso, vero? Beh, si rompe se hai una funzione con due o più parm. Per esempio, hai questo:

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

In questo caso, si utilizza la funzione apply (). Tuttavia, perderà la matrice ma i risultati verranno calcolati correttamente. Possono essere riformati se sei così inclinato.

> 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