¿Cómo puedo aplicar una función a cada fila / columna de una matriz en MATLAB?

106

Puede aplicar una función a cada elemento en un vector diciendo, por ejemplo v + 1,, o puede usar la función arrayfun. ¿Cómo puedo hacerlo para cada fila / columna de una matriz sin usar un bucle for?

FurtiveFelon
fuente

Respuestas:

73

Muchas operaciones integradas como sumy prodya pueden operar en filas o columnas, por lo que es posible que pueda refactorizar la función que está aplicando para aprovechar esto.

Si esa no es una opción viable, una forma de hacerlo es recopilar las filas o columnas en celdas usando mat2cello num2cell, luego usar cellfunpara operar en la matriz de celdas resultante.

Como ejemplo, digamos que desea sumar las columnas de una matriz M. Puede hacer esto simplemente usando sum:

M = magic(10);           %# A 10-by-10 matrix
columnSums = sum(M, 1);  %# A 1-by-10 vector of sums for each column

Y así es como lo haría usando la opción num2cell/ más complicada 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
gnovice
fuente
17
Probaría el rendimiento de este enfoque para cualquier caso particular contra un bucle for simple, que podría ser más rápido que convertir una matriz en una matriz de celdas. Utilice tic / tac wrap para probar.
yuk
5
@yuk: Creo que quisiste decir "tic / toc". ;)
gnovice
4
@gnovice, quizás yuk hizo algo de magia y asignó tak = toc. En un idioma donde true = falsehay una declaración válida, estoy seguro de que hay una forma de hacerlo (:
chessofnerd
1
@Argyll: Determinar qué enfoque es más eficiente dependerá del tipo de función que desee aplicar, el tamaño de la matriz, etc. En resumen, es probable que dependa del problema. De hecho, a veces un buen bucle for puede ser la opción más rápida.
gnovice
2
@gnovice, sugiero una edición para sum(M, 1). Los principiantes pueden pensar que sumse puede usar de esta manera para matrices de tamaño arbitrario y luego quedar perplejos cuando la matriz lo sea un día 1-by-n.
Stewie Griffin
24

Es posible que desee la función bsxfun de Matlab, más oscura . De la documentación de Matlab, bsxfun "aplica la operación binaria elemento por elemento especificada por la función handle fun a las matrices A y B, con la expansión singleton habilitada".

@gnovice indicó anteriormente que la suma y otras funciones básicas ya operan en la primera dimensión que no es singleton (es decir, filas si hay más de una fila, columnas si solo hay una fila, o dimensiones más altas si todas las dimensiones inferiores tienen tamaño == 1 ). Sin embargo, bsxfun funciona para cualquier función, incluidas (y especialmente) las funciones definidas por el usuario.

Por ejemplo, digamos que tiene una matriz A y un vector fila BEg, digamos:

A = [1 2 3;
     4 5 6;
     7 8 9]
B = [0 1 2]

Quieres una función power_by_col que devuelva en un vector C todos los elementos en A a la potencia de la columna correspondiente de B.

En el ejemplo anterior, C es una matriz de 3x3:

C = [1^0 2^1 3^2;
     4^0 5^1 6^2;
     7^0 8^1 9^2]

es decir,

C = [1 2 9;
     1 5 36;
     1 8 81]

Podrías hacer esto por la fuerza bruta usando repmat:

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

O puede hacer esto de la manera elegante usando bsxfun, que internamente se encarga del paso repmat:

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

Entonces, bsxfun le ahorra algunos pasos (no necesita calcular explícitamente las dimensiones de A). Sin embargo, en algunas pruebas informales mías, resulta que repmat es aproximadamente el doble de rápido si la función que se va a aplicar (como mi función de potencia, arriba) es simple. Por lo tanto, deberá elegir si desea simplicidad o velocidad.

Daniel dorado
fuente
21

No puedo comentar qué tan eficiente es esto, pero aquí hay una solución:

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
fuente
Aquí se da una respuesta más genérica .
Wok
11

Sobre la base de la respuesta de Alex , aquí hay una función más genérica:

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

Aquí hay una comparación entre las dos funciones:

>> % 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))'
Wok
fuente
4

Además de la naturaleza evolutiva de la respuesta a esta pregunta, comenzando con r2016b, MATLAB expandirá implícitamente las dimensiones singleton, eliminando la necesidad de bsxfunen muchos casos.

De las notas de la versión r2016b :

Expansión implícita: aplique operaciones y funciones de elementos a matrices con expansión automática de dimensiones de longitud 1

