ฉันจะใช้ฟังก์ชันกับทุกแถว / คอลัมน์ของเมทริกซ์ใน MATLAB ได้อย่างไร


106

คุณสามารถใช้ฟังก์ชั่นทุกรายการในเวกเตอร์โดยกล่าวว่าตัวอย่างเช่นv + 1, arrayfunหรือคุณสามารถใช้ฟังก์ชั่น ฉันจะทำมันสำหรับทุกแถว / คอลัมน์ของเมทริกซ์โดยไม่ต้องใช้ for loop ได้อย่างไร

คำตอบ:


73

การดำเนินการในตัวจำนวนมากเช่นsumและprodสามารถดำเนินการในแถวหรือคอลัมน์ได้อยู่แล้วดังนั้นคุณอาจสามารถปรับโครงสร้างฟังก์ชันที่คุณใช้เพื่อใช้ประโยชน์จากสิ่งนี้ได้

หากนั่นไม่ใช่ตัวเลือกที่ทำงานได้วิธีหนึ่งที่ทำได้คือรวบรวมแถวหรือคอลัมน์ลงในเซลล์โดยใช้mat2cellหรือnum2cellจากนั้นใช้cellfunเพื่อดำเนินการกับอาร์เรย์ของเซลล์ที่เป็นผลลัพธ์

Mตัวอย่างเช่นสมมติว่าคุณต้องการที่จะสรุปคอลัมน์ของเมทริกซ์ คุณสามารถทำได้ง่ายๆโดยใช้sum:

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

และนี่คือวิธีที่คุณจะทำได้โดยใช้ตัวเลือก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

17
ฉันจะทดสอบประสิทธิภาพของแนวทางนี้สำหรับกรณีใด ๆ โดยเฉพาะกับฟอร์ลูปแบบง่ายซึ่งอาจเร็วกว่าจากนั้นแปลงเมทริกซ์เป็นอาร์เรย์ของเซลล์ ใช้ tic / tac wrap เพื่อทดสอบ
จุ๊บ

5
@yuk: ฉันคิดว่าคุณหมายถึง "tic / toc" ;)
gnovice

4
@gnovice บางที yuk ก็ใช้เวทมนตร์และมอบหมายให้ tak = toc ในภาษาที่true = falseเป็นคำสั่งที่ถูกต้องฉันแน่ใจว่ามีวิธีที่คุณทำได้ (:
chessofnerd

1
@Argyll: การพิจารณาว่าแนวทางใดมีประสิทธิภาพมากกว่าจะขึ้นอยู่กับประเภทของฟังก์ชันที่คุณต้องการใช้ขนาดของเมทริกซ์ ฯลฯ ในระยะสั้นก็น่าจะขึ้นอยู่กับปัญหา ในความเป็นจริงบางครั้งลูปเก่าที่ดีอาจเป็นทางเลือกที่เร็วที่สุด
gnovice

2
@gnovice sum(M, 1)ผมขอแนะนำให้ไปแก้ไข เริ่มต้นอาจจะคิดว่าสามารถใช้วิธีนี้ได้โดยพลการฝึกอบรมขนาดใหญ่และได้รับแล้วนิ่งงันเมื่อเมทริกซ์หนึ่งในวันนี้คือsum 1-by-n
Stewie Griffin

24

คุณอาจต้องการที่คลุมเครือมากขึ้น Matlab ฟังก์ชั่นbsxfun จากเอกสาร Matlab bsxfun "ใช้การดำเนินการไบนารีองค์ประกอบโดยองค์ประกอบที่ระบุโดย fun handle ฟังก์ชันกับอาร์เรย์ A และ B โดยเปิดใช้งานการขยายแบบซิงเกิลตัน"

@gnovice ระบุไว้ข้างต้นว่าผลรวมและฟังก์ชันพื้นฐานอื่น ๆ ทำงานในมิติข้อมูลที่ไม่ใช่ซิงเกิลตันแรกอยู่แล้ว (เช่นแถวถ้ามีมากกว่าหนึ่งแถวคอลัมน์ถ้ามีเพียงแถวเดียวหรือมีมิติที่สูงกว่าหากมิติที่ต่ำกว่าทั้งหมดมีขนาด == 1 ). อย่างไรก็ตาม bsxfun ใช้งานได้กับฟังก์ชันใด ๆ รวมถึง (และโดยเฉพาะ) ฟังก์ชันที่ผู้ใช้กำหนดเอง

ตัวอย่างเช่นสมมติว่าคุณมีเมทริกซ์ A และเวกเตอร์แถว BEg สมมติว่า:

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

คุณต้องการฟังก์ชัน power_by_col ซึ่งส่งกลับเวกเตอร์ C องค์ประกอบทั้งหมดใน A เป็นพลังของคอลัมน์ที่เกี่ยวข้องของ B

จากตัวอย่างข้างต้น C คือเมทริกซ์ 3x3:

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

กล่าวคือ

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

คุณสามารถทำสิ่งนี้ด้วยวิธีดุร้ายโดยใช้ repmat:

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

หรือคุณสามารถทำวิธีนี้ได้อย่างดีเยี่ยมโดยใช้ bsxfun ซึ่งดูแลขั้นตอน repmat ภายใน:

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

ดังนั้น bsxfun จะช่วยคุณประหยัดขั้นตอนบางอย่าง (คุณไม่จำเป็นต้องคำนวณขนาดของ A อย่างชัดเจน) อย่างไรก็ตามในการทดสอบอย่างไม่เป็นทางการบางอย่างของฉันปรากฎว่า repmat นั้นเร็วกว่าประมาณสองเท่าหากฟังก์ชันที่จะใช้ (เช่นฟังก์ชันกำลังของฉันด้านบน) นั้นเรียบง่าย ดังนั้นคุณจะต้องเลือกว่าคุณต้องการความเรียบง่ายหรือความเร็ว


21

ฉันไม่สามารถแสดงความคิดเห็นว่าสิ่งนี้มีประสิทธิภาพเพียงใด แต่นี่คือวิธีแก้ปัญหา:

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)

