Как я могу применить функцию к каждой строке / столбцу матрицы в MATLAB? - PullRequest
98 голосов
/ 21 февраля 2010

Вы можете применить функцию к каждому элементу в векторе, сказав, например, v + 1, или вы можете использовать функцию arrayfun. Как я могу сделать это для каждой строки / столбца матрицы без использования цикла for?

Ответы [ 11 ]

68 голосов
/ 21 февраля 2010

Многие встроенные операции, такие как 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
24 голосов
/ 08 июля 2013

Возможно, вам понадобится более неясная функция Matlab bsxfun . Из документации Matlab, bsxfun "применяет бинарную операцию элемент за элементом, заданную функцией fun дескриптора функции, к массивам A и B с включенным одноэлементным расширением."

@ gnovice, как указано выше, сумма и другие базовые функции уже работают с первым не-одиночным измерением (т. Е. Строки, если имеется более одной строки, столбцы, если есть только одна строка, или более высокие измерения, если все нижние измерения имеют размер == 1). Однако bsxfun работает с любой функцией, включая (и особенно) пользовательские функции.

Например, допустим, у вас есть матрица A и вектор строки B. Например, скажем:

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 примерно в два раза быстрее, если функция, которую нужно применить (например, моя функция мощности, выше), проста. Так что вам нужно будет выбрать простоту или скорость.

19 голосов
/ 30 октября 2012

Я не могу комментировать, насколько это эффективно, но вот решение:

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 голосов
/ 12 апреля 2013

Опираясь на Ответ Алекса , вот более общая функция:

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 голосов
/ 06 января 2014

Для полноты картины / интереса я хотел бы добавить, что у matlab есть функция, которая позволяет вам работать с данными на строку, а не на элемент. Он называется rowfun (http://www.mathworks.se/help/matlab/ref/rowfun.html),, но единственная «проблема» заключается в том, что он работает с таблицами (http://www.mathworks.se/help/matlab/ref/table.html) вместо матриц .

3 голосов
/ 26 сентября 2016

В дополнение к эволюционному характеру ответа на этот вопрос, начиная с 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, неявное расширение предлагает более высокую скорость, лучшее использование памяти и улучшенная читаемость кода.

1 голос
/ 01 марта 2017

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

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 голос
/ 06 мая 2015

В последних версиях Matlab вы можете использовать структуру данных Table в своих интересах.Есть даже операция '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 голос
/ 08 июля 2013

Кажется, что принятым ответом является сначала преобразование в ячейки, а затем использование 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 для всех видов встроенных функций, и вы можете объявить свои собственные. В документации есть список многих встроенных функций, но в основном вы можете назвать любую функцию, которая принимает два массива (вектор или матрицу) в качестве аргументов, и заставить ее работать.

0 голосов
/ 29 августа 2013

если вы знаете длину ваших строк, вы можете сделать что-то вроде этого:

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