2013-04-26 8 views
6

Ho bisogno di elaborare grandi quantità di dati tabulari di tipo misto - stringhe e doppi. Un problema standard, penserei. Qual è la migliore struttura dati in Matlab per lavorare con questo?Struttura dati Matlab per tipo misto - che cosa è tempo + spazio efficiente?

Cellarray non è sicuramente la risposta. È estremamente inefficiente nella memoria. (test mostrati sotto). Il set di dati (dalla casella degli strumenti delle statistiche) è orribile in termini di tempo e spazio inefficiente. Questo mi lascia con structarray o struct of matrici. Ho fatto un test su tutte e quattro le diverse opzioni per il tempo e la memoria di seguito e mi sembra che la struttura degli array sia l'opzione migliore per le cose per cui ho provato.

Sono relativamente nuovo a Matlab e questo è un po 'deludente, francamente. Ad ogni modo - cerco consigli se mi manca qualcosa o se i miei test sono accurati/ragionevoli. Mi mancano altre considerazioni oltre all'accesso/conversione/utilizzo della memoria che sono suscettibili di venire fuori mentre codice più usando questa roba. (fyi am using R2010b)

* * Test # 1: Velocità di accesso Accesso a un elemento di dati.

cellarray:0.002s 
dataset:36.665s  %<<< This is horrible 
structarray:0.001s 
struct of array:0.000s 

* * Test # 2: velocità di conversione e l'utilizzo della memoria ho lasciato cadere set di dati da questa prova.

Cellarray(doubles)->matrix:d->m: 0.865s 
Cellarray(mixed)->structarray:c->sc: 0.268s 
Cellarray(doubles)->structarray:d->sd: 0.430s 
Cellarray(mixed)->struct of arrays:c->sac: 0.361s 
Cellarray(doubles)->struct of arrays:d->sad: 0.887s 
    Name   Size    Bytes Class  Attributes 
    c   100000x10   68000000 cell     
    d   100000x10   68000000 cell     
    m   100000x10    8000000 double    
    sac   1x1    38001240 struct    
    sad   1x1    8001240 struct    
    sc  100000x1    68000640 struct    
    sd  100000x1    68000640 struct 

CODICE ==================: TEST # 1

