Wie iteriere ich in MATLAB durch jedes Element in einer n-dimensionalen Matrix?

86

Ich habe ein Problem. Ich muss jedes Element in einer n-dimensionalen Matrix in MATLAB durchlaufen. Das Problem ist, ich weiß nicht, wie ich das für eine beliebige Anzahl von Dimensionen machen soll. Ich weiß, ich kann sagen

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

und so weiter, aber gibt es eine Möglichkeit, dies für eine beliebige Anzahl von Dimensionen zu tun?

rlbond
quelle
13
Hinweis zur Matlab-Terminologie: Matlab verfügt über eine kleine Anzahl von Kerndatentypen. Die wichtigsten sind: Struktur, Matrix und Zellenarray. Wenn auf Teile einer Matrix verwiesen wird, wird häufig der Begriff "Element" verwendet, und der Begriff "Zelle" wird für Teile eines Zellenarrays reserviert. Zellarrays und Matrizen weisen zahlreiche syntaktische und semantische Unterschiede auf, obwohl beide N-dimensionale Datenstrukturen sind.
Herr Fooz
3
Darf ich fragen, wofür Sie die Iteration benötigen? Vielleicht gibt es stattdessen eine "vektorisierte" Möglichkeit ...
Hosam Aly

Antworten:

92

Sie können die lineare Indizierung verwenden, um auf jedes Element zuzugreifen.

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

Dies ist nützlich, wenn Sie nicht wissen müssen, an welchem ​​i, j, k Sie sich befinden. Wenn Sie jedoch nicht wissen müssen, an welchem ​​Index Sie sich befinden, ist es wahrscheinlich besser, arrayfun () zu verwenden.

Andrew
quelle
1
Wenn Sie die Indizes aus irgendeinem Grund wiederherstellen möchten, können Sie dennoch diese beiden einfachen Befehle verwenden : I = cell(1, ndims(array)); [I{:}] = ind2sub(size(array),idx);.
Knedlsepp
33

Die Idee eines linearen Index für Arrays in Matlab ist wichtig. Ein Array in MATLAB ist eigentlich nur ein Vektor von Elementen, die im Speicher aufgereiht sind. Mit MATLAB können Sie entweder einen Zeilen- und Spaltenindex oder einen einzelnen linearen Index verwenden. Beispielsweise,

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

A(2,3)
ans =
     7

A(8)
ans =
     7

Wir können die Reihenfolge sehen, in der die Elemente im Speicher gespeichert sind, indem wir das Array in einen Vektor abrollen.

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

Wie Sie sehen können, ist das 8. Element die Zahl 7. Tatsächlich gibt die Funktion find ihre Ergebnisse als linearen Index zurück.

find(A>6)
ans =
     1
     6
     8

Das Ergebnis ist, dass wir nacheinander auf jedes Element eines allgemeinen nd-Arrays mit einer einzigen Schleife zugreifen können. Wenn wir zum Beispiel die Elemente von A quadrieren wollten (ja, ich weiß, dass es bessere Möglichkeiten gibt, dies zu tun), könnte man dies tun:

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

Es gibt viele Umstände, unter denen der lineare Index nützlicher ist. Die Konvertierung zwischen dem linearen Index und zwei (oder höheren) dimensionalen Indizes erfolgt mit den Funktionen sub2ind und ind2sub.

Der lineare Index gilt im Allgemeinen für jedes Array in Matlab. Sie können es also für Strukturen, Zellenarrays usw. verwenden. Das einzige Problem mit dem linearen Index besteht darin, dass sie zu groß werden. MATLAB verwendet eine 32-Bit-Ganzzahl zum Speichern dieser Indizes. Wenn Ihr Array also mehr als 2 ^ 32 Elemente enthält, schlägt der lineare Index fehl. Es ist wirklich nur ein Problem, wenn Sie häufig spärliche Matrizen verwenden, wenn dies gelegentlich zu einem Problem führt. (Obwohl ich keine 64-Bit-MATLAB-Version verwende, glaube ich, dass das Problem für die glücklichen Menschen, die dies tun, gelöst wurde.)


