Você pode aplicar uma função a cada item em um vetor dizendo, por exemplo v + 1
, ou pode usar a função arrayfun
. Como posso fazer isso para cada linha / coluna de uma matriz sem usar um loop for?
Você pode aplicar uma função a cada item em um vetor dizendo, por exemplo v + 1
, ou pode usar a função arrayfun
. Como posso fazer isso para cada linha / coluna de uma matriz sem usar um loop for?
Respostas:
Muitas operações integradas gostam sum
e prod
já são capazes de operar em linhas ou colunas, portanto, você pode refatorar a função que está aplicando para tirar vantagem disso.
Se essa não for uma opção viável, uma maneira de fazer isso é coletar as linhas ou colunas em células usando mat2cell
ou e num2cell
, em seguida, use cellfun
para operar na matriz de células resultante.
Como exemplo, digamos que você deseja somar as colunas de uma matriz M
. Você pode fazer isso simplesmente usando sum
:
M = magic(10); %# A 10-by-10 matrix
columnSums = sum(M, 1); %# A 1-by-10 vector of sums for each column
E aqui está como você faria isso usando a opção num2cell
/ mais 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
true = false
é uma declaração válida, tenho certeza de que há uma maneira de fazer isso (:
sum(M, 1)
. Iniciantes podem pensar que sum
podem ser usados dessa forma para matrizes de tamanhos arbitrários e então ficar perplexos quando a matriz um dia for usada 1-by-n
.
Você pode querer a função mais obscura do Matlab, bsxfun . A partir da documentação do Matlab, bsxfun "aplica a operação binária elemento a elemento especificada pelo identificador de função divertido para os arrays A e B, com a expansão singleton habilitada."
@gnovice afirmou acima que soma e outras funções básicas já operam na primeira dimensão não única (ou seja, linhas se houver mais de uma linha, colunas se houver apenas uma linha, ou dimensões superiores se as dimensões inferiores tiverem tamanho == 1 ) No entanto, bsxfun funciona para qualquer função, incluindo (e especialmente) funções definidas pelo usuário.
Por exemplo, digamos que você tenha uma matriz A e um vetor linha BEg, digamos:
A = [1 2 3;
4 5 6;
7 8 9]
B = [0 1 2]
Você quer uma função power_by_col que retorna em um vetor C todos os elementos em A à potência da coluna correspondente de B.
No exemplo acima, C é uma matriz 3x3:
C = [1^0 2^1 3^2;
4^0 5^1 6^2;
7^0 8^1 9^2]
ie,
C = [1 2 9;
1 5 36;
1 8 81]
Você poderia fazer isso usando a força bruta usando repmat:
C = A.^repmat(B, size(A, 1), 1)
Ou você pode fazer isso da maneira clássica usando bsxfun, que cuida internamente da etapa repmat:
C = bsxfun(@(x,y) x.^y, A, B)
Portanto, o bsxfun economiza algumas etapas (você não precisa calcular explicitamente as dimensões de A). No entanto, em alguns testes informais meus, descobri que repmat é quase duas vezes mais rápido se a função a ser aplicada (como minha função de potência, acima) for simples. Portanto, você precisará escolher se deseja simplicidade ou velocidade.
Não posso comentar o quão eficiente isso é, mas aqui está uma solução:
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)
Com base na resposta de Alex , aqui está uma função mais 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));
Aqui está uma comparação entre as duas funções:
>> % 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))'
Para integridade / interesse, gostaria de acrescentar que o matlab tem uma função que permite operar em dados por linha em vez de por elemento. É chamado rowfun
( http://www.mathworks.se/help/matlab/ref/rowfun.html ), mas o único "problema" é que funciona em tabelas ( http://www.mathworks.se/help/ matlab / ref / table.html ) em vez de matrizes .
Somando-se à natureza evolutiva da resposta a esta pergunta, começando com r2016b, o MATLAB expandirá implicitamente as dimensões do singleton, removendo a necessidade de bsxfun
em muitos casos.
Das notas de lançamento do r2016b :
Expansão implícita: Aplicar operações e funções de elemento a matrizes com expansão automática de dimensões de comprimento 1
A expansão implícita é uma generalização da expansão escalar. Com a expansão escalar, um escalar se expande para ter o mesmo tamanho de outra matriz para facilitar as operações em elementos. Com a expansão implícita, os operadores e funções element-wise listados aqui podem expandir implicitamente suas entradas para que tenham o mesmo tamanho, desde que os arrays tenham tamanhos compatíveis. Dois arrays têm tamanhos compatíveis se, para cada dimensão, os tamanhos das dimensões das entradas forem iguais ou um deles for 1. Consulte Tamanhos de array compatíveis para operações básicas e Array vs. Operações de matriz para obter mais informações.
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 exemplo, você pode calcular a média de cada coluna em uma matriz A e, em seguida, subtrair o vetor de valores médios de cada coluna com A - média (A).
Anteriormente, essa funcionalidade estava disponível por meio da função bsxfun. Agora, é recomendado que você substitua a maioria dos usos de bsxfun por chamadas diretas para as funções e operadores que suportam a expansão implícita. Em comparação com o uso de bsxfun, a expansão implícita oferece velocidade mais rápida, melhor uso de memória e legibilidade de código aprimorada.
Nenhuma das respostas acima funcionou "fora da caixa" para mim, no entanto, a seguinte função, obtida copiando as ideias das outras respostas funciona:
apply_func_2_cols = @(f,M) cell2mat(cellfun(f,num2cell(M,1), 'UniformOutput',0));
Ele pega uma função f
e a aplica a todas as colunas da matrizM
.
Então, por exemplo:
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
Com as versões recentes do Matlab, você pode usar a estrutura de dados da tabela a seu favor. Há até uma operação 'rowfun', mas achei mais fácil fazer isso:
a = magic(6);
incrementRow = cell2mat(cellfun(@(x) x+1,table2cell(table(a)),'UniformOutput',0))
ou aqui está um mais antigo que eu tinha que não requer tabelas, para versões mais antigas do Matlab.
dataBinner = cell2mat(arrayfun(@(x) Binner(a(x,:),2)',1:size(a,1),'UniformOutput',0)')
A resposta aceita parece ser primeiro converter em células e depois usar cellfun
para operar em todas as células. Não conheço a aplicação específica, mas no geral acho que usar bsxfun
para operar sobre a matriz seria mais eficiente. Basicamente, bsxfun
aplica uma operação elemento por elemento em duas matrizes. Portanto, se você quiser multiplicar cada item de um n x 1
vetor por cada item de um m x 1
vetor para obter uma n x m
matriz, poderá usar:
vec1 = [ stuff ]; % n x 1 vector
vec2 = [ stuff ]; % m x 1 vector
result = bsxfun('times', vec1.', vec2);
Isso lhe dará uma matriz chamada result
em que a entrada (i, j) será o iésimo elemento de vec1
multiplicado pelo j-ésimo elemento de vec2
.
Você pode usar bsxfun
para todos os tipos de funções internas e pode declarar as suas próprias. A documentação tem uma lista de muitas funções embutidas, mas basicamente você pode nomear qualquer função que aceite dois arrays (vetor ou matriz) como argumentos e fazê-la funcionar.
Tropecei nesta pergunta / resposta enquanto procurava como calcular as somas das linhas de uma matriz.
Eu gostaria apenas de acrescentar que a função SUM do Matlab, na verdade, tem suporte para somar para uma dada dimensão, ou seja, uma matriz padrão com duas dimensões.
Portanto, para calcular as somas das colunas:
colsum = sum(M) % or sum(M, 1)
e para as somas de linha, basta fazer
rowsum = sum(M, 2)
Minha aposta é que isso é mais rápido do que programar um loop for e converter para células :)
Tudo isso pode ser encontrado na ajuda do matlab para SUM.
se você sabe o comprimento de suas linhas, pode fazer algo assim:
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) )