Função de mapa no MATLAB?

100

Estou um pouco surpreso que o MATLAB não tenha uma função Map, então eu mesmo criei um, já que é algo sem o qual não posso viver. Existe uma versão melhor por aí? Existe uma biblioteca de programação funcional padrão para MATLAB que estou perdendo?

function results = map(f,list)
% why doesn't MATLAB have a Map function?
results = zeros(1,length(list));
for k = 1:length(list)
    results(1,k) = f(list(k));
end

end

o uso seria, por exemplo

map( @(x)x^2,1:10)
Will Ness
fonte
12
Lição # 1 indo de outras linguagens para o Matlab: não use loops for, eles são algumas ordens de magnitude mais lentos do que uma solução vetorizada.
CookieOfFortune
15
Com a introdução do JIT, os loops for não sofrem a mesma penalidade de antes.
MatlabDoug
@CookieOfFortune Acho que isso não é mais verdade ...
Ander Biguri
2
@AnderBiguri Acho que adicionaram algumas melhorias, mas ainda é muito mais lento.
CookieOfFortune
A Biblioteca Funcional no File Exchange tem map, foldl(também conhecido como reduce), select(aka filter) e outros itens indispensáveis. Recomendado (se você tiver que usar Matlab).
Ahmed Fasih

Respostas:

133

A resposta curta: a função integrada arrayfunfaz exatamente o que sua mapfunção faz para matrizes numéricas:

>> y = arrayfun(@(x) x^2, 1:10)
y =

     1     4     9    16    25    36    49    64    81   100

Existem duas outras funções integradas que se comportam de forma semelhante: cellfun(que opera em elementos de matrizes de células) e structfun(que opera em cada campo de uma estrutura).

No entanto, essas funções geralmente não são necessárias se você tirar proveito da vetorização, especificamente usando operadores aritméticos elementares . Para o exemplo que você deu, uma solução vetorizada seria:

>> x = 1:10;
>> y = x.^2
y =

     1     4     9    16    25    36    49    64    81   100

Algumas operações irão operar automaticamente entre os elementos (como adicionar um valor escalar a um vetor), enquanto outros operadores têm uma sintaxe especial para operação em elementos (denotada por a .antes do operador). Muitas funções embutidas no MATLAB são projetadas para operar em argumentos vetoriais e matriciais usando operações elementares (frequentemente aplicadas a uma determinada dimensão, como sume meanpor exemplo) e, portanto, não requerem funções de mapa.

Para resumir, aqui estão algumas maneiras diferentes de enquadrar cada elemento em uma matriz:

x = 1:10;       % Sample array
f = @(x) x.^2;  % Anonymous function that squares each element of its input

% Option #1:
y = x.^2;  % Use the element-wise power operator

% Option #2:
y = f(x);  % Pass a vector to f

% Option #3:
y = arrayfun(f, x);  % Pass each element to f separately

Obviamente, para uma operação tão simples, a opção nº 1 é a escolha mais sensata (e eficiente).

gnovice
fonte
2
Deve-se notar que a opção 1 não é apenas mais simples, mas também mais rápida (em comparação com a opção 3, 2 deve ser muito semelhante a 1)!
Diederick C. Niehorster
10

Além das operações vetoriais e de elementos, também cellfunhá funções de mapeamento em matrizes de células. Por exemplo:

cellfun(@upper, {'a', 'b', 'c'}, 'UniformOutput',false)
ans = 
    'A'    'B'    'C'

Se 'UniformOutput' for verdadeiro (ou não fornecido), ele tentará concatenar os resultados de acordo com as dimensões da matriz de células, então

cellfun(@upper, {'a', 'b', 'c'})
ans =
ABC
Kwatford
fonte
2

Uma solução bastante simples, usando a vetorização do Matlab seria:

a = [ 10 20 30 40 50 ]; % the array with the original values
b = [ 10 8 6 4 2 ]; % the mapping array
c = zeros( 1, 10 ); % your target array

Agora digitando

c( b ) = a

retorna

c = 0    50     0    40     0    30     0    20     0    10

c (b) é uma referência a um vetor de tamanho 5 com os elementos de c nos índices dados por b. Agora, se você atribuir valores a este vetor de referência, os valores originais em c são substituídos, pois c (b) contém referências aos valores em ce nenhuma cópia.

doc
fonte
1

Parece que o arrayfun integrado não funciona se o resultado necessário for um array de funções: por exemplo: map (@ (x) [xx ^ 2 x ^ 3], 1: 10)

os pequenos mods abaixo tornam este trabalho melhor:

function results = map(f,list)
% why doesn't MATLAB have a Map function?
for k = 1:length(list)
    if (k==1)
        r1=f(list(k));
        results = zeros(length(r1),length(list));
        results(:,k)=r1;
    else
        results(:,k) = f(list(k));

    end;
end;
end
Foo Bara
fonte
5
ARRAYFUN funcionaria para o seu exemplo, você apenas teria que incluir os argumentos de entrada ..., 'UniformOutput', false);para criar uma saída de array de células contendo seus arrays, então formatar e combiná-los como quiser em um array não celular.
gnovice
0

Se o matlab não tiver uma função de mapa embutida, pode ser devido a considerações de eficiência. Em sua implementação, você está usando um loop para iterar os elementos da lista, o que geralmente é desaprovado no mundo do matlab. A maioria das funções embutidas do matlab são "vetorizadas", ou seja, é mais eficiente chamar uma função em um array inteiro do que iterar você mesmo e chamar a função para cada elemento.

Em outras palavras, este


a = 1:10;
a.^2

é muito mais rápido que isso


a = 1:10;
map(@(x)x^2, a)

assumindo sua definição de mapa.

Dima
fonte
2
Acho que seu ponto não era que ele queria necessariamente fazer um loop, mas simplesmente ser especificado como tendo como resultado a matriz de resultados da aplicação da função fornecida aos elementos correspondentes da matriz fornecida. Eu não sei muito sobre matlab, mas parece que arrayfun faz o trabalho.
1
A maioria das funções e operadores integrados do Matlab já fazem isso: eles operam em cada elemento da matriz de entrada e retornam uma matriz correspondente de resultados.
Dima
0

Você não precisa, mapjá que uma função escalar que é aplicada a uma lista de valores é aplicada a cada um dos valores e, portanto, funciona de forma semelhante a map. Apenas tente

l = 1:10
f = @(x) x + 1

f(l)

No seu caso particular, você pode até escrever

l.^2
Dario
fonte
9
-1: Isso não é verdade. O Matlab não possui um sistema de tipos forte o suficiente para especificar funções escalares. f é chamado com o vetor e uma única adição de vetor é executada em seu exemplo. Para verificar isso, crie o perfil de sua amostra de código ("profile on" antes de executar o código e "profile off report" depois). Você verá que há uma única chamada para f.
Sr. Fooz
-1

Vetorizar a solução conforme descrito nas respostas anteriores é provavelmente a melhor solução para velocidade. A vetorização também é muito Matlaby e se sente bem.

Com isso, o Matlab agora tem uma classe container Map.

Veja http://www.mathworks.com/help/matlab/map-containers.html

TallBrian
fonte
Op está falando sobre a função de ordem superior, ou seja, cellfunet al., Não tabelas hash ou pares de valor-chave.
Ahmed Fasih