La expansión implícita es una generalización de la expansión escalar. Con la expansión escalar, un escalar se expande para tener el mismo tamaño que otra matriz para facilitar las operaciones de elementos. Con la expansión implícita, los operadores y las funciones de elementos enumerados aquí pueden expandir implícitamente sus entradas para que tengan el mismo tamaño, siempre que las matrices tengan tamaños compatibles. Dos matrices tienen tamaños compatibles si, para cada dimensión, los tamaños de dimensión de las entradas son iguales o uno de ellos es 1. Consulte Tamaños de matriz compatibles para operaciones básicas y Operaciones de matriz frente a matriz para obtener más información.

Element-wise arithmetic operators+, -, .*, .^, ./, .\

Relational operators<, <=, >, >=, ==, ~=

Logical operators&, |, xor

Bit-wise functionsbitand, bitor, bitxor

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

Por ejemplo, puede calcular la media de cada columna en una matriz A y luego restar el vector de valores medios de cada columna con A - media (A).

Anteriormente, esta funcionalidad estaba disponible a través de la función bsxfun. Ahora se recomienda que reemplace la mayoría de los usos de bsxfun con llamadas directas a las funciones y operadores que admiten la expansión implícita. En comparación con el uso de bsxfun, la expansión implícita ofrece una velocidad más rápida, un mejor uso de la memoria y una mejor legibilidad del código.

craigim
fuente
2

Ninguna de las respuestas anteriores funcionó "fuera de la caja" para mí, sin embargo, la siguiente función, obtenida al copiar las ideas de las otras respuestas, funciona:

apply_func_2_cols = @(f,M) cell2mat(cellfun(f,num2cell(M,1), 'UniformOutput',0));

Toma una función fy la aplica a cada columna de la matriz M.

Así por ejemplo:

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
fuente
1

Con las versiones recientes de Matlab, puede utilizar la estructura de datos de la tabla a su favor. Incluso hay una operación 'rowfun', pero me resultó más fácil hacer esto:

a = magic(6);
incrementRow = cell2mat(cellfun(@(x) x+1,table2cell(table(a)),'UniformOutput',0))

o aquí hay uno más antiguo que tenía que no requiere tablas, para versiones anteriores de Matlab.

dataBinner = cell2mat(arrayfun(@(x) Binner(a(x,:),2)',1:size(a,1),'UniformOutput',0)')
Jordán
fuente
1

La respuesta aceptada parece ser convertir primero a celdas y luego usar cellfunpara operar sobre todas las celdas. No conozco la aplicación específica, pero en general creo que usar bsxfunpara operar sobre la matriz sería más eficiente. Básicamente, bsxfunaplica una operación elemento por elemento en dos matrices. Entonces, si quisiera multiplicar cada elemento en un n x 1vector por cada elemento en un m x 1vector para obtener una n x mmatriz, podría usar:

vec1 = [ stuff ];    % n x 1 vector
vec2 = [ stuff ];    % m x 1 vector
result = bsxfun('times', vec1.', vec2);

Esto le dará una matriz llamada resulten la que la entrada (i, j) será el i-ésimo elemento de vec1multiplicado por el j-ésimo elemento de vec2.

Puede utilizarlo bsxfunpara todo tipo de funciones integradas y puede declarar las suyas propias. La documentación tiene una lista de muchas funciones integradas, pero básicamente puede nombrar cualquier función que acepte dos matrices (vector o matriz) como argumentos y hacer que funcione.

Ingeniero
fuente
-1

Me encontré con esta pregunta / respuesta mientras buscaba cómo calcular las sumas de fila de una matriz.

Solo me gustaría agregar que la función SUMA de Matlab en realidad tiene soporte para sumar para una dimensión determinada, es decir, una matriz estándar con dos dimensiones.

Entonces, para calcular las sumas de las columnas, haga lo siguiente:

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

y para las sumas de fila, simplemente haz

rowsum = sum(M, 2)

Mi apuesta es que esto es más rápido que programar un bucle for y convertir a celdas :)

Todo esto se puede encontrar en la ayuda de matlab para SUM.

nunca
fuente
7
la capacidad de aplicar SUM a lo largo de una dimensión determinada se mencionó en la primera oración de la respuesta original a esta pregunta. Luego, la respuesta pasó a abordar el caso en el que la capacidad de elegir una dimensión aún no está incorporada en la función. Sin embargo, tiene razón en que usar las opciones de selección de dimensión integradas, cuando están disponibles, es casi siempre más rápido que un bucle for o convertir a celdas.
cjh
Es cierto que, sin embargo, la respuesta anterior me envió de vuelta a la documentación de matlab, ya que no necesitaba toda esa fantasía, así que solo quería compartir y salvar a otros, que necesitan la solución simple, de buscar.
2012
-2

si conoce la longitud de sus filas, puede hacer algo como esto:

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) )
Stefan
fuente
2
Para cualquiera que vea esta respuesta: ¡Esta no es la manera de hacerlo! ¡Esta no es la manera de hacer nada en MATLAB!
Stewie Griffin