Jak iterować przez każdy element w n-wymiarowej macierzy w MATLAB?

87

Mam problem. Muszę iterować przez każdy element w n-wymiarowej macierzy w MATLAB-ie. Problem w tym, że nie wiem, jak to zrobić dla dowolnej liczby wymiarów. Wiem, że mogę powiedzieć

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

i tak dalej, ale czy jest sposób, aby to zrobić dla dowolnej liczby wymiarów?

rlbond
źródło
13
Uwaga terminologiczna Matlab: Matlab ma niewielką liczbę podstawowych typów danych. Najważniejsze z nich to: struktura, macierz i tablica komórek. Odnosząc się do części macierzy, często używa się terminu „element”, a termin „komórka” rezerwuje się w odniesieniu do części tablicy komórek. Macierze i macierze komórek mają liczne różnice składniowe i semantyczne, mimo że obie są N-wymiarowymi strukturami danych.
Pan Fooz
3
Czy mogę zapytać, do czego potrzebujesz iteracji? Może zamiast tego istnieje „wektoryzowany” sposób, aby to zrobić ...
Hosam Aly

Odpowiedzi:

92

Aby uzyskać dostęp do każdego elementu, można użyć indeksowania liniowego.

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

Jest to przydatne, jeśli nie musisz wiedzieć, na czym jesteś i, j, k. Jeśli jednak nie musisz wiedzieć, w jakim indeksie się znajdujesz, prawdopodobnie lepiej będzie użyć funkcji arrayfun ()

Andrzej
źródło
1
Ponadto, jeśli chce odzyskać indeksy z jakiegoś powodu, nadal mógłby za pomocą tych dwóch prostych poleceń: I = cell(1, ndims(array)); [I{:}] = ind2sub(size(array),idx);.
knedlsepp
34

Idea liniowego indeksu dla tablic w Matlabie jest ważna. Tablica w MATLABie jest w rzeczywistości wektorem elementów ułożonych w pamięci. MATLAB pozwala na użycie indeksu wierszy i kolumn lub pojedynczego indeksu liniowego. Na przykład,

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

A(2,3)
ans =
     7

A(8)
ans =
     7

Możemy zobaczyć kolejność elementów przechowywanych w pamięci, rozwijając tablicę do wektora.

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

Jak widać, ósmy element to liczba 7. W rzeczywistości funkcja find zwraca swoje wyniki jako indeks liniowy.

find(A>6)
ans =
     1
     6
     8

W rezultacie możemy uzyskać dostęp do każdego elementu po kolei z ogólnej tablicy nd za pomocą pojedynczej pętli. Na przykład, gdybyśmy chcieli podnieść do kwadratu elementy A (tak, wiem, że są na to lepsze sposoby), można to zrobić:

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

Istnieje wiele okoliczności, w których wskaźnik liniowy jest bardziej przydatny. Konwersja między indeksem liniowym a dwoma (lub wyższymi) wymiarowymi indeksami dolnymi odbywa się za pomocą funkcji sub2ind i ind2sub.

Indeks liniowy ma zastosowanie ogólnie do dowolnej tablicy w programie MATLAB. Możesz więc używać go na strukturach, tablicach komórek itp. Jedyny problem z indeksem liniowym polega na tym, że stają się zbyt duże. MATLAB używa 32-bitowej liczby całkowitej do przechowywania tych indeksów. Więc jeśli twoja tablica ma więcej niż łącznie 2 ^ 32 elementów, indeks liniowy zawiedzie. Tak naprawdę jest to problem tylko wtedy, gdy często używasz rzadkich macierzy, gdy czasami powoduje to problem. (Chociaż nie używam 64-bitowej wersji MATLAB, uważam, że problem został rozwiązany dla tych szczęśliwców, którzy to robią.)


