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

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

11 ответов


многие встроенные операции, такие как 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

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

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

например, предположим, что у вас есть матрица A и вектор строки B. E. g., скажем:

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


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

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)

дом на Алекс, вот более общая функция:

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

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


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

ранее эта функция была доступна через функцию bsxfun. Теперь рекомендуется заменить большинство применений bsxfun на direct вызовы функций и операторов, поддерживающих неявное расширение. По сравнению с использованием bsxfun, неявный расширение предлагает более быструю скорость, лучшее использование памяти и улучшенная читаемость кода.


в последних версиях 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)')

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


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

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

наткнулся на этот вопрос / ответ, ища, как вычислить суммы строк матрицы.

Я просто хотел бы добавить, что функция SUM Matlab фактически поддерживает суммирование для данного измерения, i.e стандартная матрица с двумя измерениями.

Итак, чтобы вычислить суммы столбцов, сделайте:

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

и для сумм строк просто сделайте

rowsum = sum(M, 2)

моя ставка заключается в том, что это быстрее, чем программирование цикла for и преобразование в ячейки :)

все это можно найти в справке matlab для SUM.


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

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