Làm cách nào để áp dụng một hàm cho mọi hàng / cột của ma trận trong MATLAB?


106

Ví dụ: bạn có thể áp dụng một hàm cho mọi mục trong một vectơ bằng cách nói v + 1, hoặc bạn có thể sử dụng hàm arrayfun. Làm cách nào tôi có thể làm điều đó cho mọi hàng / cột của ma trận mà không sử dụng vòng lặp for?

Câu trả lời:


73

Nhiều hoạt động tích hợp sẵn như sumprodđã có thể hoạt động trên các hàng hoặc cột, vì vậy bạn có thể cấu trúc lại chức năng bạn đang áp dụng để tận dụng lợi thế này.

Nếu đó không phải là một tùy chọn khả thi, một cách để thực hiện là thu thập các hàng hoặc cột vào các ô bằng cách sử dụng mat2cellhoặc num2cell, sau đó sử dụng cellfunđể hoạt động trên mảng ô kết quả.

Ví dụ, giả sử bạn muốn tính tổng các cột của ma trận M. Bạn có thể làm điều này đơn giản bằng cách sử dụng sum:

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

Và đây là cách bạn thực hiện việc này bằng cách sử dụng tùy chọn num2cell/ phức tạp hơn 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

17
Tôi sẽ kiểm tra hiệu suất của phương pháp này cho bất kỳ trường hợp cụ thể nào đối với vòng lặp for đơn giản, điều này có thể nhanh hơn khi chuyển đổi ma trận thành mảng ô. Sử dụng bọc tic / tac để kiểm tra.
yuk

5
@yuk: Tôi nghĩ ý bạn là "tic / toc". ;)
gnovice

4
@gnovice, có lẽ yuk đã làm một phép thuật nào đó và gán tak = toc. Trong một ngôn ngữ mà true = falselà một tuyên bố hợp lệ, tôi chắc chắn có một cách bạn có thể làm điều đó (:
chessofnerd

1
@Argyll: Việc xác định cách tiếp cận nào hiệu quả hơn sẽ phụ thuộc vào loại chức năng bạn muốn áp dụng, kích thước của ma trận, v.v. Tóm lại, nó có thể phụ thuộc vào vấn đề. Trên thực tế, đôi khi một vòng lặp for cũ tốt có thể là lựa chọn nhanh nhất.
gnovice

2
@gnovice, tôi đề nghị chỉnh sửa thành sum(M, 1). Những người mới bắt đầu có thể nghĩ rằng sumcó thể sử dụng theo cách này cho các ma trận có kích thước tùy ý và sau đó bị bối rối khi ma trận một ngày nào đó 1-by-n.
Stewie Griffin

24

Bạn có thể muốn hàm Matlab tối nghĩa hơn bsxfun . Từ tài liệu Matlab, bsxfun "áp dụng phép toán nhị phân từng phần tử được chỉ định bởi hàm xử lý vui nhộn cho mảng A và B, có bật tính năng mở rộng singleton."

@gnovice đã nói ở trên rằng tính tổng và các hàm cơ bản khác đã hoạt động trên thứ nguyên không phải singleton đầu tiên (tức là hàng nếu có nhiều hơn một hàng, cột nếu chỉ có một hàng hoặc kích thước cao hơn nếu các thứ nguyên thấp hơn đều có kích thước == 1 ). Tuy nhiên, bsxfun hoạt động cho bất kỳ chức năng nào, bao gồm (và đặc biệt) các chức năng do người dùng xác định.

Ví dụ: giả sử bạn có một ma trận A và một vectơ hàng BEg, giả sử:

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

Bạn muốn một hàm power_by_col trả về trong vectơ C tất cả các phần tử trong A thành lũy thừa của cột B tương ứng.

Từ ví dụ trên, C là ma trận 3x3:

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

I E,

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

Bạn có thể làm điều này theo cách bạo lực bằng cách sử dụng repmat:

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

Hoặc bạn có thể làm điều này theo cách cổ điển bằng cách sử dụng bsxfun, công cụ này sẽ thực hiện bước repmat bên trong:

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

Vì vậy, bsxfun tiết kiệm cho bạn một số bước (bạn không cần phải tính toán rõ ràng các kích thước của A). Tuy nhiên, trong một số thử nghiệm không chính thức của tôi, hóa ra là repmat nhanh gấp đôi nếu hàm được áp dụng (như hàm power của tôi, ở trên) là đơn giản. Vì vậy, bạn sẽ cần phải chọn xem bạn muốn đơn giản hay nhanh chóng.


21

Tôi không thể bình luận về mức độ hiệu quả của điều này, nhưng đây là một giải pháp:

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)

Một câu trả lời chung chung hơn được đưa ra ở đây .
Wok

11

Dựa trên câu trả lời của Alex , đây là một hàm chung chung hơn:

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

Dưới đây là so sánh giữa hai chức năng:

>> % 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))'


4

Thêm vào bản chất đang phát triển của câu trả lời cho câu hỏi này, bắt đầu với r2016b, MATLAB sẽ mở rộng ngầm các kích thước singleton, loại bỏ sự cần thiết bsxfuntrong nhiều trường hợp.

Từ ghi chú phát hành r2016b :

Mở rộng ngầm định: Áp dụng các hoạt động và hàm khôn ngoan của phần tử cho các mảng có tự động mở rộng kích thước có độ dài 1

