Come iterare ogni elemento in una matrice n-dimensionale in MATLAB?


87

Ho un problema. Ho bisogno di iterare attraverso ogni elemento in una matrice n-dimensionale in MATLAB. Il problema è che non so come farlo per un numero arbitrario di dimensioni. So di poterlo dire

for i = 1:size(m,1)
    for j = 1:size(m,2)
        for k = 1:size(m,3)

e così via, ma c'è un modo per farlo per un numero arbitrario di dimensioni?


13
Nota sulla terminologia Matlab: Matlab ha un numero limitato di tipi di dati principali. I più importanti sono: struct, matrix e cell array. Quando si fa riferimento a parti di una matrice, è comune utilizzare il termine "elemento" e riservare il termine "cella" per fare riferimento a parti di una matrice di celle. Le matrici e le matrici di celle presentano numerose differenze sintattiche e semantiche, anche se entrambe sono strutture di dati a N dimensioni.
Mr Fooz

3
Posso chiederti per cosa hai bisogno dell'iterazione? Forse c'è un modo "vettorializzato" per farlo invece ...
Hosam Aly

Risposte:


92

È possibile utilizzare l'indicizzazione lineare per accedere a ogni elemento.

for idx = 1:numel(array)
    element = array(idx)
    ....
end

Questo è utile se non hai bisogno di sapere in cosa ti trovi i, j, k. Tuttavia, se non hai bisogno di sapere a quale indice ti trovi, probabilmente è meglio usare arrayfun ()


1
Inoltre, se si voleva recuperare gli indici per qualche ragione, è ancora potrebbe utilizzare questi due semplici comandi: I = cell(1, ndims(array)); [I{:}] = ind2sub(size(array),idx);.
knedlsepp

34

L'idea di un indice lineare per array in matlab è importante. Un array in MATLAB è in realtà solo un vettore di elementi, appeso in memoria. MATLAB ti consente di utilizzare un indice di riga e di colonna o un singolo indice lineare. Per esempio,

A = magic(3)
A =
     8     1     6
     3     5     7
     4     9     2

A(2,3)
ans =
     7

A(8)
ans =
     7

Possiamo vedere l'ordine in cui gli elementi sono archiviati in memoria svolgendo l'array in un vettore.

A(:)
ans =
     8
     3
     4
     1
     5
     9
     6
     7
     2

Come puoi vedere, l'ottavo elemento è il numero 7. Infatti, la funzione find restituisce i suoi risultati come un indice lineare.

find(A>6)
ans =
     1
     6
     8

Il risultato è che possiamo accedere a ogni elemento a turno di un array nd generale utilizzando un singolo ciclo. Ad esempio, se volessimo quadrare gli elementi di A (sì, so che ci sono modi migliori per farlo), si potrebbe fare questo:

B = zeros(size(A));
for i = 1:numel(A)
  B(i) = A(i).^2;
end

B
B =
    64     1    36
     9    25    49
    16    81     4

Ci sono molte circostanze in cui l'indice lineare è più utile. La conversione tra l'indice lineare e gli indici bidimensionali (o superiori) viene eseguita con le funzioni sub2ind e ind2sub.

L'indice lineare si applica in generale a qualsiasi array in matlab. Quindi puoi usarlo su strutture, array di celle, ecc. L'unico problema con l'indice lineare è quando diventano troppo grandi. MATLAB utilizza un numero intero a 32 bit per memorizzare questi indici. Quindi, se il tuo array ha più di un totale di 2 ^ 32 elementi, l'indice lineare fallirà. In realtà è un problema solo se si utilizzano spesso matrici sparse, quando occasionalmente ciò causerà un problema. (Anche se non utilizzo una versione MATLAB a 64 bit, credo che il problema sia stato risolto per quei fortunati che lo fanno.)


L'indicizzazione in MATLAB a 64 bit consente infatti correttamente gli indici a 64 bit. Ad esempio: x = ones(1,2^33,'uint8'); x(2^33)funziona come previsto.
Edric

@Edric - Naturalmente, questo è un comportamento che sicuramente sarebbe cambiato negli anni (e in molti rilasci) da quando ho fatto quella dichiarazione. Grazie per il controllo comunque.

