Як я можу застосувати функцію до кожного рядка / стовпця матриці в MATLAB?


106

Ви можете застосувати функцію до кожного елемента у векторі, сказавши, наприклад v + 1, або ви можете використовувати функцію arrayfun. Як я можу це зробити для кожного рядка / стовпця матриці, не використовуючи цикл for?

Відповіді:


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
Я би перевірив ефективність цього підходу для будь-якого конкретного випадку проти простого циклу for, який може бути швидшим, ніж перетворення матриці в масив комірок. Для тестування використовуйте обгортку tic / tac.
юк

5
@yuk: Я думаю, ти мав на увазі "tic / toc". ;)
gnovice

4
@gnovice, можливо, юк зробив якусь магію і призначив tak = toc. Мовою, де true = falseє дійсне твердження, я впевнений, що ви можете це зробити (:
chessofnerd

1
@Argyll: Визначення того, який підхід є більш ефективним, залежатиме від того, яку функцію ви хотіли застосувати, розмір матриці тощо. Коротше кажучи, це, ймовірно, залежить від проблеми. Насправді іноді хороший старий для циклу може бути найшвидшим вибором.
gnovice

2
@gnovice, я пропоную змінити sum(M, 1). Початківці можуть подумати, що sumїх можна використовувати таким чином для матриць довільного розміру, а потім застрягти, коли матриця одного дня 1-by-n.
Стюі Гріффін

24

Можливо, ви хочете більш незрозумілу функцію Matlab bsxfun . З документації на Matlab, bsxfun "застосовує бінарні операції по елементам, визначені функцією обробки функції весело до масивів 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]

Ви можете зробити це брутальним шляхом, використовуючи репмат:

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

Або ви могли зробити це класним способом, використовуючи bsxfun, який внутрішньо піклується про крок репмати:

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

Таким чином, bsxfun економить кілька кроків (не потрібно чітко обчислювати розміри A). Однак у деяких неформальних тестах виявляється, що репмат приблизно вдвічі швидший, якщо функція, яка застосовується (як і моя функція живлення, вище), проста. Тож вам потрібно буде вибрати, чи хочете ви простоти чи швидкості.


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

Спираючись на відповідь Алекса , тут є більш загальна функція:

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 / ref / 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 - середнім (A).

Раніше ця функція була доступна через функцію bsxfun. Тепер рекомендується замінити більшість застосувань bsxfun прямими дзвінками до функцій та операторів, які підтримують неявне розширення. У порівнянні з використанням bsxfun, неявне розширення пропонує більш високу швидкість, краще використання пам’яті та покращену читабельність коду.


2

Жодна з перерахованих вище відповідей для мене не працювала "поза коробкою", проте працює наступна функція, отримана при копіюванні ідей інших відповідей:

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

Він бере функцію fі застосовує її до кожного стовпця матриці M.

Так, наприклад:

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де запис (i, j) буде i-м елементом, vec1помноженим на j-й елемент vec2.

Ви можете використовувати bsxfunдля всіляких вбудованих функцій, а ви можете заявити про власну. У документації є перелік багатьох вбудованих функцій, але в основному ви можете назвати будь-яку функцію, яка приймає два масиви (вектор або матрицю) як аргументи, і змушує її працювати.


-1

Натрапили на це питання / відповідь, шукаючи, як обчислити рядкові суми матриці.

Я хотів би лише додати, що функція SUM Matlab насправді підтримує підсумовування заданого виміру, тобто стандартну матрицю з двома вимірами.

Отже, для обчислення сум стовпців виконайте:

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

а для рядкових сум просто зробити

rowsum = sum(M, 2)

Моя обставина, це швидше, ніж програмування циклу для циклу та перетворення на комірки :)

Все це можна знайти у довідці з matlab для SUM.


7
можливість застосувати SUM уздовж заданого виміру згадувалася в першому реченні оригінальної відповіді на це питання. Потім відповідь пішла на розгляд випадку, коли можливість вибору виміру ще не вбудована у функцію. Ти маєш рацію, що використання вбудованих параметрів вибору розмірів - коли вони доступні - майже завжди швидше, ніж для циклу або перетворення на комірки.
cjh

Правда, що, однак, вищевказана відповідь повернула мене до документації про матлаб, оскільки мені не потрібна була вся ця фантазія, тому я просто хотів поділитися та врятувати інших, хто потребує простого рішення, від пошуку.
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!
Стюі Гріффін
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.