คำตอบทั่วไปมากขึ้นจะได้รับที่นี่

11

จากคำตอบของ Alexนี่คือฟังก์ชั่นทั่วไปเพิ่มเติม:

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

นี่คือการเปรียบเทียบระหว่างสองฟังก์ชัน:

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

6

เพื่อความสมบูรณ์ / ความสนใจฉันต้องการเพิ่ม matlab นั้นมีฟังก์ชันที่อนุญาตให้คุณดำเนินการกับข้อมูลต่อแถวแทนที่จะเป็นต่อองค์ประกอบ เรียกว่าrowfun( http://www.mathworks.se/help/matlab/ref/rowfun.html ) แต่ "ปัญหา" เพียงอย่างเดียวคือการทำงานบนตาราง ( http://www.mathworks.se/help/ MATLAB / อ้างอิง / table.html ) มากกว่าการฝึกอบรม


4

การเพิ่มลักษณะการพัฒนาของคำตอบสำหรับคำถามนี้โดยเริ่มจาก r2016b MATLAB จะขยายมิติข้อมูลเดี่ยวโดยปริยายทำให้ไม่จำเป็นต้องมีbsxfunในหลาย ๆ กรณี

จากบันทึกประจำรุ่น r2016b :

การขยายโดยนัย: ใช้การดำเนินการและฟังก์ชันที่ชาญฉลาดขององค์ประกอบกับอาร์เรย์ที่มีการขยายขนาดความยาว 1 โดยอัตโนมัติ

การขยายโดยนัยเป็นลักษณะทั่วไปของการขยายสเกลาร์ ด้วยการขยายสเกลาร์สเกลาร์จะขยายให้มีขนาดเท่ากับอาร์เรย์อื่นเพื่ออำนวยความสะดวกในการดำเนินการตามองค์ประกอบ ด้วยการขยายโดยปริยายตัวดำเนินการและฟังก์ชันที่ชาญฉลาดขององค์ประกอบที่ระบุไว้ในที่นี้สามารถขยายอินพุตให้มีขนาดเท่ากันได้โดยปริยายตราบเท่าที่อาร์เรย์มีขนาดที่เข้ากันได้ อาร์เรย์สองอาร์เรย์มีขนาดที่เข้ากันได้ถ้าสำหรับทุกมิติขนาดของอินพุตเหมือนกันหรือหนึ่งในนั้นคือ 1 ดูขนาดอาร์เรย์ที่เข้ากันได้สำหรับการดำเนินการขั้นพื้นฐานและการดำเนินการอาร์เรย์เทียบกับเมทริกซ์สำหรับข้อมูลเพิ่มเติม

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

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

Logical operators&, |, xor

Bit-wise functionsbitand, bitor, bitxor

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

ตัวอย่างเช่นคุณสามารถคำนวณค่าเฉลี่ยของแต่ละคอลัมน์ในเมทริกซ์ A จากนั้นลบเวกเตอร์ของค่าเฉลี่ยออกจากแต่ละคอลัมน์ด้วย A - mean (A)

ก่อนหน้านี้ฟังก์ชันนี้พร้อมใช้งานผ่านฟังก์ชัน bsxfun ตอนนี้ขอแนะนำให้คุณแทนที่การใช้ bsxfun ส่วนใหญ่ด้วยการเรียกโดยตรงไปยังฟังก์ชันและตัวดำเนินการที่รองรับการขยายโดยปริยาย เมื่อเทียบกับการใช้ bsxfun การขยายโดยนัยให้ความเร็วที่เร็วขึ้นการใช้หน่วยความจำที่ดีขึ้นและการอ่านโค้ดที่ดีขึ้น


2

ไม่มีคำตอบใดที่ "นอกกรอบ" สำหรับฉันอย่างไรก็ตามฟังก์ชันต่อไปนี้ได้มาจากการคัดลอกแนวคิดของคำตอบอื่น ๆ ได้ผล:

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

มันต้องใช้ฟังก์ชั่นและนำไปใช้กับคอลัมน์ของเมทริกซ์ทุกfM

ตัวอย่างเช่น:

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

ด้วย Matlab เวอร์ชันล่าสุดคุณสามารถใช้โครงสร้างข้อมูลตารางเพื่อประโยชน์ของคุณ แม้จะมีการดำเนินการ 'rowfun' แต่ฉันพบว่ามันง่ายกว่าที่จะทำสิ่งนี้:

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

หรือนี่คือรุ่นเก่าที่ฉันมีซึ่งไม่ต้องใช้ตารางสำหรับ Matlab เวอร์ชันเก่า

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

1

คำตอบที่ได้รับการยอมรับดูเหมือนจะแปลงเป็นเซลล์ก่อนแล้วจึงใช้cellfunเพื่อดำเนินการกับเซลล์ทั้งหมด ฉันไม่รู้จักแอปพลิเคชันเฉพาะ แต่โดยทั่วไปฉันคิดว่าการใช้bsxfunเพื่อดำเนินการบนเมทริกซ์จะมีประสิทธิภาพมากกว่า โดยทั่วไปbsxfunจะใช้องค์ประกอบการดำเนินการต่อองค์ประกอบในสองอาร์เรย์ ดังนั้นหากคุณต้องการคูณแต่ละรายการในn x 1เวกเตอร์โดยแต่ละรายการในm x 1เวกเตอร์เพื่อให้ได้n x mอาร์เรย์คุณสามารถใช้:

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

ซึ่งจะทำให้คุณเมทริกซ์ที่เรียกว่าresultขัดแย้ง (ฉัน j) รายการจะเป็นองค์ประกอบที่ i ของคูณด้วยองค์ประกอบของvec1 jthvec2

คุณสามารถใช้bsxfunกับฟังก์ชันในตัวได้ทุกประเภทและคุณสามารถประกาศของคุณเองได้ เอกสารประกอบมีรายการฟังก์ชันในตัวมากมาย แต่โดยพื้นฐานแล้วคุณสามารถตั้งชื่อฟังก์ชันใด ๆ ที่รับอาร์เรย์สองอาร์เรย์ (เวกเตอร์หรือเมทริกซ์) เป็นอาร์กิวเมนต์และทำให้ทำงานได้


-1

สะดุดกับคำถาม / คำตอบนี้ในขณะที่ค้นหาวิธีคำนวณผลรวมแถวของเมทริกซ์

ฉันแค่อยากจะเพิ่มว่าจริงๆแล้วฟังก์ชัน SUM ของ Matlab รองรับการหาผลรวมสำหรับมิติที่กำหนดนั่นคือเมทริกซ์มาตรฐานที่มีสองมิติ

ดังนั้นในการคำนวณผลรวมคอลัมน์ให้ทำ:

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

และสำหรับผลรวมแถวก็ทำ

rowsum = sum(M, 2)

เดิมพันของฉันคือเร็วกว่าทั้งการเขียนโปรแกรมสำหรับลูปและการแปลงเป็นเซลล์ :)

