Як я можу застосувати функцію до кожного рядка / стовпця матриці в 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 functions — bitand, bitor, bitxor

Elementary math functions — max, 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.