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?
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:
Nhiều hoạt động tích hợp sẵn như sum
và prod
đã 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 mat2cell
hoặ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
true = false
là 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 đó (:
sum(M, 1)
. Những người mới bắt đầu có thể nghĩ rằng sum
có 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
.
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.
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)
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))'
Để hoàn thiện / quan tâm, tôi muốn thêm rằng matlab có một chức năng cho phép bạn hoạt động trên dữ liệu trên mỗi hàng chứ không phải trên mỗi phần tử. Nó được gọi là rowfun
( http://www.mathworks.se/help/matlab/ref/rowfun.html ), nhưng "vấn đề" duy nhất là nó hoạt động trên các bảng ( http://www.mathworks.se/help/ matlab / ref / table.html ) chứ không phải ma trận .
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 bsxfun
trong nhiều trường hợp.
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 functions — bitand, bitor, bitxor Elementary math functions — max, 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ã.
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 f
và á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
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)')
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 1
vectơ với từng mục trong m x 1
vectơ để có được một n x m
mả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à result
trong đó mục nhập (i, j) sẽ là phần tử thứ i của vec1
nhân với phần tử thứ j của vec2
.
Bạn có thể sử dụng bsxfun
cho 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.
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.
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) )