Jak mogę zastosować funkcję do każdego wiersza / kolumny macierzy w MATLAB?

106

Możesz zastosować funkcję do każdego elementu w wektorze, mówiąc na przykład v + 1, lub możesz użyć funkcji arrayfun. Jak mogę to zrobić dla każdego wiersza / kolumny macierzy bez używania pętli for?

FurtiveFelon
źródło

Odpowiedzi:

73

Wiele wbudowanych operacji, takich jak sumi prodjest już w stanie działać w wierszach lub kolumnach, więc możesz być w stanie refaktoryzować funkcję, którą stosujesz, aby to wykorzystać.

Jeśli nie jest to wykonalna opcja, jednym ze sposobów jest zebranie wierszy lub kolumn do komórek za pomocą mat2celllub num2cell, a następnie użycie cellfunoperacji na wynikowej tablicy komórek.

Jako przykład, powiedzmy, że chcesz zsumować kolumny macierzy M. Możesz to zrobić po prostu za pomocą sum:

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

A oto, jak byś to zrobił, używając bardziej skomplikowanej opcji 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
gnovice
źródło
17
Testowałbym wydajność tego podejścia dla każdego konkretnego przypadku z prostą pętlą for, która może być szybsza niż konwersja macierzy na macierz komórek. Do testu użyj zawijania tic / tac.
fuj,
5
@yuk: Myślę, że miałeś na myśli „tic / toc”. ;)
gnovice
4
@gnovice, być może yuk zrobił trochę magii i przypisał tak = toc. W języku, w którym true = falsejest ważne stwierdzenie, jestem pewien, że jest to sposób w jaki można to zrobić (:
chessofnerd
1
@Argyll: Określenie, które podejście jest bardziej wydajne, będzie zależeć od rodzaju funkcji, którą chcesz zastosować, rozmiaru macierzy itp. Krótko mówiąc, prawdopodobnie zależy to od problemu. W rzeczywistości czasami najszybszym wyborem może być dobra stara pętla for.
gnovice
2
@gnovice, proponuję edycję sum(M, 1). Początkujący mogą pomyśleć, że summożna go użyć w ten sposób do macierzy o dowolnej wielkości, a potem wpadną w zakłopotanie, gdy macierz pewnego dnia jest 1-by-n.
Stewie Griffin
24

Możesz potrzebować bardziej niejasnej funkcji Matlab bsxfun . Z dokumentacji Matlaba, bsxfun „stosuje operację binarną element po elemencie określoną przez funkcję uchwyt fun do tablic A i B, z włączoną ekspansją pojedynczych elementów”.

@gnovice stwierdził powyżej, że suma i inne podstawowe funkcje już działają na pierwszym nie-pojedynczym wymiarze (tj. wiersze, jeśli jest więcej niż jeden wiersz, kolumny, jeśli jest tylko jeden wiersz, lub wyższe wymiary, jeśli wszystkie niższe wymiary mają rozmiar == 1 ). Jednak bsxfun działa dla każdej funkcji, w tym (a zwłaszcza) funkcji zdefiniowanych przez użytkownika.

Na przykład, powiedzmy, że masz macierz A i wektor wierszowy BEg, powiedzmy:

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

Potrzebujesz funkcji power_by_col, która zwraca w wektorze C wszystkie elementy w A do potęgi odpowiedniej kolumny B.

Z powyższego przykładu C to macierz 3x3:

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

to znaczy,

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

Możesz to zrobić brutalną siłą używając repmat:

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

Lub możesz to zrobić w klasyczny sposób za pomocą bsxfun, który wewnętrznie zajmuje się krokiem repmat:

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

Więc bsxfun oszczędza ci kilku kroków (nie musisz jawnie obliczać wymiarów A). Jednak w niektórych moich nieformalnych testach okazuje się, że repmat jest mniej więcej dwa razy szybszy, jeśli funkcja, która ma zostać zastosowana (jak moja funkcja potęgowa, powyżej) jest prosta. Musisz więc wybrać, czy chcesz prostoty, czy szybkości.

Daniel Golden
źródło
21

Nie mogę komentować, jak wydajne jest to, ale oto rozwiązanie:

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
źródło
Tutaj podano bardziej ogólną odpowiedź .
Wok
11

Opierając się na odpowiedzi Alexa , oto bardziej ogólna funkcja:

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

Oto porównanie między tymi dwiema funkcjami:

>> % 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
źródło
4

Dodając do ewoluującego charakteru odpowiedzi na to pytanie, począwszy od r2016b, MATLAB niejawnie rozszerzy pojedyncze wymiary, eliminując potrzebę bsxfun w wielu przypadkach.

Z informacji o wersji r2016b :

Niejawna ekspansja: zastosuj operacje i funkcje elementarne do tablic z automatycznym rozszerzaniem wymiarów długości 1

Niejawna ekspansja jest uogólnieniem ekspansji skalarnej. Przy rozszerzaniu skalarnym skalar rozwija się do tego samego rozmiaru co inna tablica, aby ułatwić operacje na elementach. W przypadku niejawnego rozwinięcia wymienione tutaj operatory i funkcje elementarne mogą niejawnie rozszerzyć swoje dane wejściowe, aby miały ten sam rozmiar, o ile tablice mają zgodne rozmiary. Dwie tablice mają zgodne rozmiary, jeśli dla każdego wymiaru rozmiary wymiarów wejściowych są takie same lub jeden z nich wynosi 1. Aby uzyskać więcej informacji, zobacz Zgodne rozmiary tablic dla operacji podstawowych i Operacje na macierzach i macierzach.

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

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

Logical operators&, |, xor

Bit-wise functionsbitand, bitor, bitxor

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

Na przykład możesz obliczyć średnią z każdej kolumny w macierzy A, a następnie odjąć wektor średnich wartości z każdej kolumny za pomocą A - średnia (A).

Wcześniej ta funkcjonalność była dostępna za pośrednictwem funkcji bsxfun. Obecnie zaleca się zastąpienie większości zastosowań bsxfun bezpośrednimi wywołaniami funkcji i operatorów obsługujących niejawne rozwinięcie. W porównaniu z używaniem bsxfun, niejawna ekspansja zapewnia większą prędkość, lepsze wykorzystanie pamięci i lepszą czytelność kodu.

Craigim
źródło
2

Żadna z powyższych odpowiedzi nie zadziałała dla mnie „po wyjęciu z pudełka”, jednak następująca funkcja, uzyskana poprzez skopiowanie pomysłów z innych odpowiedzi, działa:

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

Pobiera funkcję fi stosuje ją do każdej kolumny macierzyM .

Na przykład:

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
źródło
1

Dzięki najnowszym wersjom Matlab możesz wykorzystać strukturę danych tabeli na swoją korzyść. Jest nawet operacja typu `` rowfun '', ale okazało się, że łatwiej jest to zrobić:

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

lub tutaj jest starszy, który miałem, który nie wymaga tabel, dla starszych wersji Matlab.

dataBinner = cell2mat(arrayfun(@(x) Binner(a(x,:),2)',1:size(a,1),'UniformOutput',0)')
Jordania
źródło
1

Zaakceptowaną odpowiedzią wydaje się być najpierw konwersja na komórki, a następnie użycie cellfundo operacji na wszystkich komórkach. Nie znam konkretnej aplikacji, ale generalnie myślę, że użycie bsxfundo operowania na matrycy byłoby bardziej wydajne. Zasadniczo bsxfunstosuje operację element po elemencie w dwóch tablicach. Więc jeśli chcesz pomnożyć każdy element w n x 1wektorze przez każdy element w m x 1wektorze, aby uzyskać n x mtablicę, możesz użyć:

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

To da ci macierz o nazwie, w resultktórej pozycja (i, j) będzie i-tym elementem vec1pomnożonej przez j-ty element funkcjivec2 .

Możesz używać bsxfundo wszelkiego rodzaju wbudowanych funkcji i możesz zadeklarować własne. Dokumentacja zawiera listę wielu wbudowanych funkcji, ale w zasadzie możesz nazwać dowolną funkcję, która przyjmuje dwie tablice (wektor lub macierz) jako argumenty i uruchomić ją.

Engineero
źródło
-1

Natknąłem się na to pytanie / odpowiedź, szukając sposobu obliczenia sum wierszy macierzy.

Chciałbym tylko dodać, że funkcja SUMA Matlaba faktycznie obsługuje sumowanie dla danego wymiaru, czyli standardowej macierzy z dwoma wymiarami.

Aby obliczyć sumy kolumn, wykonaj:

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

a dla sum wierszy po prostu zrób

rowsum = sum(M, 2)

Założę się, że jest to szybsze niż programowanie pętli for i konwersja do komórek :)

Wszystko to można znaleźć w pomocy Matlab dla programu SUM.

nover
źródło
7
możliwość zastosowania SUMA wzdłuż danego wymiaru została wspomniana w pierwszym zdaniu pierwotnej odpowiedzi na to pytanie. Następnie odpowiedź dotyczyła przypadku, gdy możliwość wyboru wymiaru nie jest już wbudowana w funkcję. Masz jednak rację, że używanie wbudowanych opcji wyboru wymiarów - jeśli są dostępne - jest prawie zawsze szybsze niż pętla for lub konwersja do komórek.
cjh
To prawda, że ​​jednak powyższa odpowiedź odesłała mnie z powrotem do dokumentacji Matlab, ponieważ nie potrzebowałem całej tej fantazji, więc chciałem tylko udostępnić i uratować innych, którzy potrzebują prostego rozwiązania, od wyszukiwania.
nover
-2

jeśli znasz długość swoich rzędów, możesz zrobić coś takiego:

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
źródło
2
Do każdego, kto widzi tę odpowiedź: To nie jest sposób na zrobienie tego! To nie jest sposób na zrobienie czegokolwiek w MATLAB!
Stewie Griffin