ทั้งหมดนี้สามารถพบได้ในวิธีใช้ matlab สำหรับ SUM


7
ความสามารถในการนำ SUM ไปใช้กับมิติที่กำหนดได้ถูกกล่าวถึงในประโยคแรกของคำตอบเดิมสำหรับคำถามนี้ จากนั้นคำตอบก็จะกล่าวถึงกรณีนี้เมื่อความสามารถในการเลือกมิติข้อมูลยังไม่ได้สร้างขึ้นในฟังก์ชัน คุณพูดถูกว่าการใช้ตัวเลือกการเลือกมิติข้อมูลในตัวเมื่อพร้อมใช้งานมักจะเร็วกว่าการวนซ้ำหรือการแปลงเป็นเซลล์
cjh

จริงอยู่ที่คำตอบข้างต้นส่งฉันกลับไปที่เอกสาร matlab เนื่องจากฉันไม่ต้องการความหรูหราขนาดนั้นดังนั้นฉันแค่ต้องการแบ่งปันและช่วยชีวิตผู้อื่นที่ต้องการวิธีแก้ปัญหาง่ายๆจากการค้นหา
nover

-2

หากคุณทราบความยาวของแถวคุณสามารถสร้างสิ่งนี้ได้:

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
สำหรับทุกคนที่เห็นคำตอบนี้: นี่ไม่ใช่วิธีที่จะทำได้! นี่ไม่ใช่วิธีทำอะไรใน MATLAB!
Stewie Griffin
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.