resultados - ¿Cómo puedo aplicar una función a cada fila/columna de una matriz en MATLAB?
matrices en matlab programacion (11)
Agregando a la evolución de la naturaleza de la respuesta a esta pregunta, comenzando con r2016b, MATLAB expandirá implícitamente las dimensiones de singleton, eliminando la necesidad de bsxfun
en muchos casos.
De las notas de la versión r2016b :
Expansión implícita: aplique operaciones y funciones basadas en elementos a arreglos 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 las dimensiones de las entradas son las mismas o una de ellas 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 functions — bitand, bitor, bitxor Elementary math functions — max, 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 soportan 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.
Puede aplicar una función a cada elemento de 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?
Con las versiones recientes de Matlab, puede usar la estructura de datos Table para su ventaja. Incluso hay una operación ''rowfun'', pero me resultó más fácil simplemente hacer esto:
a = magic(6);
incrementRow = cell2mat(cellfun(@(x) x+1,table2cell(table(a)),''UniformOutput'',0))
o aquí hay uno más viejo 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)'')
Es posible que desee la función más oscura de Matlab bsxfun . 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 no única (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) funciones definidas por el usuario.
Por ejemplo, supongamos que tiene una matriz A y un vector de fila BEg, digamos:
A = [1 2 3;
4 5 6;
7 8 9]
B = [0 1 2]
Desea 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.
Del ejemplo anterior, C es una matriz 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]
Puedes hacer esto de la manera de fuerza bruta usando repmat:
C = A.^repmat(B, size(A, 1), 1)
O puede hacerlo de la manera clásica utilizando 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.
La respuesta aceptada parece ser convertir primero a celdas y luego utilizar cellfun
para operar sobre todas las celdas. No conozco la aplicación específica, pero en general creo que usar bsxfun
para operar sobre la matriz sería más eficiente. Básicamente, bsxfun
aplica una operación elemento por elemento en dos matrices. Entonces, si quisieras multiplicar cada ítem en un vector nx 1
por cada ítem en un vector mx 1
para obtener una matriz nxm
, podrías usar:
vec1 = [ stuff ]; % n x 1 vector
vec2 = [ stuff ]; $ m x 1 vector
result = bsxfun(''times'', vec1.'', vec2);
Esto le dará una matriz llamada result
donde la entrada (i, j) será el i-ésimo elemento de vec1
multiplicado por el elemento j-ésimo de vec2
.
Puede usar bsxfun
para todo tipo de funciones incorporadas, y puede declarar las suyas propias. La documentación tiene una lista de muchas funciones incorporadas, pero básicamente puede nombrar cualquier función que acepte dos matrices (vector o matriz) como argumentos y hacer que funcione.
Muchas operaciones integradas como sum
y prod
ya 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 recolectar las filas o columnas en celdas usando mat2cell
o num2cell
, luego use cellfun
para operar en la matriz de celdas resultante.
Como ejemplo, digamos que desea sumar las columnas de una matriz M
Puedes 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 aquí es cómo lo harías usando la opción num2cell
/ cellfun
más complicada:
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
Ninguna de las respuestas anteriores funcionó "de la caja" para mí, sin embargo, funciona la siguiente función, obtenida al copiar las ideas de las otras respuestas:
apply_func_2_cols = @(f,M) cell2mat(cellfun(f,num2cell(M,1), ''UniformOutput'',0));
Toma una función f
y 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
No puedo comentar sobre cuán 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)
Para completar / interés me gustaría agregar que matlab tiene una función que le permite operar en datos por fila en lugar de por elemento. Se llama rowfun
( http://www.mathworks.se/help/matlab/ref/rowfun.html ), pero el único "problema" es que funciona en tablas ( http://www.mathworks.se/help/matlab/ref/table.html ) en lugar de matrices .
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))''
Tropecé con esta pregunta / respuesta mientras buscaba cómo calcular las sumas de fila de una matriz.
Me gustaría agregar que la función SUM de Matlab en realidad tiene soporte para sumar para una dimensión dada, 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 filas, simplemente hazlo
rowsum = sum(M, 2)
Mi apuesta es que esto es más rápido que tanto la programación de un bucle for y la conversión a las células :)
Todo esto se puede encontrar en la ayuda de matlab para SUM.
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) )