quelle
Die Indizierung in 64-Bit-MATLAB erlaubt tatsächlich korrekt 64-Bit-Indizes. Zum Beispiel: x = ones(1,2^33,'uint8'); x(2^33)funktioniert wie erwartet.
Edric
@Edric - Natürlich ist dies ein Verhalten, das sich sicherlich in den Jahren (und vielen Veröffentlichungen) geändert hätte, seit ich diese Aussage gemacht habe. Vielen Dank für die Überprüfung.
:) Ich wusste nicht, wie alt die Antwort war, bis ich sie kommentierte - die Frage tauchte nur in meinem RSS-Feed auf und ich bemerkte nicht einmal, dass ich sie auch beantwortet hatte!
Edric
15

Wie in einigen anderen Antworten ausgeführt, können Sie alle Elemente in einer Matrix A(beliebiger Dimension) mithilfe eines linearen Index von 1bis numel(A)in einer einzelnen for-Schleife durchlaufen. Es gibt auch einige Funktionen, die Sie verwenden können: arrayfunund cellfun.

Nehmen wir zunächst an, Sie haben eine Funktion, die Sie auf jedes Element von A(aufgerufen my_func) anwenden möchten . Sie erstellen zunächst ein Funktionshandle für diese Funktion:

fcn = @my_func;

Wenn Aes sich um eine Matrix (vom Typ double, single usw.) mit beliebiger Dimension handelt, können Sie arrayfunsie my_funcauf jedes Element anwenden :

outArgs = arrayfun(fcn, A);

Wenn Aes sich um ein Zellenarray mit beliebiger Dimension handelt, können Sie cellfunFolgendes my_funcauf jede Zelle anwenden :

outArgs = cellfun(fcn, A);

Die Funktion my_funcmuss Aals Eingabe akzeptieren . Wenn es Ausgänge von gibt my_func, werden diese platziert outArgs, die dieselbe Größe / Dimension wie haben A.

Eine Einschränkung bei Ausgängen ... Wenn my_funcAusgänge unterschiedlicher Größe und Art zurückgegeben werden, wenn sie mit verschiedenen Elementen von arbeiten A, outArgsmuss dies zu einem Zellenarray gemacht werden. Dies erfolgt durch Aufrufen von entweder arrayfunoder cellfunmit einem zusätzlichen Parameter / Wert-Paar:

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

Ein weiterer Trick ist die Verwendung von ind2subund sub2ind. In Verbindung mit numelund sizekönnen Sie so etwas wie das Folgende tun, wodurch ein N-dimensionales Array erstellt wird und dann alle Elemente auf der "Diagonale" auf 1 gesetzt werden.

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
Edric
quelle
+1 für ein gutes Beispiel dafür, wie MATLAB das Tippen von Enten bricht.
Phillip Cloud
1

Sie könnten eine rekursive Funktion veranlassen, die Arbeit zu erledigen

  • Lassen L = size(M)
  • Lassen idx = zeros(L,1)
  • Nehmen Sie length(L)als maximale Tiefe
  • Schleife for idx(depth) = 1:L(depth)
  • Wenn Ihre Tiefe ist length(L), führen Sie die Elementoperation aus, andernfalls rufen Sie die Funktion erneut mit aufdepth+1

Nicht so schnell wie vektorisierte Methoden, wenn Sie alle Punkte überprüfen möchten, aber wenn Sie die meisten nicht bewerten müssen, kann dies eine ziemliche Zeitersparnis bedeuten.

Dennis Jaheruddin
quelle
1

Diese Lösungen sind schneller (ca. 11%) als die Verwendung numel;)

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

oder

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

UPD. tnx @rayryeng für erkannten Fehler in der letzten Antwort


Haftungsausschluss

Die Timing-Informationen, auf die in diesem Beitrag verwiesen wird, sind aufgrund eines grundlegenden Tippfehlers falsch und ungenau (siehe Kommentar-Stream unten sowie den Bearbeitungsverlauf - siehe insbesondere die erste Version dieser Antwort). Vorbehalt Emptor .

