Comment puis-je appliquer une fonction à chaque ligne / colonne d'une matrice dans MATLAB?


Answers

Vous pouvez vouloir la fonction de Matlab plus obscure bsxfun . Dans la documentation Matlab, bsxfun "applique l'opération binaire élément par élément spécifiée par la fonction handle fun aux tableaux A et B, avec l'extension singleton activée."

@gnovice a indiqué plus haut que la somme et d'autres fonctions de base agissent déjà sur la première dimension non singulière (lignes s'il y a plus d'une ligne, colonnes s'il n'y a qu'une seule ligne ou dimensions plus élevées si les dimensions inférieures ont toutes la taille == 1 ). Cependant, bsxfun fonctionne pour toutes les fonctions, y compris (et surtout) les fonctions définies par l'utilisateur.

Par exemple, disons que vous avez une matrice A et un vecteur ligne BEg, disons:

A = [1 2 3;
     4 5 6;
     7 8 9]
B = [0 1 2]

Vous voulez une fonction power_by_col qui retourne dans un vecteur C tous les éléments de A à la puissance de la colonne correspondante de B.

Dans l'exemple ci-dessus, C est une matrice 3x3:

C = [1^0 2^1 3^2;
     4^0 5^1 6^2;
     7^0 8^1 9^2]

c'est à dire,

C = [1 2 9;
     1 5 36;
     1 8 81]

Vous pouvez le faire par la force brute en utilisant repmat:

C = A.^repmat(B, size(A, 1), 1)

Ou vous pouvez le faire de manière élégante en utilisant bsxfun, qui s'occupe en interne de l'étape repmat:

C = bsxfun(@(x,y) x.^y, A, B)

Donc bsxfun vous sauve quelques étapes (vous n'avez pas besoin de calculer explicitement les dimensions de A). Cependant, dans certains tests informels, il s'avère que repmat est environ deux fois plus rapide si la fonction à appliquer (comme ma fonction de puissance, ci-dessus) est simple. Vous devrez donc choisir si vous voulez la simplicité ou la vitesse.

Question

Vous pouvez appliquer une fonction à chaque élément d'un vecteur en disant, par exemple, v + 1 , ou vous pouvez utiliser la fonction arrayfun . Comment puis-je le faire pour chaque ligne / colonne d'une matrice sans utiliser de boucle for?




En s'appuyant sur la réponse d'Alex , voici une fonction plus générique:

applyToGivenRow = @(func, matrix) @(row) func(matrix(row, :));
newApplyToRows = @(func, matrix) arrayfun(applyToGivenRow(func, matrix), 1:size(matrix,1), 'UniformOutput', false)';
takeAll = @(x) reshape([x{:}], size(x{1},2), size(x,1))';
genericApplyToRows = @(func, matrix) takeAll(newApplyToRows(func, matrix));

Voici une comparaison entre les deux fonctions:

>> % Example
myMx = [1 2 3; 4 5 6; 7 8 9];
myFunc = @(x) [mean(x), std(x), sum(x), length(x)];
>> genericApplyToRows(myFunc, myMx)

ans =

     2     1     6     3
     5     1    15     3
     8     1    24     3

>> applyToRows(myFunc, myMx)
??? Error using ==> arrayfun
Non-scalar in Uniform output, at index 1, output 1.
Set 'UniformOutput' to false.

Error in ==> @(func,matrix)arrayfun(applyToGivenRow(func,matrix),1:size(matrix,1))'



La réponse acceptée semble être de convertir d'abord en cellules puis d'utiliser cellfun pour opérer sur toutes les cellules. Je ne connais pas l'application spécifique, mais en général, je pense que l'utilisation de bsxfun pour opérer sur la matrice serait plus efficace. Fondamentalement, bsxfun applique une opération élément par élément sur deux tableaux. Donc, si vous voulez multiplier chaque élément dans un vecteur nx 1 par chaque élément dans un vecteur mx 1 pour obtenir un tableau nxm , vous pouvez utiliser:

vec1 = [ stuff ];    % n x 1 vector
vec2 = [ stuff ];    $ m x 1 vector
result = bsxfun('times', vec1.', vec2);

Cela vous donnera un result appelé matrice dans laquelle l'entrée (i, j) sera l'élément ith de vec1 multiplié par le jème élément de vec2 .

Vous pouvez utiliser bsxfun pour toutes sortes de fonctions intégrées, et vous pouvez déclarer les vôtres. La documentation contient une liste de nombreuses fonctions intégrées, mais vous pouvez nommer n'importe quelle fonction qui accepte deux tableaux (vecteur ou matrice) comme arguments et la faire fonctionner.




Trébuché sur cette question / réponse tout en cherchant comment calculer les sommes en ligne d'une matrice.

Je voudrais simplement ajouter que la fonction SUM de Matlab a en fait un support pour sommer une dimension donnée, c'est-à-dire une matrice standard à deux dimensions.

Donc, pour calculer la somme des colonnes, faites:

colsum = sum(M) % or sum(M, 1)

et pour les sommes en ligne, faites simplement

rowsum = sum(M, 2)

Mon pari est que c'est plus rapide que la programmation d'une boucle for et la conversion en cellules :)

Tout cela peut être trouvé dans l'aide matlab pour SUM.




En ajoutant à la nature évolutive de la réponse à cette question, en commençant par r2016b, MATLAB développera implicitement les dimensions singleton, éliminant ainsi le besoin de bsxfun dans de nombreux cas.

À partir des notes de publication r2016b :

Expansion implicite: Appliquez des opérations et des fonctions par éléments aux tableaux avec une expansion automatique des dimensions de longueur 1

L'expansion implicite est une généralisation de l'expansion scalaire. Avec l'expansion scalaire, un scalaire se dilate pour avoir la même taille qu'un autre tableau afin de faciliter les opérations par éléments. Avec l'expansion implicite, les opérateurs et les fonctions listés ici peuvent implicitement étendre leurs entrées pour qu'elles aient la même taille, à condition que les tailles des tableaux soient compatibles. Deux matrices ont des tailles compatibles si, pour chaque dimension, les dimensions des entrées sont identiques ou si l'une d'entre elles est 1. Pour plus d'informations, reportez-vous à la section Tailles de matrices compatibles pour les opérations de base et matrices et matrices.

Element-wise arithmetic operators — +, -, .*, .^, ./, .\

Relational operators — <, <=, >, >=, ==, ~=

Logical operators — &, |, xor

Bit-wise functions — bitand, bitor, bitxor

Elementary math functions — max, min, mod, rem, hypot, atan2, atan2d

Par exemple, vous pouvez calculer la moyenne de chaque colonne dans une matrice A, puis soustraire le vecteur des valeurs moyennes de chaque colonne avec A - mean (A).

Auparavant, cette fonctionnalité était disponible via la fonction bsxfun. Il est maintenant recommandé de remplacer la plupart des utilisations de bsxfun par des appels directs aux fonctions et aux opérateurs qui prennent en charge l'expansion implicite. Comparé à l'utilisation de bsxfun, l'expansion implicite offre une vitesse plus rapide, une meilleure utilisation de la mémoire et une meilleure lisibilité du code.




Related