%% cellarray 
    c = cell(100000,10); 
    c(:,[1,3,5,7,9]) = num2cell(zeros(100000,5)); 
    c(:,[2,4,6,8,10]) = repmat({'asdf'}, 100000, 5); 
    cols = strcat('Var', strtrim(cellstr(num2str((1:10)'))))'; 
    te = tic; 
    for iii=1:1000 
     x = c(1234,5); 
    end 
    te = toc(te); 
    fprintf('cellarray:%0.3fs\n', te); 
    %% dataset 
    ds = dataset({ c, cols{:} }); 
    te = tic; 
    for iii=1:1000 
     x = ds(1234,5); 
    end 
    te = toc(te); 
    fprintf('dataset:%0.3fs\n', te); 
    %% structarray 
    s = cell2struct(c, cols, 2); 
    te = tic; 
    for iii=1:1000 
     x = s(1234).Var5; 
    end 
    te = toc(te); 
    fprintf('structarray:%0.3fs\n', te); 
    %% struct of arrays 
    for iii=1:numel(cols) 
     if iii/2==floor(iii/2) % even => string 
      sac.(cols{iii}) = c(:,iii); 
     else 
      sac.(cols{iii}) = cell2mat(c(:,iii)); 
     end 
    end 
    te = tic; 
    for iii=1:1000 
     x = sac.Var5(1234); 
    end 
    te = toc(te); 
    fprintf('struct of array:%0.3fs\n', te); 

============= ===== CODICE: TEST # 2

%% cellarray 
% c - cellarray containing mixed type 
c = cell(100000,10); 
c(:,[1,3,5,7,9]) = num2cell(zeros(100000,5)); 
c(:,[2,4,6,8,10]) = repmat({'asdf'}, 100000, 5); 
cols = strcat('Var', strtrim(cellstr(num2str((1:10)'))))'; 
% c - cellarray containing doubles only 
d = num2cell(zeros(100000, 10)); 
%% matrix 
% doubles only 
te = tic; 
m = cell2mat(d); 
te = toc(te); 
fprintf('Cellarray(doubles)->matrix:d->m: %0.3fs\n', te); 
%% structarray 
% mixed 
te = tic; 
sc = cell2struct(c, cols, 2); 
te = toc(te); 
fprintf('Cellarray(mixed)->structarray:c->sc: %0.3fs\n', te); 
% doubles 
te = tic; 
sd = cell2struct(d, cols, 2); 
te = toc(te); 
fprintf('Cellarray(doubles)->structarray:d->sd: %0.3fs\n', te); 
%% struct of arrays 
% mixed 
te = tic; 
for iii=1:numel(cols) 
    if iii/2==floor(iii/2) % even => string 
     sac.(cols{iii}) = c(:,iii); 
    else 
     sac.(cols{iii}) = cell2mat(c(:,iii)); 
    end 
end 
te = toc(te); 
fprintf('Cellarray(mixed)->struct of arrays:c->sac: %0.3fs\n', te); 
% doubles 
te = tic; 
for iii=1:numel(cols) 
    sad.(cols{iii}) = cell2mat(d(:,iii)); 
end 
te = toc(te); 
fprintf('Cellarray(doubles)->struct of arrays:d->sad: %0.3fs\n', te); 
%% 
clear iii cols te; 
whos 
+0

mentre "set di dati" è effettivamente lento, i tempi sono orribilmente lenti. Sto ottenendo 'dataset: 0.7s' sull'accesso mentre gli altri sono nello stesso ordine del tuo. Sto eseguendo R2013a su WinXP a 32 bit – Amro

risposta

1

direi che se avete bisogno di gestire grandi quantità di dati, allora MATLAB non è la scelta migliore per cominciare. Vai per un db corretto ed eventualmente importa i dati che ti servono in MATLAB.

Tuttavia, se hai intenzione di utilizzare MATLAB comunque vorrei ancora scegliere cellarrays, cioè se non hai bisogno di riferimenti sintattici ai dati in forma di fieldnames come in strutture.

Quando si utilizzano i cellarray, tenere presente che ogni cella impone 112 byte di sovraccarico. Pertanto, vorrei creare una cella per ogni colonna (non una cella per ogni doppia scalare):

c = cell(1,10); 
c(1,1:2:10) = num2cell(rand(1e5,5),1); 
c(1,2:2:10) = {cellstr(repmat('asdf', 100000, 1))}; 

e la memoria-saggio (nessun cambiamento nel tempo):

Name   Size    Bytes Class Attributes 
c    1x10   38000600 cell 

Inoltre, quello che si chiama una struttura di matrice viene solitamente indicata con una struttura "scalare" anziché una struttura di strutture (o una struttura non scalare).

Se ricordo correttamente, le strutture tendono a peggiorare le prestazioni per lettura/scrittura quando si avviano i campi di nidificazione (ho bisogno di trovare il thread specifico però).

3

Il modo per rendere il codice Matlab efficiente in termini di spazio e tempo è quello di lavorare con grandi matrici di primitivi, ovvero array di doppi, inti o caratteri. Questo ti dà un layout più stretto in memoria e ti permette di eseguire operazioni vettorializzate.

Per i dati tabulari, ciascuna colonna sarà di tipo omogeneo, ma colonne diverse possono essere di tipi diversi e in genere si hanno molte più righe rispetto alle colonne. E spesso farai operazioni - confronti o matematica - su tutti gli elementi di una colonna, o una selezione mascherata di una colonna, che si presta a operazioni vettorializzate. Quindi immagazzina ogni colonna come un array di colonne, si spera di primitive. Puoi incollare quelle colonne nei campi di una struttura o elementi di un vettore di cella; non importa molto per quanto riguarda le prestazioni, e la forma della struttura sarà molto più leggibile e sembrerà più simile a un tavolo. Un array di celle 2-d o altra struttura di dati che interrompe tutti gli elementi nei loro piccoli mxarray non funzionerà in modo accettabile.

Cioè, se si dispone di una tabella di 10.000 righe per 10 colonne, si desidera disporre di un array di celle lungo 10 o di una struttura di 10 campi, con ciascuno di quei campi o elementi che contengono un vettore di colonna primitivo lungo 10.000.

L'oggetto oggetto dataset è fondamentalmente un involucro attorno a una struttura di matrici di colonne come descritto in precedenza, bloccato in un oggetto. Ma gli oggetti in Matlab hanno un sovraccarico maggiore rispetto alle strutture e alle celle normali; stai pagando per uno o più chiamate di metodo ad ogni accesso ad esso. Dai un'occhiata a Is MATLAB OOP slow or am I doing something wrong? (completa informativa: questa è una delle mie risposte).

Il test impostato non è indicativo di come funzionerà il codice Matlab, perché sta eseguendo l'accesso scalare a elemento singolo. Cioè, sta pagando per la colonna e poi l'accesso all'elemento riga su ogni passata del ciclo. Se il tuo codice Matlab lo sta facendo, sei già sfortunato. Per essere veloci, è necessario far apparire le colonne all'esterno di un ciclo, ovvero caricare la costosa operazione di accesso alle colonne a un ciclo esterno o codice di configurazione e quindi eseguire operazioni vettoriali (come +, ==, '<', ismember, e così via) su interi vettori di colonne o loop su vettori numerici primitivi (che il JIT può ottimizzare). Se si esegue questa operazione, quindi dataset e altre strutture tabulari basate su oggetti possono avere prestazioni decenti.

Stringhe in Matlab tipo di succhiare, sfortunatamente. Tu vuoi andare via da cellstrs. Hai un paio di opzioni.

  • Se le stringhe in una colonna sono di circa la stessa lunghezza, e non si hanno alcun lunghe stringhe in loro, è possibile memorizzare un vettore di stringhe come un 2-D char array. Questo è un singolo array contiguo in memoria ed è più efficiente rispetto allo spazio di un array di celle, e potrebbe essere più veloce per le operazioni di confronto e così via. È anche una delle rappresentazioni di stringa nativa di Matlab, quindi le normali funzioni di stringa funzioneranno con esso.
  • Se le stringhe sono a bassa cardinalità (ovvero il numero di valori distinti è piccolo rispetto al numero totale di elementi), è possibile codificarli come "simboli", memorizzandoli come una matrice di valori primitivi che sono indici in ad una lista dei valori di stringa distinti. La funzione unique e ismember può aiutare a implementare queste codifiche. Finché si eseguono solo test di uguaglianza e non si ordina, queste colonne di stringhe codificate funzioneranno a velocità numerica.
  • Credo che una delle Toolbox, forse quella con dataset, supporti le variabili "classifier" o "categorical", che sono fondamentalmente un'implementazione pronte per la codifica a bassa cardinalità.
  • Non preoccupatevi di Java Strings; il sovraccarico nell'attraversare la barriera Matlab-to-Java lo renderà una perdita netta.
  • Speriamo che qualcuno abbia escogitato qualcos'altro ormai.

Se si devono attaccare con cellstrs, memorizzarli come vettori di colonna cellstr all'interno della struttura come descritto sopra; in questo modo devi solo pagare il prezzo dell'accesso alla cella quando stai effettivamente operando su una colonna di stringhe.

+0

+1 risposta ben spiegata e perspicace. Anche il problema di overhead OOP ha decisamente migliorato le prestazioni. Ad esempio, sto ricevendo solo 0,7 s per l'accesso 'dataset' in questo test (in R2013a) rispetto al 36 sec che l'OP ha segnalato – Amro

+0

Ovviamente c'è ancora spazio per miglioramenti. Sarebbe interessante se fosse possibile aggiornare i risultati del benchmark se si ha accesso all'ultima versione – Amro

+0

Grazie. Sì, l'aggiornamento sembra una buona idea. Anche le prestazioni non OOP dovrebbero migliorare. Ho paura che qualcun altro debba fare un benchmark aggiornato, però - ho cambiato lavoro e non ho una licenza Matlab al momento. –

Problemi correlati