Mathcow
quelle
1
1 : array(:)ist äquivalent zu 1 : array(1). Dies durchläuft nicht alle Elemente, weshalb Ihre Laufzeiten schnell sind. Darüber hinaus werden Gleitkommazahlenrand generiert , und dies würde ein leeres Array erzeugen, wenn Ihre Anweisung versucht, einen ansteigenden Vektor mit dem Anfangswert 1 mit einem Endwert als Gleitkommazahl mit einem Bereich von ausschließlich 1 zu finden Schritte von 1. Es gibt keinen solchen möglichen Vektor, was zu einem leeren Vektor führt. Ihre Schleife wird nicht ausgeführt, daher ist Ihre Behauptung falsch. -1 Stimme. Es tut uns leid. 1 : array(:)[0,1)for
Rayryeng
@rayryeng du bist nicht richtig. array (:) entspricht nicht 1: array (1). Es mag reshape(...).
Mathcow
@rayryeng matlab r2013a + linux - es funktioniert! ;) Ich habe gerade auch diesen Code ausgeführt
Mathcow
Geben Sie auf 1 : array(:)Ihrer Eingabeaufforderung nach dem Erstellen array . Erhalten Sie eine leere Matrix? Wenn ja, funktioniert Ihr Code nicht. Ich verlasse meine Stimme, weil Sie falsche Angaben machen.
Rayryeng
@rayryeng Ich verstehe! Ja, Sie haben Recht, entschuldigen Sie den dummen Streit
Mathcow
-1

Wenn Sie sich die anderen Verwendungszwecke sizegenauer ansehen, können Sie feststellen, dass Sie tatsächlich einen Vektor mit der Größe jeder Dimension erhalten können. Dieser Link zeigt Ihnen die Dokumentation:

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

Nachdem Sie den Größenvektor erhalten haben, iterieren Sie über diesen Vektor. So etwas (entschuldigen Sie meine Syntax, da ich Matlab seit dem College nicht mehr verwendet habe):

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

Machen Sie dies zu einer tatsächlichen Matlab-legalen Syntax, und ich denke, es würde tun, was Sie wollen.

Außerdem sollten Sie in der Lage sein, die hier beschriebene lineare Indizierung durchzuführen .

Erich Mirabal
quelle
Ich kann nicht genau sehen, wie diese Reihenfolge der Schleifen über alle Elemente einer Matrix iteriert. Wenn Sie beispielsweise eine 3-mal-4-Matrix (mit 12 Elementen) haben, wird Ihre innere Schleife nur sieben Mal wiederholt.
Gnovice
Es sollte über jede Dimension der Matrix iterieren. Die äußere Schleife iteriert über die Dimension, die innere Schleife über die Größe dieser Dimension. Zumindest ist das die Idee. Wie alle anderen sagen, ist die Indizierung des Liners am besten, wenn er nur jede Zelle haben möchte. Wenn er über jede Dimension iterieren möchte, muss er etwas Ähnliches tun.
Erich Mirabal
auch danke für die bearbeitung. Mein Link war irgendwie verworren und würde auf die übliche Art und Weise nicht richtig funktionieren. Um meine Aussage zu erweitern: Er müsste noch viele andere Nachverfolgungen des Index vornehmen (unter Verwendung eines Zählers oder ähnlichem). Ich denke, Sie oder Andrews Ansatz wären einfacher für das, was er meiner Meinung nach versucht zu tun.
Erich Mirabal
-1

Sie möchten n-verschachtelte for-Schleifen simulieren.

Das Iterieren durch ein n-dimensionales Array kann als Erhöhen der n-stelligen Zahl angesehen werden.

Bei jeder Dimension haben wir so viele Ziffern wie die Länge der Dimension.

Beispiel:

Angenommen, wir hätten ein Array (Matrix)

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

in "zur Notation" haben wir:

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]=...

Um dies zu simulieren, müssten Sie die "n-stellige Notation" verwenden.

Wir haben eine 3-stellige Nummer mit 3 Ziffern für die erste, 4 für die zweite und fünf für die dritte Ziffer

Wir müssen die Anzahl erhöhen, damit wir die Sequenz erhalten

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

Sie können also den Code zum Erhöhen dieser n-stelligen Zahl schreiben. Sie können dies so tun, dass Sie mit einem beliebigen Wert der Zahl beginnen und die Ziffern um eine beliebige Zahl erhöhen / verringern können. Auf diese Weise können Sie verschachtelte Schleifen simulieren, die irgendwo in der Tabelle beginnen und nicht am Ende enden.

Dies ist jedoch keine leichte Aufgabe. Ich kann leider nicht mit der Matlab-Notation helfen.

bmegli
quelle