Bagaimana cara menerapkan fungsi ke setiap baris / kolom matriks di MATLAB?

106

Anda dapat menerapkan fungsi ke setiap item dalam vektor dengan mengucapkan, misalnya v + 1, atau Anda dapat menggunakan fungsi tersebut arrayfun. Bagaimana saya bisa melakukannya untuk setiap baris / kolom dari matriks tanpa menggunakan for loop?

FurtiveFelon
sumber

Jawaban:

73

Banyak operasi bawaan seperti sumdan prodsudah dapat beroperasi melintasi baris atau kolom, jadi Anda mungkin dapat memfaktor ulang fungsi yang Anda terapkan untuk memanfaatkan ini.

Jika itu bukan opsi yang layak, salah satu cara untuk melakukannya adalah dengan mengumpulkan baris atau kolom ke dalam sel menggunakan mat2cellatau num2cell, lalu gunakan cellfununtuk beroperasi pada larik sel yang dihasilkan.

Sebagai contoh, katakanlah Anda ingin menjumlahkan kolom dari sebuah matriks M. Anda dapat melakukannya hanya dengan menggunakan sum:

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

Dan inilah cara Anda melakukannya menggunakan opsi num2cell/ yang lebih rumit 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
sumber
17
Saya akan menguji kinerja pendekatan ini untuk kasus tertentu terhadap loop-for sederhana, yang mungkin lebih cepat kemudian mengubah matriks menjadi array sel. Gunakan tic / tac wrap untuk mengujinya.
yuk
5
@yuk: Saya pikir yang Anda maksud "tic / toc". ;)
gnovice
4
@gnovice, mungkin yuk melakukan sihir dan menetapkan tak = toc. Dalam bahasa true = falseyang pernyataannya valid, saya yakin ada cara untuk melakukannya (:
chessofnerd
1
@Argyll: Menentukan pendekatan mana yang lebih efisien akan bergantung pada jenis fungsi yang ingin Anda terapkan, ukuran matriks, dll. Singkatnya, ini mungkin tergantung pada masalah. Faktanya, terkadang loop for lama yang bagus bisa menjadi pilihan tercepat.
gnovice
2
@ pemula, saya menyarankan pengeditan ke sum(M, 1). Para pemula mungkin berpikir sumbahwa cara ini dapat digunakan untuk matriks berukuran arbitrer dan kemudian menjadi bingung ketika matriks tersebut suatu hari nanti 1-by-n.
Stewie Griffin
24

Anda mungkin menginginkan fungsi Matlab bsxfun yang lebih tidak jelas . Dari dokumentasi Matlab, bsxfun "menerapkan operasi biner elemen demi elemen yang ditentukan oleh fungsi handle fun ke array A dan B, dengan ekspansi tunggal diaktifkan."

@gnovice yang disebutkan di atas bahwa jumlah dan fungsi dasar lainnya sudah beroperasi pada dimensi non-tunggal pertama (yaitu, baris jika ada lebih dari satu baris, kolom jika hanya ada satu baris, atau dimensi yang lebih tinggi jika semua dimensi yang lebih rendah memiliki ukuran == 1 ). Namun, bsxfun berfungsi untuk fungsi apa pun, termasuk (dan terutama) fungsi yang ditentukan pengguna.

Misalnya, Anda memiliki matriks A dan vektor baris BEg, katakanlah:

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

Anda menginginkan fungsi power_by_col yang mengembalikan dalam vektor C semua elemen di A pangkat dari kolom B.

Dari contoh di atas, C adalah matriks 3x3:

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

yaitu,

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

Anda bisa melakukan ini dengan cara brute force menggunakan repmat:

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

Atau Anda dapat melakukan ini dengan cara berkelas menggunakan bsxfun, yang secara internal menangani langkah repmat:

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

Jadi bsxfun menghemat beberapa langkah (Anda tidak perlu menghitung dimensi A secara eksplisit). Namun, dalam beberapa tes informal saya, ternyata repmat kira-kira dua kali lebih cepat jika fungsi yang akan diterapkan (seperti fungsi power saya, di atas) sederhana. Jadi, Anda harus memilih apakah Anda menginginkan kesederhanaan atau kecepatan.

Daniel Golden
sumber
21

Saya tidak dapat berkomentar tentang seberapa efisien ini, tetapi inilah solusinya:

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
sumber
Jawaban yang lebih umum diberikan di sini .
Wajan
11

Berdasarkan jawaban Alex , berikut adalah fungsi yang lebih umum:

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

Berikut perbandingan antara kedua fungsi tersebut:

>> % 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))'
Wajan
sumber
4

Menambah sifat jawaban yang terus berkembang untuk pertanyaan ini, dimulai dengan r2016b, MATLAB secara implisit akan memperluas dimensi tunggal, menghilangkan kebutuhan bsxfundalam banyak kasus.

Dari catatan rilis r2016b :

Ekspansi Implisit: Menerapkan operasi dan fungsi bijak-elemen ke array dengan perluasan otomatis dimensi panjang 1

