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

98

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

Теги:
function
matrix
vectorization

11 ответов

67
Лучший ответ

Многие встроенные операции, такие как 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 Wrap для проверки.
  • 5
    @yuk: Я думаю, что вы имели в виду "Tic / Toc". ;)
Показать ещё 8 комментариев
23

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

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

18

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

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)
  • 0
    Более общий ответ дается здесь .
11

Основываясь на Alex answer, вот более общая функция:

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), а не матрицы.

3

Добавляя к эволюционирующему характеру ответа на этот вопрос, начиная с r2016b, MATLAB будет неявно расширять размеры Singleton, устраняя необходимость во bsxfun во многих случаях.

Из примечания к выпуску r2016b:

Неявное расширение: применяйте элементарные операции и функции к массивам с автоматическим расширением размеров длины 1

Неявное расширение является обобщением скалярного разложения. С скалярное расширение, скаляр расширяется до того же размера, что и другой массив для облегчения действий по элементам. При неявном расширении, элементарные операторы и функции, перечисленные здесь, могут неявно расширяйте свои входы до того же размера, если массивы имеют совместимые размеры. Два массива имеют совместимые размеры, если для каждого размерность размеров входов либо одинакова, либо один из них - 1. См. раздел Совместимые размеры массивов для основных операций и Array vs. Matrix Operations для получения дополнительной информации.

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

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 применяет операцию поэлементно к двум массивам. Поэтому, если вы хотите умножить каждый элемент в векторе nx 1 на каждый элемент в векторе mx 1 чтобы получить массив nxm, вы можете использовать:

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

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

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 SUM фактически поддерживает подведение для данного измерения, т.е. стандартную матрицу с двумя измерениями.

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

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

и для сумм строк просто do

rowsum = sum(M, 2)

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

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

  • 7
    Возможность применения СУММ по заданному измерению упоминалась в первом предложении оригинального ответа на этот вопрос. Затем был дан ответ на случай, когда возможность выбора измерения еще не встроена в функцию. Вы правы, однако, что использование встроенных опций выбора размеров - когда они доступны - почти всегда быстрее, чем цикл for или преобразование в ячейки.
  • 0
    Правда, ответ, приведенный выше, вернул меня обратно к документации на matlab, поскольку мне не требовалась вся эта фантазия, поэтому я просто хотел поделиться и спасти других, нуждающихся в простом решении, от поиска.
-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) )
  • 1
    Для всех, кто видит этот ответ: это не способ сделать это! Это не способ сделать что-нибудь в MATLAB!

Ещё вопросы

Сообщество Overcoder
Наверх
Меню