źródło
Indeksowanie w 64-bitowym MATLABie rzeczywiście poprawnie zezwala na 64-bitowe indeksowanie. Na przykład: x = ones(1,2^33,'uint8'); x(2^33)działa zgodnie z oczekiwaniami.
Edric
@Edric - Oczywiście jest to zachowanie, które z pewnością zmieniłoby się przez lata (i wiele wydań), odkąd wydałem to oświadczenie. Dziękuję za sprawdzenie.
:) Nie zdawałem sobie sprawy, jak stara była odpowiedź, dopóki nie skomentowałem - pytanie właśnie pojawiło się w moim kanale RSS, a nawet nie zauważyłem, że też na nie odpowiedziałem!
Edric
15

Jak wskazano w kilku innych odpowiedziach, możesz iterować po wszystkich elementach macierzy A(o dowolnym wymiarze), używając indeksu liniowego od 1do numel(A)w pojedynczej pętli for. Istnieje również kilka funkcji, których możesz użyć: arrayfuni cellfun.

Załóżmy najpierw, że masz funkcję, którą chcesz zastosować do każdego elementu A(nazwanego my_func). Najpierw tworzysz uchwyt funkcji do tej funkcji:

fcn = @my_func;

Jeśli Ajest to macierz (typu double, single, itp.) O dowolnym wymiarze, możesz zastosować arrayfunmy_funcdo każdego elementu:

outArgs = arrayfun(fcn, A);

Jeśli Ajest to tablica komórek o dowolnym wymiarze, możesz zastosować cellfunmy_funcdo każdej komórki:

outArgs = cellfun(fcn, A);

Funkcja my_funcmusi akceptować Ajako dane wejściowe. Jeśli są jakieś wyjścia z my_func, są one umieszczane w outArgs, które będą miały taki sam rozmiar / wymiar jak A.

Jedno zastrzeżenie dotyczące danych wyjściowych ... jeśli my_funczwraca dane wyjściowe o różnych rozmiarach i typach, gdy działa na różnych elementach programu A, outArgsbędzie musiało zostać przekształcone w tablicę komórek. Odbywa się to poprzez wywołanie jednej arrayfunlub cellfundodatkowej pary parametr / wartość:

outArgs = arrayfun(fcn, A, 'UniformOutput', false);
outArgs = cellfun(fcn, A, 'UniformOutput', false);
gnovice
źródło
13

Jeszcze jedna sztuczka polega na użyciu ind2subi sub2ind. W połączeniu z numeli sizepozwala to na wykonanie takich czynności, jak poniższe, które tworzą tablicę N-wymiarową, a następnie ustawiają wszystkie elementy na „przekątnej” na 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
Edric
źródło
+1 za pokazanie dobrego przykładu tego, jak MATLAB łamie pisanie na klawiaturze.
Phillip Cloud
1

Możesz sprawić, że funkcja rekurencyjna będzie działać

  • Pozwolić L = size(M)
  • Pozwolić idx = zeros(L,1)
  • Przyjmij length(L)jako maksymalną głębokość
  • Pętla for idx(depth) = 1:L(depth)
  • Jeśli twoja głębokość wynosi length(L), wykonaj operację elementu, w przeciwnym razie wywołaj funkcję ponownie za pomocądepth+1

Nie tak szybko jak metody wektoryzowane, jeśli chcesz sprawdzić wszystkie punkty, ale jeśli nie musisz oceniać większości z nich, może to znacznie zaoszczędzić czas.

Dennis Jaheruddin
źródło
1

te rozwiązania są szybsze (ok. 11%) niż przy użyciu numel;)

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

lub

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

UPD. tnx @rayryeng dla wykrycia błędu w ostatniej odpowiedzi


Zrzeczenie się

Informacje o czasie, do których odwołuje się ten post, są niepoprawne i niedokładne z powodu fundamentalnej literówki (zobacz strumień komentarzy poniżej, a także historię zmian - spójrz w szczególności na pierwszą wersję tej odpowiedzi). Caveat Emptor .

