87 votes

Comment itérer à travers chaque élément d'une matrice à n dimensions dans MATLAB ?

Je suis confronté à un problème. J'ai besoin d'itérer à travers chaque élément d'une matrice à n dimensions dans MATLAB. Le problème est que je ne sais pas comment le faire pour un nombre arbitraire de dimensions. Je sais que je peux dire

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

et ainsi de suite, mais existe-t-il un moyen de le faire pour un nombre arbitraire de dimensions ?

13 votes

Note sur la terminologie Matlab : Matlab possède un petit nombre de types de données de base. Les plus importants sont : struct, matrix et cell array. Pour faire référence aux parties d'une matrice, il est courant d'utiliser le terme "élément", et de réserver le terme "cellule" aux parties d'un tableau de cellules. Les tableaux de cellules et les matrices présentent de nombreuses différences syntaxiques et sémantiques, même s'il s'agit de structures de données à N dimensions.

3 votes

Puis-je vous demander à quoi sert l'itération ? Peut-être y a-t-il une façon "vectorisée" de le faire à la place...

93voto

Andrew Points 569

Vous pouvez utiliser l'indexation linéaire pour accéder à chaque élément.

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

C'est utile si vous n'avez pas besoin de savoir à quel indice i, j, k, vous vous trouvez. Cependant, si vous n'avez pas besoin de savoir à quel indice vous vous trouvez, vous feriez mieux d'utiliser arrayfun().

1 votes

En outre, si vous souhaitez récupérer les indices pour une raison quelconque, vous pouvez toujours le faire en utilisant ces deux commandes simples : I = cell(1, ndims(array)); [I{:}] = ind2sub(size(array),idx); .

36voto

L'idée d'un index linéaire pour les tableaux dans MATLAB est importante. Un tableau dans MATLAB n'est en fait qu'un vecteur d'éléments, disposés en mémoire. MATLAB vous permet d'utiliser soit un index de ligne et de colonne, soit un index linéaire unique. Par exemple,

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

A(2,3)
ans =
     7

A(8)
ans =
     7

Nous pouvons voir l'ordre dans lequel les éléments sont stockés en mémoire en déroulant le tableau en un vecteur.

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

Comme vous pouvez le voir, le 8ème élément est le nombre 7. En fait, la fonction find renvoie ses résultats sous la forme d'un indice linéaire.

find(A>6)
ans =
     1
     6
     8

Le résultat est que nous pouvons accéder à chaque élément tour à tour d'un tableau général n-d en utilisant une seule boucle. Par exemple, si l'on veut mettre au carré les éléments de A (oui, je sais qu'il y a de meilleures façons de faire), on peut faire ceci :

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

Il existe de nombreuses circonstances où l'indice linéaire est plus utile. La conversion entre l'indice linéaire et les indices à deux (ou plus) dimensions est accomplie avec les fonctions sub2ind et ind2sub.

L'index linéaire s'applique en général à n'importe quel tableau dans Matlab. Vous pouvez donc l'utiliser sur des structures, des tableaux de cellules, etc. Le seul problème avec l'index linéaire est lorsqu'ils deviennent trop grands. MATLAB utilise un entier de 32 bits pour stocker ces index. Donc si votre tableau contient plus de 2^32 éléments au total, l'index linéaire échouera. Ce n'est vraiment un problème que si vous utilisez souvent des matrices éparses, où occasionnellement cela peut poser un problème. (Bien que je n'utilise pas une version 64 bits de MATLAB, je pense que ce problème a été résolu pour les personnes chanceuses qui le font).

0 votes

L'indexation dans MATLAB 64 bits autorise en effet correctement les indices 64 bits. Par exemple : x = ones(1,2^33,'uint8'); x(2^33) fonctionne comme prévu.

0 votes

@Edric - Bien sûr, c'est un comportement qui aurait sûrement changé au cours des années (et des nombreuses versions) depuis que j'ai fait cette déclaration. Merci quand même d'avoir vérifié.

0 votes

:) Je n'ai réalisé l'ancienneté de la réponse qu'après avoir commenté - la question est apparue dans mon flux RSS, et je n'ai même pas remarqué que j'y avais répondu aussi !

15voto

gnovice Points 70970

Comme indiqué dans d'autres réponses, vous pouvez itérer sur tous les éléments d'une matrice. A (de n'importe quelle dimension) en utilisant un indice linéaire de 1 a numel(A) dans une seule boucle for. Il existe également quelques fonctions que vous pouvez utiliser : arrayfun y cellfun .

Supposons d'abord que vous avez une fonction que vous voulez appliquer à chaque élément de A (appelé my_func ). Vous créez d'abord un fonction poignée à cette fonction :

fcn = @my_func;

Si A est une matrice (de type double, simple, etc.) de dimension arbitraire, vous pouvez utiliser arrayfun pour appliquer my_func à chaque élément :

outArgs = arrayfun(fcn, A);

Si A est un réseau de cellules de dimension arbitraire, vous pouvez utiliser cellfun pour appliquer my_func à chaque cellule :

outArgs = cellfun(fcn, A);

La fonction my_func doit accepter A comme une entrée. S'il y a des sorties de my_func Ceux-ci sont placés dans outArgs qui sera de la même taille/dimension que A .

Une mise en garde sur les sorties... si my_func renvoie des sorties de tailles et de types différents lorsqu'il opère sur différents éléments de l'UE. A entonces outArgs devra être transformé en un tableau de cellules. Ceci est fait en appelant soit arrayfun o cellfun avec une paire paramètre/valeur supplémentaire :

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

13voto

Edric Points 11387

Une autre astuce consiste à utiliser ind2sub y sub2ind . En collaboration avec numel y size cela peut vous permettre de faire des choses comme ce qui suit, qui crée un tableau à N dimensions, puis donne la valeur 1 à tous les éléments de la "diagonale".

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

0 votes

+1 pour avoir montré un bon exemple de la façon dont MATLAB casse la saisie des canards.

1voto

mathcow Points 71

Ces solutions sont plus rapides (environ 11%) que l'utilisation de numel ;)

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

ou

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

UPD. tnx @rayryeng pour l'erreur détectée dans la dernière réponse.


Avis de non-responsabilité

L'information sur le calendrier que ce post a référencé est incorrecte et inexacte en raison d'une erreur typographique fondamentale qui a été faite (voir le flux de commentaires ci-dessous ainsi que la histoire de l'édition - regardez spécifiquement la première version de cette réponse). Caveat Emptor .

1 votes

1 : array(:) est équivalent à 1 : array(1) . Il n'y a pas d'itération de tous les éléments, c'est pourquoi vos temps d'exécution sont rapides. De plus, rand génère virgule flottante numéros, et ainsi de suite 1 : array(:) produirait un tableau vide car votre instruction essaie de trouver un vecteur croissant dont la valeur initiale est 1 et dont la valeur finale est un nombre à virgule flottante dont l'intervalle est de [0,1) Il n'y a pas de vecteur possible, ce qui donne un vecteur vide. Votre for La boucle ne fonctionne pas, et donc votre affirmation est fausse. -1 vote. Désolé.

0 votes

@rayryeng vous n'avez pas raison. array( :) n'est pas équivalent à 1 : array(1). Il aime reshape(...) .

0 votes

@rayryeng matlab r2013a + linux - ça marche ! ;) Je viens aussi d'exécuter ce code

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X