2013-04-26 5 views
6

Ich muss große Mengen von Tabellendaten gemischten Typs verarbeiten - Strings und Doubles. Ein Standardproblem, würde ich denken. Was ist die beste Datenstruktur in Matlab für die Arbeit mit diesem?Matlab Datenstruktur für gemischten Typ - was ist zeit- und platzsparend?

Cellarray ist definitiv nicht die Antwort. Es ist extrem speicherineffizient. (Tests unten gezeigt). Dataset (von stats toolbox) ist furchtbar zeit- und platzineffizient. Das lässt mich mit structarray oder Struktur von Arrays. Ich habe einen Test über alle vier verschiedenen Optionen für Zeit und Speicher unten durchgeführt und es scheint mir, dass die Struktur der Arrays die beste Option für die Dinge ist, für die ich getestet habe.

Ich bin relativ neu in Matlab und das ist ein bisschen enttäuschend, ehrlich gesagt. Wie auch immer - auf der Suche nach Ratschlägen, ob ich etwas vermisse oder ob meine Tests genau/angemessen sind. Fehle ich andere Überlegungen neben dem Zugriff/Konvertierung/Speichernutzung, die wahrscheinlich kommen, wenn ich mehr Code mit diesem Zeug code. (fyi mit R2010b)

* * Test # 1: Zugriffsgeschwindigkeit Zugriff auf ein Datenelement.

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

* * Test # 2: Umwandlung Geschwindigkeit und Speicherverbrauch sank I-Datensatz aus diesem Test.

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 

================== CODE: 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); 

============= ===== Code: 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

während "Dataset" in der Tat langsam ist, ist Ihr Timing schrecklich langsam. Ich bekomme 'Dataset: 0.7s' beim Zugriff, während die anderen in der gleichen Reihenfolge wie deine sind. Im Ausführen von R2013a auf 32-Bit WinXP – Amro

Antwort

1

ich würde sagen, dass, wenn Sie große Datenmengen verwalten müssen, dann MATLAB ist nicht die beste Wahl zu beginnen. Suchen Sie nach einer geeigneten Datenbank und importieren Sie schließlich die benötigten Daten in MATLAB.

Wenn Sie jedoch MATLAB planen verwenden sowieso ich wählen würde, noch cellarrays, das heißt, wenn Sie nicht syntaktische Hinweise auf Ihre Daten in Form von Feldnamen wie in Strukturen brauchen.

Bei der Verwendung von cellarrays ist zu beachten, dass jede Zelle 112 Bytes Overhead benötigt. Deshalb würde ich eine Zelle für jede Spalte (keine Zelle für jeden skalaren double) erstellen:

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

und Speicher-weise (keine Änderungen in der Zeit):

Name   Size    Bytes Class Attributes 
c    1x10   38000600 cell 

Auch, was Sie nennen Eine Struktur eines Arrays wird normalerweise mit einer Skalarstruktur im Gegensatz zu einem Strukturarray (oder einer nicht skalaren Struktur) bezeichnet.

Wenn ich mich richtig erinnere, tendieren Strukturen dazu, die Leistung für Lese-/Schreibvorgänge zu verschlechtern, wenn Sie Felder verschachteln (ich muss jedoch den spezifischen Thread finden).

3

Um Matlab-Code raum- und zeiteffizient zu machen, ist es wichtig, mit großen Arrays von Primitiven zu arbeiten - also Arrays aus Doubles, Ints oder Chars. Dadurch erhalten Sie ein dichteres Layout im Speicher und können vektorisierte Operationen ausführen.

Für Tabellendaten wird jede Spalte homogen in ihrem Typ sein, aber verschiedene Spalten können unterschiedlicher Art sein, und Sie haben normalerweise viel mehr Zeilen als Spalten. Und Sie werden oft Operationen - Vergleiche oder Berechnungen - über alle Elemente einer Spalte oder eine maskierte Auswahl einer Spalte durchführen, die sich für vektorisierte Operationen eignet. Speichern Sie daher jede Spalte als ein Spaltenarray, hoffentlich von Primitiven. Sie können diese Spalten entweder in Felder einer Struktur oder in Elemente eines Zellenvektors einfügen; Leistung spielt keine große Rolle, und die Strukturform wird viel besser lesbar sein und mehr wie eine Tabelle aussehen. Ein 2-d-Zellenarray oder eine andere Datenstruktur, die alle Elemente in ihre eigenen kleinen mxarrays aufteilt, wird nicht akzeptabel ausgeführt.