:) Non mi ero reso conto di quanti anni avesse la risposta fino a dopo aver commentato - la domanda è appena apparsa nel mio feed RSS, e non mi sono nemmeno accorto di aver risposto anch'io!
Edric

15

Come sottolineato in alcune altre risposte, è possibile iterare su tutti gli elementi in una matrice A(di qualsiasi dimensione) utilizzando un indice lineare da 1a numel(A)in un unico ciclo for. Ci sono anche un paio di funzioni che puoi usare: arrayfune cellfun.

Supponiamo innanzitutto di avere una funzione da applicare a ciascun elemento di A(chiamato my_func). Per prima cosa crei un handle di funzione per questa funzione:

fcn = @my_func;

Se Aè una matrice (di tipo double, single, ecc.) Di dimensione arbitraria, puoi utilizzare arrayfunper applicare my_funca ciascun elemento:

outArgs = arrayfun(fcn, A);

Se Aè un array di celle di dimensione arbitraria, puoi utilizzare cellfunper applicare my_funca ciascuna cella:

outArgs = cellfun(fcn, A);

La funzione my_funcdeve accettare Acome input. Se ci sono degli output da my_func, questi vengono inseriti outArgs, che avrà la stessa dimensione / dimensione di A.

Un avvertimento sugli output ... se my_funcrestituisce output di dimensioni e tipi diversi quando opera su elementi diversi di A, allora outArgsdovrà essere trasformato in un array di celle. Questo viene fatto chiamando arrayfuno cellfuncon una coppia parametro / valore aggiuntiva:

outArgs = arrayfun(fcn, A, 'UniformOutput', false);
outArgs = cellfun(fcn, A, 'UniformOutput', false);

13

Un altro trucco è usare ind2sube sub2ind. Insieme a numele size, questo può farti fare cose come le seguenti, che crea un array N-dimensionale, e quindi imposta tutti gli elementi sulla "diagonale" su 1.

d = zeros( 3, 4, 5, 6 ); % Let's pretend this is a user input
nel = numel( d );
sz = size( d );
szargs = cell( 1, ndims( d ) ); % We'll use this with ind2sub in the loop
for ii=1:nel
    [ szargs{:} ] = ind2sub( sz, ii ); % Convert linear index back to subscripts
    if all( [szargs{2:end}] == szargs{1} ) % On the diagonal?
        d( ii ) = 1;
    end
end

+1 per mostrare un buon esempio di come MATLAB rompe la digitazione a papera.
Phillip Cloud

1

Potresti fare in modo che una funzione ricorsiva faccia il lavoro

  • Permettere L = size(M)
  • Permettere idx = zeros(L,1)
  • Prendi length(L)come profondità massima
  • Ciclo continuo for idx(depth) = 1:L(depth)
  • Se la tua profondità è length(L), esegui l'operazione sull'elemento, altrimenti chiama di nuovo la funzione condepth+1

Non veloce come i metodi vettorializzati se vuoi controllare tutti i punti, ma se non hai bisogno di valutarne la maggior parte può farti risparmiare tempo.


1

queste soluzioni sono più veloci (circa l'11%) rispetto all'utilizzo numel;)

for idx = reshape(array,1,[]),
     element = element + idx;
end

o

for idx = array(:)',
    element = element + idx;
end

UPD. tnx @rayryeng per l'errore rilevato nell'ultima risposta


Disclaimer

Le informazioni sui tempi a cui fa riferimento questo post non sono corrette e imprecise a causa di un errore di battitura fondamentale che è stato fatto (vedi il flusso dei commenti di seguito e la cronologia delle modifiche - in particolare guarda la prima versione di questa risposta). Caveat Emptor .


1
1 : array(:)è equivalente a 1 : array(1). Questo non scorre attraverso tutti gli elementi, motivo per cui i tempi di esecuzione sono rapidi. Inoltre, randgenera numeri in virgola mobile , e così facendo 1 : array(:)produrrebbe un array vuoto poiché la tua istruzione sta cercando di trovare un vettore crescente con il suo valore iniziale come 1 con un valore finale come numero in virgola mobile con un intervallo [0,1)di 1 escluso in aumento passi di 1. Non esiste un vettore possibile di questo tipo, che risulta in un vettore vuoto. Il tuo forciclo non viene eseguito e quindi la tua affermazione è falsa. -1 voto. scusa.
rayryeng

