Puoi applicare una funzione a ogni elemento in un vettore dicendo, ad esempio v + 1
, oppure puoi usare la funzione arrayfun
. Come posso farlo per ogni riga / colonna di una matrice senza utilizzare un ciclo for?
Puoi applicare una funzione a ogni elemento in un vettore dicendo, ad esempio v + 1
, oppure puoi usare la funzione arrayfun
. Come posso farlo per ogni riga / colonna di una matrice senza utilizzare un ciclo for?
Risposte:
Molte operazioni incorporate come sum
e prod
sono già in grado di operare su righe o colonne, quindi potresti essere in grado di refactoring della funzione che stai applicando per trarne vantaggio.
Se questa non è un'opzione praticabile, un modo per farlo è raccogliere le righe o le colonne in celle utilizzando mat2cell
o num2cell
, quindi utilizzare cellfun
per operare sulla matrice di celle risultante.
Ad esempio, supponiamo che tu voglia sommare le colonne di una matrice M
. Puoi farlo semplicemente usando sum
:
M = magic(10); %# A 10-by-10 matrix
columnSums = sum(M, 1); %# A 1-by-10 vector of sums for each column
Ed ecco come lo faresti usando l' opzione più complicata 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
true = false
è un'affermazione valida, sono sicuro che c'è un modo per farlo (:
sum(M, 1)
. I principianti potrebbero pensare di sum
poter essere usati in questo modo per matrici di dimensioni arbitrarie e poi rimanere perplessi quando la matrice un giorno lo è 1-by-n
.
Potresti volere la più oscura funzione Matlab bsxfun . Dalla documentazione di Matlab, bsxfun "applica l'operazione binaria elemento per elemento specificata dalla funzione handle fun agli array A e B, con l'espansione singleton abilitata."
@gnovice ha affermato sopra che la somma e altre funzioni di base operano già sulla prima dimensione non singleton (cioè, righe se c'è più di una riga, colonne se c'è solo una riga, o dimensioni superiori se le dimensioni inferiori hanno tutte dimensione == 1 ). Tuttavia, bsxfun funziona per qualsiasi funzione, comprese (e soprattutto) le funzioni definite dall'utente.
Ad esempio, supponiamo di avere una matrice A e un vettore riga BEg, diciamo:
A = [1 2 3;
4 5 6;
7 8 9]
B = [0 1 2]
Vuoi una funzione power_by_col che restituisca in un vettore C tutti gli elementi in A alla potenza della corrispondente colonna di B.
Dall'esempio sopra, C è una matrice 3x3:
C = [1^0 2^1 3^2;
4^0 5^1 6^2;
7^0 8^1 9^2]
vale a dire,
C = [1 2 9;
1 5 36;
1 8 81]
Puoi farlo in modo brute force usando repmat:
C = A.^repmat(B, size(A, 1), 1)
Oppure potresti farlo in modo classico usando bsxfun, che internamente si occupa del passaggio repmat:
C = bsxfun(@(x,y) x.^y, A, B)
Quindi bsxfun ti fa risparmiare alcuni passaggi (non è necessario calcolare esplicitamente le dimensioni di A). Tuttavia, in alcuni miei test informali, risulta che repmat è circa il doppio più veloce se la funzione da applicare (come la mia funzione di alimentazione, sopra) è semplice. Quindi dovrai scegliere se vuoi semplicità o velocità.
Non posso commentare quanto sia efficiente, ma ecco una soluzione:
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)
Basandosi sulla risposta di Alex , ecco una funzione più generica:
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));
Ecco un confronto tra le due funzioni:
>> % 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))'
Per completezza / interesse vorrei aggiungere che matlab ha una funzione che ti consente di operare sui dati per riga piuttosto che per elemento. Si chiama rowfun
( http://www.mathworks.se/help/matlab/ref/rowfun.html ), ma l'unico "problema" è che opera su tabelle ( http://www.mathworks.se/help/ matlab / ref / table.html ) piuttosto che matrici .
Aggiungendo alla natura in evoluzione della risposta a questa domanda, a partire da r2016b, MATLAB espanderà implicitamente le dimensioni singleton, eliminando la necessità bsxfun
in molti casi.
Dalle note sulla versione r2016b :
Espansione implicita: applica operazioni e funzioni basate sugli elementi agli array con espansione automatica delle dimensioni di lunghezza 1
L'espansione implicita è una generalizzazione dell'espansione scalare. Con l'espansione scalare, uno scalare si espande per avere le stesse dimensioni di un altro array per facilitare le operazioni a livello di elemento. Con l'espansione implicita, gli operatori e le funzioni degli elementi elencati qui possono espandere implicitamente i loro input per avere la stessa dimensione, purché gli array abbiano dimensioni compatibili. Due array hanno dimensioni compatibili se, per ogni dimensione, le dimensioni delle dimensioni degli input sono uguali o una di esse è 1. Per ulteriori informazioni, vedere Dimensioni array compatibili per operazioni di base e Operazioni array vs.
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
Ad esempio, è possibile calcolare la media di ogni colonna in una matrice A, quindi sottrarre il vettore dei valori medi da ciascuna colonna con A - media (A).
In precedenza, questa funzionalità era disponibile tramite la funzione bsxfun. Si consiglia ora di sostituire la maggior parte degli usi di bsxfun con chiamate dirette alle funzioni e agli operatori che supportano l'espansione implicita. Rispetto all'utilizzo di bsxfun, l'espansione implicita offre una maggiore velocità, un migliore utilizzo della memoria e una migliore leggibilità del codice.
Nessuna delle risposte precedenti ha funzionato "fuori dagli schemi" per me, tuttavia, la seguente funzione, ottenuta copiando le idee delle altre risposte, funziona:
apply_func_2_cols = @(f,M) cell2mat(cellfun(f,num2cell(M,1), 'UniformOutput',0));
Prende una funzione f
e la applica a ogni colonna della matrice M
.
Quindi per esempio:
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
Con le versioni recenti di Matlab, puoi utilizzare la struttura dei dati della tabella a tuo vantaggio. C'è anche un'operazione 'rowfun' ma ho trovato più semplice farlo:
a = magic(6);
incrementRow = cell2mat(cellfun(@(x) x+1,table2cell(table(a)),'UniformOutput',0))
oppure eccone uno più vecchio che avevo che non richiede tabelle, per le versioni precedenti di Matlab.
dataBinner = cell2mat(arrayfun(@(x) Binner(a(x,:),2)',1:size(a,1),'UniformOutput',0)')
La risposta accettata sembra essere quella di convertire prima in celle e quindi utilizzare cellfun
per operare su tutte le celle. Non conosco l'applicazione specifica, ma in generale penso che utilizzare bsxfun
per operare sulla matrice sarebbe più efficiente. Fondamentalmente bsxfun
applica un'operazione elemento per elemento su due array. Quindi, se volessi moltiplicare ogni elemento in un n x 1
vettore per ogni elemento in un m x 1
vettore per ottenere un n x m
array, potresti usare:
vec1 = [ stuff ]; % n x 1 vector
vec2 = [ stuff ]; % m x 1 vector
result = bsxfun('times', vec1.', vec2);
Questo ti darà una matrice chiamata in result
cui la voce (i, j) sarà l'ennesimo elemento di vec1
moltiplicato per il jesimo elemento di vec2
.
Puoi usarlo bsxfun
per tutti i tipi di funzioni integrate e puoi dichiarare il tuo. La documentazione ha un elenco di molte funzioni incorporate, ma fondamentalmente puoi nominare qualsiasi funzione che accetta due array (vettore o matrice) come argomenti e farlo funzionare.
Sono incappato in questa domanda / risposta mentre cercavo come calcolare le somme di riga di una matrice.
Vorrei solo aggiungere che la funzione SUM di Matlab ha effettivamente il supporto per la somma per una data dimensione, cioè una matrice standard con due dimensioni.
Quindi per calcolare le somme delle colonne fai:
colsum = sum(M) % or sum(M, 1)
e per le somme di riga, fallo semplicemente
rowsum = sum(M, 2)
La mia scommessa è che questo è più veloce sia della programmazione di un ciclo for che della conversione in celle :)
Tutto questo può essere trovato nell'help di matlab per SUM.
se conosci la lunghezza delle tue righe puoi fare qualcosa del genere:
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) )