Das heißt, wenn Sie eine Spalte mit 10.000 Zeilen mit 10 Spalten haben, möchten Sie ein 10-Zeilen- oder 10-Feld-Struktur haben, wobei jedes dieser Felder oder Elemente einen 10.000-langen primitiven Spaltenvektor enthält.

Das dataset Objekt Objekt ist im Grunde ein Wrapper um eine Struktur von Spalten-Arrays wie zuvor beschrieben, in einem Objekt stecken. Aber Objekte in Matlab haben einen größeren Overhead als reguläre Strukturen und Zellen; Sie zahlen für jeden Zugriff auf einen oder mehrere Methodenaufrufe. Schauen Sie sich Is MATLAB OOP slow or am I doing something wrong? (vollständige Offenlegung: das ist eine meiner Antworten).

Der Test, den Sie eingerichtet haben, ist kein Hinweis darauf, wie gut Matlab-Code funktioniert, weil er skalare Einzelelementzugriffe ausführt. Das heißt, es zahlt für den Spalten- und dann den Zeilenelementzugriff bei jedem Durchgang durch die Schleife. Wenn Ihr Matlab-Code das tut, haben Sie schon Pech. Um schnell zu sein, müssen Sie Spalten außerhalb einer Schleife öffnen - dh die teure Spaltenzugriffsoperation auf eine äußere Schleife oder einen Setup-Code hieven - und dann entweder vektorisierte Operationen (wie +, ==, '<', ismember, usw.) auf ganzen Spaltenvektoren oder Schleife über primitive numerische Vektoren (die der JIT optimieren kann). Wenn Sie dies tun, können dataset und andere objektbasierte Tabellenstrukturen eine anständige Leistung haben.

Saiten in Matlab Art von saugen, leider. Du willst weg von Zellern. Sie haben ein paar Optionen.

  • Wenn die Saiten in einer Spalte von etwa der gleichen Länge sind, und Sie haben keine langen Strings in ihnen, können Sie einen Vektor von Strings speichern als 2-D char Array. Dies ist ein einzelnes zusammenhängendes Array im Speicher und ist platzsparender als ein Zellen-Array und kann für Vergleichsoperationen usw. schneller sein. Es ist auch eine von Matlabs nativen String-Repräsentationen, so dass normale String-Funktionen damit funktionieren.
  • Wenn die Zeichenfolgen eine geringe Kardinalität aufweisen (dh die Anzahl der einzelnen Werte ist relativ zur Gesamtzahl der Elemente klein), können Sie sie als "Symbole" kodieren, indem Sie sie als ein Array primitiver Zeichenfolgen speichern, die Indizes sind zu einer Liste der verschiedenen String-Werte. Die unique und ismember Funktion kann helfen, diese Kodierungen zu implementieren. Solange Sie nur Gleichheitstests durchführen und nicht sortieren, arbeiten diese codierten String-Spalten mit numerischer Geschwindigkeit.
  • Ich glaube, eine der Toolboxes, vielleicht die mit dataset, hat Unterstützung für "Classifier" oder "kategorische" Variablen, die im Grunde eine vorgefertigte Implementierung dieser Codierung mit geringer Kardinalität sind.
  • Mach dir keine Sorgen mit Java Strings; Der Overhead beim Überqueren der Matlab-Java-Barriere wird einen Nettoverlust bedeuten.
  • Hoffentlich hat sich jemand inzwischen etwas anderes einfallen lassen.

Wenn Sie mit cellstrs haften haben, speichern Sie sie als cellstr Spaltenvektoren innerhalb der Struktur, wie oben beschrieben; Auf diese Weise müssen Sie nur den Preis für den Zellenzugriff bezahlen, wenn Sie tatsächlich an einer String-Spalte arbeiten.

+0

+1 gut erklärt und aufschlussreiche Antwort. Auch das OOP-Overhead-Problem hat sich definitiv leistungsmäßig verbessert. Zum Beispiel bekomme ich nur 0.7s für 'Dataset' Zugriff in diesem Test (in R2013a) vs. 36 Sek. Das OP hat berichtet – Amro

+0

Natürlich gibt es noch Raum für Verbesserungen. Es wäre interessant, wenn Sie Ihre Benchmark-Ergebnisse aktualisieren könnten, wenn Sie Zugriff auf die neueste Version – Amro

+0

haben. Vielen Dank. Ja, Upgrade klingt wie eine gute Idee. Non-OOP-Leistung sollte ebenfalls verbessert werden. Angst, dass jemand anderes einen aktualisierten Benchmark machen muss - ich habe den Job gewechselt und habe momentan keine Matlab-Lizenz. –