Khai triển ngầm định là một tổng quát của khai triển vô hướng. Với mở rộng vô hướng, một đại lượng vô hướng sẽ mở rộng để có cùng kích thước với một mảng khác để tạo điều kiện thuận lợi cho các hoạt động thông minh của phần tử. Với sự mở rộng ngầm định, các toán tử và hàm khôn ngoan phần tử được liệt kê ở đây có thể ngầm định mở rộng đầu vào của chúng để có cùng kích thước, miễn là các mảng có kích thước tương thích. Hai mảng có kích thước tương thích nếu, đối với mọi thứ nguyên, kích thước thứ nguyên của các đầu vào giống nhau hoặc một trong số chúng là 1. Xem Kích thước mảng tương thích cho các phép toán cơ bản và phép toán mảng so với ma trận để biết thêm thông tin.

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

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

Logical operators&, |, xor

Bit-wise functionsbitand, bitor, bitxor

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

Ví dụ: bạn có thể tính giá trị trung bình của mỗi cột trong ma trận A, sau đó trừ vectơ giá trị trung bình của mỗi cột với A - mean (A).

Trước đây, chức năng này có sẵn thông qua hàm bsxfun. Bây giờ bạn nên thay thế hầu hết việc sử dụng bsxfun bằng các lệnh gọi trực tiếp đến các hàm và toán tử hỗ trợ mở rộng ngầm định. So với việc sử dụng bsxfun, mở rộng ngầm cung cấp tốc độ nhanh hơn, sử dụng bộ nhớ tốt hơn và cải thiện khả năng đọc mã.


2

Không có câu trả lời nào ở trên hoạt động "vượt trội" đối với tôi, tuy nhiên, chức năng sau, có được bằng cách sao chép ý tưởng của các câu trả lời khác hoạt động:

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

Nó nhận một hàm fvà áp dụng nó cho mọi cột của ma trậnM .

Ví dụ:

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

1

Với các phiên bản gần đây của Matlab, bạn có thể sử dụng cấu trúc dữ liệu Bảng để có lợi cho mình. Thậm chí còn có một hoạt động 'đánh hàng' nhưng tôi thấy nó dễ dàng hơn khi làm điều này:

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

hoặc đây là một cái cũ hơn mà tôi đã có không yêu cầu bảng, dành cho các phiên bản Matlab cũ hơn.

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

1

Câu trả lời được chấp nhận dường như là chuyển đổi thành các ô trước và sau đó sử dụng cellfunđể hoạt động trên tất cả các ô. Tôi không biết ứng dụng cụ thể, nhưng nói chung, tôi nghĩ rằng sử dụng bsxfunđể vận hành trên ma trận sẽ hiệu quả hơn. Về cơ bản bsxfunáp dụng một phép toán từng phần tử trên hai mảng. Vì vậy, nếu bạn muốn nhân từng mục trong một n x 1vectơ với từng mục trong m x 1vectơ để có được một n x mmảng, bạn có thể sử dụng:

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

Điều này sẽ cung cấp cho bạn ma trận được gọi là resulttrong đó mục nhập (i, j) sẽ là phần tử thứ i của vec1nhân với phần tử thứ j của vec2.

Bạn có thể sử dụng bsxfuncho tất cả các loại chức năng cài sẵn và bạn có thể khai báo chức năng của riêng mình. Tài liệu có danh sách nhiều hàm tích hợp sẵn, nhưng về cơ bản bạn có thể đặt tên cho bất kỳ hàm nào chấp nhận hai mảng (vectơ hoặc ma trận) làm đối số và làm cho nó hoạt động.


-1

Tình cờ gặp câu hỏi / câu trả lời này khi đang tìm cách tính tổng hàng của ma trận.

Tôi chỉ muốn nói thêm rằng hàm SUM của Matlab thực sự hỗ trợ tính tổng cho một thứ nguyên nhất định, tức là một ma trận chuẩn có hai thứ nguyên.

Vì vậy, để tính toán các tổng cột làm:

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

và đối với tổng của hàng, chỉ cần làm

rowsum = sum(M, 2)

Cá cược của tôi là điều này nhanh hơn cả lập trình vòng lặp for và chuyển đổi thành các ô :)

Tất cả điều này có thể được tìm thấy trong trợ giúp matlab cho SUM.


7
khả năng áp dụng SUM dọc theo một thứ nguyên nhất định đã được đề cập trong câu đầu tiên của câu trả lời ban đầu cho câu hỏi này. Câu trả lời sau đó tiếp tục giải quyết trường hợp khi khả năng chọn thứ nguyên không được tích hợp sẵn trong hàm. Tuy nhiên, bạn đã đúng khi sử dụng các tùy chọn chọn thứ nguyên tích hợp sẵn - khi chúng có sẵn - hầu như luôn nhanh hơn vòng lặp for hoặc chuyển đổi thành ô.
cjh

Đúng là, tuy nhiên, câu trả lời ở trên đã đưa tôi quay trở lại tài liệu về matlab, vì tôi không cần tất cả những điều kỳ diệu đó, vì vậy tôi chỉ muốn chia sẻ và cứu những người khác, cần giải pháp đơn giản, khỏi tìm kiếm.
nover

-2

nếu bạn biết độ dài của các hàng của mình, bạn có thể tạo ra một cái gì đó như sau:

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

2
Đối với bất kỳ ai nhìn thấy câu trả lời này: Đây không phải là cách để làm điều đó! Đây không phải là cách để làm bất cứ điều gì trong MATLAB!
Stewie Griffin
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.