@rayryeng non hai ragione. array (:) non è equivalente a 1: array (1). Gli piace reshape(...).
mathcow

@rayryeng matlab r2013a + linux - funziona! ;) Ho appena eseguito anche quel codice
mathcow

Digita 1 : array(:)nel prompt dei comandi dopo aver creato array . Ottieni una matrice vuota? se sì, il tuo codice non funziona. Lascio il mio voto perché stai fornendo false informazioni.
rayryeng

@rayryeng sto capendo! sì, hai ragione, scusa per la stupida disputa
mathcow

-1

Se guardi più a fondo negli altri usi di sizete, puoi vedere che puoi effettivamente ottenere un vettore delle dimensioni di ciascuna dimensione. Questo link mostra la documentazione:

www.mathworks.com/access/helpdesk/help/techdoc/ref/size.html

Dopo aver ottenuto il vettore di dimensione, iterare su quel vettore. Qualcosa del genere (scusa la mia sintassi poiché non ho usato Matlab dal college):

d = size(m);
dims = ndims(m);
for dimNumber = 1:dims
   for i = 1:d[dimNumber]
      ...

Trasformalo in una sintassi legale per Matlab e penso che farebbe quello che vuoi.

Inoltre, dovresti essere in grado di eseguire l'indicizzazione lineare come descritto qui .


Non riesco a vedere come l'ordinamento dei cicli itererà su tutti gli elementi di una matrice. Ad esempio, se hai una matrice 3 per 4 (con 12 elementi), il tuo ciclo interno itererà solo 7 volte.
gnovice

dovrebbe iterare su ogni dimensione della matrice. Il ciclo esterno itera sulla dimensione, il ciclo interno sulla dimensione di quella dimensione. Almeno, questa è l'idea. Come affermano tutti gli altri, se tutto ciò che vuole è ogni cella, l'indicizzazione di linea è la migliore. Se vuole iterare su ogni dimensione, dovrà fare qualcosa di simile a questo.
Erich Mirabal

inoltre, grazie per la modifica. il mio collegamento era un po 'contorto e semplicemente non funzionava correttamente usando il solito modo di collegamento. Inoltre, per espandere la mia affermazione: avrebbe comunque dovuto fare molti altri tracciamenti dell'indice (usando come un contatore o qualcosa del genere). Penso che l'approccio tu o Andrew sarebbe più facile per quello che penso che stia cercando di fare.
Erich Mirabal

-1

Si desidera simulare cicli for nidificati.

L'iterazione attraverso la matrice n-dimensionale può essere vista come un aumento del numero di n cifre.

Ad ogni dimmensione abbiamo tante cifre quanta la lunghezza della dimmensione.

Esempio:

Supponiamo di avere array (matrice)

int[][][] T=new int[3][4][5];

in "per notazione" abbiamo:

for(int x=0;x<3;x++)
   for(int y=0;y<4;y++)
       for(int z=0;z<5;z++)
          T[x][y][z]=...

per simulare questo dovresti usare la "notazione del numero a n cifre"

Abbiamo un numero di 3 cifre, con 3 cifre per la prima, 4 per la seconda e cinque per la terza cifra

Dobbiamo aumentare il numero, così avremmo la sequenza

0 0 0
0 0 1
0 0 2    
0 0 3
0 0 4
0 1 0
0 1 1
0 1 2
0 1 3
0 1 4
0 2 0
0 2 1
0 2 2
0 2 3
0 2 4
0 3 0
0 3 1
0 3 2
0 3 3
0 3 4
and so on

Quindi puoi scrivere il codice per aumentare tale numero di n cifre. Puoi farlo in modo tale da poter iniziare con qualsiasi valore del numero e aumentare / diminuire le cifre di qualsiasi numero. In questo modo è possibile simulare cicli for annidati che iniziano da qualche parte nella tabella e non finiscono alla fine.

Tuttavia, non è un compito facile. Purtroppo non posso fare a meno della notazione Matlab.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.