Ekspansi implisit adalah generalisasi dari ekspansi skalar. Dengan ekspansi skalar, skalar berkembang menjadi ukuran yang sama dengan larik lainnya untuk memfasilitasi operasi yang bijaksana. Dengan ekspansi implisit, operator dan fungsi dari segi elemen yang tercantum di sini dapat secara implisit memperluas input mereka ke ukuran yang sama, selama array memiliki ukuran yang kompatibel. Dua array memiliki ukuran yang kompatibel jika, untuk setiap dimensi, ukuran dimensi dari inputnya sama atau salah satunya adalah 1. Lihat Ukuran Array yang Kompatibel untuk Operasi Dasar dan Operasi Array vs. Matriks untuk informasi lebih lanjut.

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

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

Logical operators&, |, xor

Bit-wise functionsbitand, bitor, bitxor

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

Misalnya, Anda dapat menghitung rata-rata setiap kolom dalam matriks A, lalu mengurangi nilai rata-rata vektor dari setiap kolom dengan A - rata-rata (A).

Sebelumnya, fungsi ini tersedia melalui fungsi bsxfun. Sekarang Anda disarankan untuk mengganti sebagian besar penggunaan bsxfun dengan panggilan langsung ke fungsi dan operator yang mendukung perluasan implisit. Dibandingkan dengan menggunakan bsxfun, ekspansi implisit menawarkan kecepatan yang lebih cepat, penggunaan memori yang lebih baik, dan pembacaan kode yang lebih baik.

craigim.dll
sumber
2

Tak satu pun dari jawaban di atas bekerja "di luar kotak" bagi saya, namun, fungsi berikut, diperoleh dengan menyalin ide dari jawaban lain berfungsi:

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

Dibutuhkan fungsi fdan menerapkannya ke setiap kolom matriks M.

Jadi contohnya:

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
sumber
1

Dengan versi Matlab terbaru, Anda dapat menggunakan struktur data Tabel untuk keuntungan Anda. Bahkan ada operasi 'rowfun' tetapi saya merasa lebih mudah melakukan ini:

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

atau ini yang lebih lama yang saya miliki yang tidak memerlukan tabel, untuk versi Matlab yang lebih lama.

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

Jawaban yang diterima tampaknya mengubah menjadi sel terlebih dahulu dan kemudian digunakan cellfununtuk beroperasi di semua sel. Saya tidak tahu aplikasi spesifiknya, tetapi secara umum saya akan berpikir menggunakan bsxfununtuk mengoperasikan matriks akan lebih efisien. Pada dasarnya bsxfunmenerapkan operasi elemen-demi-elemen di dua larik. Jadi jika Anda ingin mengalikan setiap item dalam n x 1vektor dengan setiap item dalam m x 1vektor untuk mendapatkan n x marray, Anda dapat menggunakan:

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

Ini akan memberi Anda matriks yang disebut di resultmana entri (i, j) akan menjadi elemen ke-i vec1dikalikan dengan elemen ke-j vec2.

Anda dapat menggunakan bsxfununtuk semua jenis fungsi bawaan, dan Anda dapat mendeklarasikannya sendiri. Dokumentasi memiliki daftar banyak fungsi bawaan, tetapi pada dasarnya Anda dapat menamai fungsi apa pun yang menerima dua larik (vektor atau matriks) sebagai argumen dan membuatnya berfungsi.

Engineero
sumber
-1

Menemukan pertanyaan / jawaban ini sambil mencari cara menghitung jumlah baris dari sebuah matriks.

Saya hanya ingin menambahkan bahwa fungsi SUM Matlab sebenarnya memiliki dukungan untuk menjumlahkan dimensi tertentu, yaitu matriks standar dengan dua dimensi.

Jadi untuk menghitung jumlah kolom lakukan:

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

dan untuk jumlah baris, lakukan saja

rowsum = sum(M, 2)

Taruhan saya adalah ini lebih cepat daripada pemrograman for loop dan mengonversi ke sel :)

Semua ini dapat ditemukan di bantuan matlab untuk SUM.

nover
sumber
7
kemampuan untuk menerapkan SUM sepanjang dimensi tertentu disebutkan dalam kalimat pertama dari jawaban asli pertanyaan ini. Jawabannya kemudian berlanjut ke kasus ketika kemampuan untuk memilih dimensi belum dibangun ke dalam fungsi. Anda benar, meskipun, bahwa menggunakan opsi pemilihan dimensi bawaan - jika tersedia - hampir selalu lebih cepat daripada for loop atau mengonversi ke sel.
cjh
Benar bahwa, bagaimanapun, jawaban di atas mengirim saya kembali ke dokumentasi matlab, karena saya tidak membutuhkan semua kemewahan itu, jadi saya hanya ingin berbagi dan menyelamatkan orang lain, yang membutuhkan solusi sederhana, dari pencarian.
nover
-2

jika Anda mengetahui panjang baris Anda, Anda dapat membuat sesuatu seperti ini:

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
sumber
2
Kepada siapa pun yang melihat jawaban ini: Ini bukanlah cara untuk melakukannya! Ini bukanlah cara untuk melakukan apapun di MATLAB!
Stewie Griffin