Funzione mappa in MATLAB?


100

Sono un po 'sorpreso che MATLAB non abbia una funzione Map, quindi ne ho hackerata una insieme da solo poiché è qualcosa di cui non posso vivere senza. Esiste una versione migliore là fuori? Esiste una libreria di programmazione funzionale piuttosto standard per MATLAB che mi manca?

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

l'utilizzo sarebbe ad es

map( @(x)x^2,1:10)

12
Lezione n. 1 da altre lingue a Matlab: non utilizzare i cicli for, sono alcuni ordini di grandezza più lenti di una soluzione vettorializzata.
CookieOfFortune

15
Con l'introduzione del JIT, i cicli for non eseguono la penalità che avevano una volta.
MatlabDoug

@CookieOfFortune Penso che non sia più vero ...
Ander Biguri

2
@AnderBiguri Penso che abbiano aggiunto alcuni miglioramenti ma è ancora molto più lento.
CookieOfFortune

La libreria funzionale su File Exchange ha map, foldl(noto anche come reduce), select(aka filter) e altre chicche indispensabili. Consigliato (se devi usare Matlab).
Ahmed Fasih

Risposte:


133

La risposta breve: la funzione integrata arrayfunfa esattamente quello che fa la tua mapfunzione per gli array numerici:

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

     1     4     9    16    25    36    49    64    81   100

Ci sono altre due funzioni incorporate che si comportano in modo simile: cellfun(che opera su elementi di array di celle) e structfun(che opera su ogni campo di una struttura).

Tuttavia, queste funzioni spesso non sono necessarie se si trae vantaggio dalla vettorializzazione, in particolare utilizzando operatori aritmetici basati sugli elementi . Per l'esempio che hai fornito, una soluzione vettorializzata sarebbe:

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

     1     4     9    16    25    36    49    64    81   100

Alcune operazioni opereranno automaticamente tra gli elementi (come l'aggiunta di un valore scalare a un vettore) mentre altri operatori hanno una sintassi speciale per l'operazione basata sugli elementi (indicata da un .prima dell'operatore). Molte funzioni integrate in MATLAB sono progettate per operare su argomenti vettoriali e matrici usando operazioni basate sugli elementi (spesso applicate a una data dimensione, come sume meanper esempio), e quindi non richiedono funzioni di mappa.

Per riassumere, ecco alcuni modi diversi per quadrare ogni elemento in un array:

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

Ovviamente, per un'operazione così semplice, l'opzione n. 1 è la scelta più sensata (ed efficiente).


2
Si noti che l'opzione 1 non è solo più semplice, ma anche più veloce (rispetto all'opzione 3, 2 dovrebbe essere molto simile a 1)!
Diederick C.Niehorster

10

Oltre alle operazioni su vettori ed elementi, esistono anche cellfunfunzioni di mappatura su array di celle. Per esempio:

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

Se 'UniformOutput' è vero (o non fornito), tenterà di concatenare i risultati in base alle dimensioni della matrice di celle, quindi

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

2

Una soluzione piuttosto semplice, utilizzando la vettorizzazione di Matlab sarebbe:

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

Ora, digitando

c( b ) = a

ritorna

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

c (b) è un riferimento a un vettore di dimensione 5 con gli elementi di c agli indici dati da b. Ora, se assegni valori a questo vettore di riferimento, i valori originali in c vengono sovrascritti, poiché c (b) contiene riferimenti ai valori in ce nessuna copia.


1

Sembra che l'arrayfun integrato non funzioni se il risultato necessario è un array di funzioni: ad esempio: map (@ (x) [xx ^ 2 x ^ 3], 1:10)

lievi modifiche di seguito rendono questo lavoro migliore:

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

5
ARRAYFUN funzionerebbe per il tuo esempio, dovresti semplicemente includere gli argomenti di input ..., 'UniformOutput', false);per creare un output di array di celle contenente i tuoi array, quindi formattarli e combinarli come preferisci in un array non di celle.
gnovice

0

Se matlab non ha una funzione map incorporata, potrebbe essere a causa di considerazioni sull'efficienza. Nella tua implementazione stai usando un ciclo per iterare sugli elementi dell'elenco, che è generalmente disapprovato nel mondo Matlab. La maggior parte delle funzioni matlab incorporate sono "vettorializzate", cioè è più efficiente chiamare una funzione su un intero array, che iterare su di essa da soli e chiamare la funzione per ogni elemento.

In altre parole, questo


a = 1:10;
a.^2

è molto più veloce di così


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

assumendo la tua definizione di mappa.


2
Penso che il suo punto non fosse che voleva che fosse necessariamente un loop, ma semplicemente che fosse specificato come risultato della matrice dei risultati dell'applicazione della funzione fornita agli elementi corrispondenti dell'array fornito. Non conosco molto Matlab, ma sembra che arrayfun faccia il suo lavoro.

1
La maggior parte delle funzioni e degli operatori Matlab integrati lo fanno già: operano su ogni elemento dell'array di input e restituiscono un array di risultati corrispondente.
Dima

0

Non è necessario mappoiché una funzione scalare applicata a un elenco di valori viene applicata a ciascuno dei valori e quindi funziona in modo simile a map. Prova

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

f(l)

Nel tuo caso particolare potresti persino scrivere

l.^2

9
-1: In realtà non è vero. Matlab non dispone di un sistema di tipi sufficientemente potente per specificare funzioni scalari. f viene chiamato con il vettore e nel tuo esempio viene eseguita una singola aggiunta vettoriale. Per verificarlo, profila il tuo codice di esempio ("profilo attivato" prima di eseguire il codice, quindi "rapporto disattivato profilo" dopo). Vedrai che c'è una sola chiamata a f.
Mr Fooz

-1

Vettorizzare la soluzione come descritto nelle risposte precedenti è probabilmente la migliore soluzione per la velocità. Anche la vettorizzazione è molto Matlaby e si sente bene.

Detto questo, Matlab ora ha una classe contenitore Map.

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


Op sta parlando della funzione di ordine superiore, cioè, cellfunet al., Non di tabelle hash o coppie chiave-valore.
Ahmed Fasih
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.