Wie kann ich eine Funktion auf jede Zeile / Spalte einer Matrix in MATLAB anwenden?

106

Sie können eine Funktion auf jedes Element in einem Vektor anwenden, indem Sie beispielsweise sagen v + 1, oder Sie können die Funktion verwenden arrayfun. Wie kann ich das für jede Zeile / Spalte einer Matrix tun, ohne eine for-Schleife zu verwenden?

FurtiveFelon
quelle

Antworten:

73

Viele eingebaute Operationen mögen sumundprod bereits zeilen- oder spaltenübergreifend ausgeführt werden, sodass Sie möglicherweise die von Ihnen angewendete Funktion umgestalten können, um dies zu nutzen.

Wenn dies keine praktikable Option ist, besteht eine Möglichkeit darin, die Zeilen oder Spalten mit mat2celloder in Zellen zu sammeln num2cellund dann cellfundas resultierende Zellenarray zu bearbeiten.

Angenommen, Sie möchten die Spalten einer Matrix summieren M. Sie können dies einfach tun mit sum:

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

Und hier ist, wie Sie dies mit der komplizierteren num2cell/ cellfunOption tun würden :

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
gnovice
quelle
17
Ich würde die Leistung dieses Ansatzes für einen bestimmten Fall anhand einer einfachen for-Schleife testen, die möglicherweise schneller ist als die Konvertierung einer Matrix in ein Zellenarray. Verwenden Sie zum Testen Tic / Tac Wrap.
Yuk
5
@yuk: Ich denke du meintest "tic / toc". ;)
Gnovice
4
@gnovice, vielleicht hat Yuk etwas Magie gemacht und tak = toc zugewiesen. In einer Sprache, in der true = falseeine gültige Aussage
vorliegt
1
@Argyll: Die Entscheidung, welcher Ansatz effizienter ist, hängt davon ab, welche Art von Funktion Sie anwenden möchten, wie groß die Matrix ist usw. Kurz gesagt, es ist wahrscheinlich problemabhängig. In der Tat kann manchmal eine gute alte for-Schleife die schnellste Wahl sein.
Gnovice
2
@gnovice, ich schlage eine Bearbeitung vor sum(M, 1). Anfänger könnten denken, sumdass sie auf diese Weise für Matrizen beliebiger Größe verwendet werden können und dann ratlos werden, wenn die Matrix eines Tages ist 1-by-n.
Stewie Griffin
24

Möglicherweise möchten Sie die dunkelere Matlab-Funktion bsxfun . In der Matlab-Dokumentation wendet bsxfun "die vom Funktionshandle fun angegebene Element-für-Element-Binäroperation auf Arrays A und B an, wobei die Singleton-Erweiterung aktiviert ist."

@gnovice hat oben angegeben, dass sum und andere Grundfunktionen bereits für die erste Nicht-Singleton-Dimension ausgeführt werden (dh Zeilen, wenn mehr als eine Zeile vorhanden ist, Spalten, wenn nur eine Zeile vorhanden ist, oder höhere Dimensionen, wenn alle niedrigeren Dimensionen die Größe == 1 haben ). Bsxfun funktioniert jedoch für jede Funktion, einschließlich (und insbesondere) benutzerdefinierter Funktionen.

Angenommen, Sie haben eine Matrix A und einen Zeilenvektor BEg. Nehmen wir an:

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

Sie möchten eine Funktion power_by_col, die in einem Vektor C alle Elemente in A auf die Potenz der entsprechenden Spalte von B zurückgibt.

Aus dem obigen Beispiel ist C eine 3x3-Matrix:

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

dh

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

Sie können dies auf Brute-Force-Weise mit repmat tun:

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

Oder Sie können dies auf klassische Weise mit bsxfun tun, das sich intern um den Repmat-Schritt kümmert:

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

Bsxfun erspart Ihnen also einige Schritte (Sie müssen die Abmessungen von A nicht explizit berechnen). In einigen meiner informellen Tests hat sich jedoch herausgestellt, dass repmat ungefähr doppelt so schnell ist, wenn die anzuwendende Funktion (wie meine Power-Funktion oben) einfach ist. Sie müssen sich also entscheiden, ob Sie Einfachheit oder Geschwindigkeit wünschen.

Daniel Golden
quelle
21

Ich kann nicht sagen, wie effizient dies ist, aber hier ist eine Lösung:

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)
Alex
quelle
Eine allgemeinere Antwort finden Sie hier .
Wok
11

Aufbauend auf Alex 'Antwort ist hier eine allgemeinere Funktion:

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));

Hier ist ein Vergleich zwischen den beiden Funktionen:

>> % 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))'
Wok
quelle
4

Zusätzlich zur Entwicklung der Antwort auf diese Frage, beginnend mit r2016b, wird MATLAB die Singleton-Dimensionen implizit erweitern und bsxfunin vielen Fällen die Notwendigkeit beseitigen .

Aus den Versionshinweisen zu r2016b :

Implizite Erweiterung: Wenden Sie elementweise Operationen und Funktionen auf Arrays mit automatischer Erweiterung der Dimensionen der Länge 1 an

