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

106

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

FurtiveFelon
источник

Ответы:

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, который может быть быстрее, чем преобразование матрицы в массив ячеек. Для проверки используйте пленку крестики-нолики.
yuk
5
@yuk: Я думаю, ты имел в виду "тик-так". ;)
gnovice
4
@gnovice, возможно, юк совершил какое-то чудо и назначил tak = toc. true = falseЯ уверен, что на языке, где есть допустимое утверждение, вы могли бы это сделать (:
chessofnerd
1
@Agyll: Определение того, какой подход более эффективен, будет зависеть от того, какую функцию вы хотели применить, размера матрицы и т. Д. Короче говоря, это, вероятно, зависит от проблемы. Фактически, иногда самым быстрым выбором может быть старый добрый цикл for.
gnovice
2
@gnovice, я предлагаю отредактировать sum(M, 1). Новички могут подумать, что sumможно использовать этот способ для матриц произвольного размера, а затем запутаются, когда матрица однажды станет такой же 1-by-n.
Stewie Griffin
24

Возможно, вам понадобится более непонятная функция Matlab bsxfun . Из документации Matlab, bsxfun «применяет бинарную операцию поэлементно, заданную дескриптором функции fun, к массивам 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]

Вы можете сделать это методом грубой силы, используя repmat:

C = A.^repmat(B, size(A, 1), 1)

Или вы можете сделать это классным способом, используя bsxfun, который внутренне заботится о шаге repmat:

C = bsxfun(@(x,y) x.^y, A, B)

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

Дэниел Голден
источник
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)
Alex
источник
Более общий ответ дан здесь .
Wok
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 functionsbitand, bitor, bitxor

Elementary math functionsmax, min, mod, rem, hypot, atan2, atan2d

Например, вы можете вычислить среднее значение каждого столбца в матрице A, а затем вычесть вектор средних значений из каждого столбца с помощью A - mean (A).

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

Craigim
источник
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
patapouf_ai
источник
1

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

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

Engineero
источник
-1

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

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

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

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

а для сумм строк просто выполните

rowsum = sum(M, 2)

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

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

новичок
источник
7
Возможность применять СУММ по заданному измерению упоминалась в первом предложении исходного ответа на этот вопрос. Затем ответ был направлен на случай, когда возможность выбора измерения еще не встроена в функцию. Однако вы правы в том, что использование встроенных параметров выбора размеров - когда они доступны - почти всегда быстрее, чем цикл for или преобразование в ячейки.
cjh
Верно, однако, что ответ выше отправил меня обратно в документацию по Matlab, поскольку мне не нужны были все эти причуды, поэтому я просто хотел поделиться и спасти других, нуждающихся в простом решении, от поиска.
ноябрь 05
-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!
Stewie Griffin