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


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

11 92

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

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

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

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

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

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

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