Implizite Expansion ist eine Verallgemeinerung der skalaren Expansion. Bei der Skalarerweiterung wird ein Skalar auf die gleiche Größe wie ein anderes Array erweitert, um elementweise Operationen zu erleichtern. Bei impliziter Erweiterung können die hier aufgeführten elementweisen Operatoren und Funktionen ihre Eingaben implizit auf dieselbe Größe erweitern, solange die Arrays kompatible Größen haben. Zwei Arrays haben kompatible Größen, wenn für jede Dimension die Dimensionsgrößen der Eingaben entweder gleich sind oder eine von ihnen 1 ist. Weitere Informationen finden Sie unter Kompatible Arraygrößen für grundlegende Operationen und Array- und Matrixoperationen.

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

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

Logical operators&, |, xor

Bit-wise functionsbitand, bitor, bitxor

Elementary math functionsmax, min, mod, rem, hypot, atan2, atan2d

Sie können beispielsweise den Mittelwert jeder Spalte in einer Matrix A berechnen und dann den Vektor der Mittelwerte von jeder Spalte mit A - Mittelwert (A) subtrahieren.

Bisher war diese Funktionalität über die Funktion bsxfun verfügbar. Es wird jetzt empfohlen, die meisten Verwendungen von bsxfun durch direkte Aufrufe der Funktionen und Operatoren zu ersetzen, die die implizite Erweiterung unterstützen. Im Vergleich zur Verwendung von bsxfun bietet die implizite Erweiterung eine schnellere Geschwindigkeit, eine bessere Speichernutzung und eine verbesserte Lesbarkeit des Codes.

craigim
quelle
2

Keine der oben genannten Antworten hat für mich "out of the box" funktioniert, jedoch funktioniert die folgende Funktion, die durch Kopieren der Ideen der anderen Antworten erhalten wird:

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

Es nimmt eine Funktion an fund wendet sie auf jede Spalte der Matrix an M.

Also zum Beispiel:

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
patapouf_ai
quelle
1

Mit neueren Versionen von Matlab können Sie die Tabellendatenstruktur zu Ihrem Vorteil nutzen. Es gibt sogar eine "Rowfun" -Operation, aber ich fand es einfacher, dies einfach zu tun:

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

oder hier ist eine ältere, für die ich keine Tabellen benötige, für ältere Matlab-Versionen.

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

Die akzeptierte Antwort scheint darin zu bestehen, zuerst in Zellen umzuwandeln und dann cellfunüber alle Zellen zu operieren. Ich kenne die spezifische Anwendung nicht, aber im Allgemeinen würde ich denken, dass die Verwendung bsxfunzum Überarbeiten der Matrix effizienter wäre. Grundsätzlich wird bsxfuneine Operation Element für Element auf zwei Arrays angewendet. Wenn Sie also jedes Element in einem n x 1Vektor mit jedem Element in einem m x 1Vektor multiplizieren möchten , um ein n x mArray zu erhalten, können Sie Folgendes verwenden:

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

Dies gibt Ihnen eine Matrix namens, resultwobei der (i, j) -Eintrag das i-te Element von vec1multipliziert mit dem j-ten Element von ist vec2.

Sie können bsxfunfür alle Arten von integrierten Funktionen verwenden und Ihre eigenen deklarieren. Die Dokumentation enthält eine Liste vieler integrierter Funktionen. Grundsätzlich können Sie jedoch jede Funktion benennen, die zwei Arrays (Vektor oder Matrix) als Argumente akzeptiert, und sie zum Laufen bringen.

Engineero
quelle
-1

Stolperte über diese Frage / Antwort, während ich suchte, wie man die Zeilensummen einer Matrix berechnet.

Ich möchte nur hinzufügen, dass die SUM-Funktion von Matlab tatsächlich die Summierung für eine bestimmte Dimension unterstützt, dh eine Standardmatrix mit zwei Dimensionen.

Um die Spaltensummen zu berechnen, gehen Sie wie folgt vor:

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

und für die Zeilensummen einfach tun

rowsum = sum(M, 2)

Meine Wette ist, dass dies schneller ist als das Programmieren einer for-Schleife und das Konvertieren in Zellen :)

All dies finden Sie in der Matlab-Hilfe für SUM.

nover
quelle
7
Die Fähigkeit, SUM entlang einer bestimmten Dimension anzuwenden, wurde im ersten Satz der ursprünglichen Antwort auf diese Frage erwähnt. Die Antwort ging dann auf den Fall ein, in dem die Möglichkeit zur Auswahl einer Dimension noch nicht in die Funktion integriert ist. Sie haben jedoch Recht, dass die Verwendung der integrierten Optionen zur Dimensionsauswahl - sofern verfügbar - fast immer schneller ist als eine for-Schleife oder die Konvertierung in Zellen.
cjh
Die obige Antwort hat mich jedoch zurück zur Matlab-Dokumentation geschickt, da ich nicht all diese Fantasie brauchte, also wollte ich nur andere, die die einfache Lösung benötigen, von der Suche an weitergeben und retten.
Nover
-2

Wenn Sie die Länge Ihrer Zeilen kennen, können Sie Folgendes tun:

a=rand(9,3);
b=rand(9,3); 
arrayfun(@(x1,x2,y1,y2,z1,z2) line([x1,x2],[y1,y2],[z1,z2]) , a(:,1),b(:,1),a(:,2),b(:,2),a(:,3),b(:,3) )
Stefan
quelle
2
An alle, die diese Antwort sehen: Dies ist nicht der richtige Weg! Dies ist nicht der Weg, um etwas in MATLAB zu tun!
Stewie Griffin