mathcow
źródło
1
1 : array(:)jest równoważne 1 : array(1). To nie powoduje iteracji przez wszystkie elementy, dlatego czasy wykonywania są szybkie. Ponadto randgeneruje liczby zmiennoprzecinkowe , co 1 : array(:)spowodowałoby powstanie pustej tablicy, ponieważ instrukcja próbuje znaleźć rosnący wektor o wartości początkowej równej 1 z wartością końcową jako liczbą zmiennoprzecinkową z zakresem [0,1)wykluczającym 1 w zwiększaniu kroki z 1. Nie ma takiego możliwego wektora, co skutkuje pustym wektorem. Twoja forpętla nie działa, więc twoje twierdzenie jest fałszywe. -1 głos. Przepraszam.
rayryeng
@rayryeng nie masz racji. tablica (:) nie jest równoważna 1: tablica (1). Lubi reshape(...).
mathcow
@rayryeng matlab r2013a + linux - to działa! ;) Właśnie uruchomiłem ten kod
mathcow
Wpisz 1 : array(:)w wierszu polecenia po utworzeniu array . Czy otrzymujesz pustą matrycę? jeśli tak, to twój kod nie działa. Zostawiam mój głos, ponieważ podaje Pan fałszywe informacje.
rayryeng
@rayryeng rozumiem! tak, masz rację, przepraszam za głupi spór
mathcow
-1

Jeśli przyjrzysz się sizebliżej innym zastosowaniom , zobaczysz, że w rzeczywistości możesz uzyskać wektor wielkości każdego wymiaru. Ten link pokazuje dokumentację:

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

Po uzyskaniu wektora rozmiaru wykonaj iterację po tym wektorze. Coś takiego (przepraszam za moją składnię, ponieważ nie korzystałem z Matlab od czasów studiów):

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

Zrób to w rzeczywistej składni Matlab-legalnej i myślę, że zrobi to, co chcesz.

Powinieneś także być w stanie przeprowadzić indeksowanie liniowe zgodnie z opisem tutaj .

Erich Mirabal
źródło
Nie do końca rozumiem, jak to uporządkowanie pętli będzie iterować po wszystkich elementach macierzy. Na przykład, jeśli masz macierz 3 na 4 (z 12 elementami), twoja wewnętrzna pętla będzie iterować tylko 7 razy.
gnovice
powinien iterować po każdym wymiarze macierzy. Pętla zewnętrzna iteruje po wymiarze, pętla wewnętrzna po rozmiarze tego wymiaru. Przynajmniej taki jest zamysł. Jak wszyscy twierdzą, jeśli wszystko, czego chce, to każda komórka, indeksowanie liniowe jest najlepsze. Jeśli chce powtórzyć każdy wymiar, będzie musiał zrobić coś podobnego do tego.
Erich Mirabal
również dzięki za edycję. mój link był trochę zagmatwany i po prostu nie działałby poprawnie przy użyciu zwykłego sposobu łączenia. Ponadto, aby rozszerzyć moje stwierdzenie: nadal musiałby wykonać wiele innych czynności śledzenia indeksu (używając jako licznika lub czegoś w tym rodzaju). Myślę, że podejście ty lub Andrew byłoby łatwiejsze dla tego, co myślę, że próbuje zrobić.
Erich Mirabal
-1

Chcesz symulować n-zagnieżdżone pętle for.

Iterowanie przez tablicę n-wymiarową można postrzegać jako zwiększenie liczby n-cyfrowej.

Na każdym wymiarze mamy tyle cyfr, ile jest długości wymiaru.

Przykład:

Załóżmy, że mamy tablicę (macierz)

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

w "do notacji" mamy:

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

aby to zasymulować, musiałbyś użyć „notacji liczby n-cyfrowej”

Mamy trzycyfrową liczbę, z 3 cyframi na pierwszą, 4 na drugą i pięcioma na trzecią

Musimy zwiększyć liczbę, aby uzyskać sekwencję

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

Możesz więc napisać kod zwiększający taką n-cyfrową liczbę. Możesz to zrobić w taki sposób, że możesz zacząć od dowolnej wartości liczby i zwiększać / zmniejszać cyfry o dowolne liczby. W ten sposób można symulować zagnieżdżone pętle, które zaczynają się gdzieś w tabeli, a kończą nie na końcu.

Nie jest to jednak łatwe zadanie. Nie mogę pomóc z notacją Matlab niestety.

bmegli
źródło