web-dev-qa-db-fra.com

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

Vous pouvez appliquer une fonction à chaque élément d'un vecteur en indiquant, 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?

95
FurtiveFelon

De nombreuses opérations intégrées telles que sum et prod sont déjà capables de fonctionner sur des lignes ou des colonnes, vous permettant ainsi de refactoriser la fonction que vous utilisez pour en tirer parti.

Si ce n'est pas une option viable, vous pouvez collecter les lignes ou les colonnes dans des cellules à l'aide de mat2cell ou num2cell , puis utilisez cellfun pour agir sur le tableau de cellules obtenu.

Par exemple, supposons que vous vouliez additionner les colonnes d'une matrice M. Vous pouvez le faire simplement en utilisant sum :

M = magic(10);           %# A 10-by-10 matrix
columnSums = sum(M, 1);  %# A 1-by-10 vector of sums for each column

Et voici comment vous feriez cela en utilisant l'option plus compliquée num2cell / cellfun :

M = magic(10);                  %# A 10-by-10 matrix
C = num2cell(M, 1);             %# Collect the columns into cells
columnSums = cellfun(@sum, C);  %# A 1-by-10 vector of sums for each cell
68
gnovice

Vous voudrez peut-être la fonction plus obscure de Matlab bsxfun . Dans la documentation Matlab, bsxfun "applique l'opération binaire élément par élément spécifiée par le descripteur de fonction fun aux tableaux A et B, avec le développement de singleton activé."

@gnovice a indiqué plus haut que sum et d'autres fonctions de base opéraient déjà sur la première dimension non singleton (c'est-à-dire des lignes s'il y a plus d'une ligne, des colonnes s'il n'y a qu'une ligne ou des dimensions supérieures si toutes les dimensions inférieures ont une taille == 1 ). Cependant, bsxfun fonctionne pour toute fonction, y compris (et en particulier) les fonctions définies par l'utilisateur.

Par exemple, supposons que vous ayez une matrice A et un vecteur de rangée B. Par exemple, supposons:

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 utiliser la méthode de la force brute avec repmat:

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

Ou vous pouvez le faire avec classe en utilisant bsxfun, qui gère en interne l’étape repmat:

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

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

23
Daniel Golden

Je ne peux pas dire à quel point c'est efficace, mais voici une solution:

applyToGivenRow = @(func, matrix) @(row) func(matrix(row, :))
applyToRows = @(func, matrix) arrayfun(applyToGivenRow(func, matrix), 1:size(matrix,1))'

% Example
myMx = [1 2 3; 4 5 6; 7 8 9];
myFunc = @sum;

applyToRows(myFunc, myMx)
19
Alex

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))'
11
Wok

Par souci d’intégralité/d’intérêt, j'aimerais ajouter que matlab a une fonction qui vous permet d’opérer sur des données par ligne plutôt que par élément. Il s'appelle rowfun ( http://www.mathworks.se/help/matlab/ref/rowfun.html ), mais le seul "problème" est qu'il fonctionne sur tables ( http : //www.mathworks.se/help/matlab/ref/table.html ) plutôt que matrices.

6
kamjagin

En ajoutant à la nature évolutive de la réponse à cette question, à partir de r2016b, MATLAB étendra implicitement les dimensions singleton, éliminant ainsi le besoin de bsxfun dans de nombreux cas.

À partir des notes de publication de r2016b :

Développement implicite: Appliquez des opérations et des fonctions élémentaires aux tableaux avec développement automatique des dimensions de longueur 1

L'expansion implicite est une généralisation de l'expansion scalaire. Avec expansion scalaire, un scalaire se développe pour avoir la même taille qu’un autre tableau pour faciliter les opérations élément par élément. Avec expansion implicite, les opérateurs et fonctions élémentaires listés ici peuvent implicitement étendre leurs entrées pour avoir la même taille, aussi longtemps que les tableaux ont tailles compatibles. Deux tableaux ont des tailles compatibles si, pour chaque dimension, les dimensions des entrées sont identiques ou 1. Voir Tailles de tableau compatibles pour les opérations de base et Array vs. Matrix Operations pour plus d'informations.

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 soustrayez le vecteur des valeurs moyennes de chaque colonne avec A - moyenne (A).

Auparavant, cette fonctionnalité était disponible via la fonction bsxfun . Il est maintenant recommandé de remplacer la plupart des utilisations de bsxfun par direct appels aux fonctions et opérateurs qui supportent le développement implicite . Par rapport à l’utilisation de bsxfun, l’expansion implicite offre une vitesse plus rapide, meilleure utilisation de la mémoire et lisibilité améliorée du code.

3
craigim

La réponse acceptée semble être de convertir d'abord les 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 penserais que l'utilisation de bsxfun pour fonctionner sur la matrice serait plus efficace. En gros, bsxfun applique une opération élément par élément sur deux tableaux. Donc, si vous voulez multiplier chaque élément d'un vecteur n x 1 par chaque élément d'un vecteur m x 1 pour obtenir un tableau n x m, vous pouvez utiliser:

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

Cela vous donnera une matrice appelée result dans laquelle l'entrée (i, j) sera le énième élément de vec1 multiplié par le jiè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 en principe nommer toute fonction acceptant deux tableaux (vecteur ou matrice) en tant qu'arguments et la faire fonctionner.

1
Engineero

Avec les versions récentes de Matlab, vous pouvez utiliser la structure de données Table à votre avantage. Il y a même une opération 'rowfun' mais j'ai trouvé plus facile de faire ceci:

a = magic(6);
incrementRow = cell2mat(cellfun(@(x) x+1,table2cell(table(a)),'UniformOutput',0))

ou voici une version plus ancienne que j'ai qui ne nécessite pas de tables, pour les anciennes versions de Matlab.

dataBinner = cell2mat(arrayfun(@(x) Binner(a(x,:),2)',1:size(a,1),'UniformOutput',0)')
1
Jordan

Aucune des réponses ci-dessus n'a fonctionné "hors de la boîte" pour moi, toutefois, la fonction suivante, obtenue en copiant les idées des autres réponses

apply_func_2_cols = @(f,M) cell2mat(cellfun(f,num2cell(M,1), 'UniformOutput',0));

Il prend une fonction f et l'applique à chaque colonne de la matrice M.

Donc par exemple:

f = @(v) [0 1;1 0]*v + [0 0.1]';
apply_func_2_cols(f,[0 0 1 1;0 1 0 1])

 ans =

   0.00000   1.00000   0.00000   1.00000
   0.10000   0.10000   1.10000   1.10000
0
patapouf_ai