2013-04-26 54 views
6

我需要處理大量的混合類型的表格數據 - 字符串和雙打。一個標準問題,我會想。 Matlab中處理這個問題的最好的數據結構是什麼?混合類型的Matlab數據結構 - 什麼是時間+空間高效?

Cellarray絕對不是答案。這是非常低效的內存。 (測試如下所示)。數據集(來自統計工具箱)在時間和空間上都非常低效。這給我留下了結構陣列或數組結構。我對下面的時間和內存的所有四個不同選項做了測試,在我看來,數組結構對於我測試的東西來說是最好的選擇。

我對Matlab比較新,坦白說這有點令人失望。無論如何 - 尋找我是否缺少東西的建議,或者我的測試是否準確/合理。除了訪問/轉換/內存使用情況之外,我還會錯過其他一些考慮事項,這些使用情況很可能會在我使用這些東西進行編碼時出現。 (使用R2010b)

* *測試#1:訪問速度訪問數據項。

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

* *測試#2:轉換速度和內存使用情況,我從這個測試下降數據集。

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

雖然'數據集'確實很慢,但您的計時非常緩慢。我在訪問時獲取「數據集:0.7s」,而其他人的順序與您的相同。我在32位的WinXP – Amro 2013-04-26 19:57:37

回答

1

我想說如果你需要管理大量的數據,那麼MATLAB並不是最好的選擇。找一個合適的分貝,並最終將你需要的數據導入MATLAB。

不過,如果你打算使用MATLAB反正我還是會選擇cellarrays,也就是說,如果你不需要在字段名結構形式數據的語法引用。

使用cellarrays時,請記住,每個單元施加112個字節的開銷。因此,我將創建一個單元格爲每列(不是每標量雙電池):

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

和內存明智的(在時間上沒有變化):

Name   Size    Bytes Class Attributes 
c    1x10   38000600 cell 

而且,你叫什麼數組的結構通常用「標量」結構來引用,而不是結構數組(或非標量結構)。

如果我沒有記錯,當你開始嵌套字段(我需要找到特定的線程)時,結構往往會降低讀/寫的性能。

3

使Matlab代碼節省空間和時間的方法是使用大型基元數組 - 即雙數,整數或字符數組。這給你一個更緊密的內存佈局,並讓你做矢量化操作。

對於表格數據,每列在類型上將是同質的,但不同的列可能是不同的類型,並且通常會有比列多得多的行。而且,您經常會對列的所有元素或列的蒙版選擇進行操作(比較或數學),從而使其自身進行向量化操作。因此將每列存儲爲一個列數組,希望是基元。您可以將這些列粘貼到結構體的字段或單元格向量的元素中;在性能方面並不重要,結構形式將更加可讀,看起來更像一張表。一個二維單元陣列或其他數據結構將所有元素分解到他們自己的小型mxarrays中並不會令人滿意。也就是說,如果你有一個10,000行10列的表,你希望有一個10長的單元數組或10個字段的結構,每個這些字段或元素都包含一個10,000長的基本列向量。

dataset對象對象基本上是一個圍繞像前面描述的列數組結構的包裝,卡在一個對象中。但是Matlab中的對象比常規的結構和單元有更大的開銷;您在每次訪問時支付一次或多次方法調用。看看Is MATLAB OOP slow or am I doing something wrong?(完全披露:這是我的答案之一)。

您設置的測試並不能說明Matlab代碼的執行效果,因爲它正在執行標量單元素訪問。也就是說,它支付列,然後通過循環的每次通過支付行元素訪問。如果你的Matlab代碼是這樣做的,那麼你已經運氣不好。爲了加快速度,您需要在循環之外彈出列 - 即將昂貴的列訪問操作提升到外部循環或設置代碼 - 然後執行向量化操作(如+==,'<',ismember,等等)整個列矢量上,或循環遍佈原始數字矢量(JIT可以優化)。如果你這樣做,那麼dataset和其他基於對象的表格結構可以有不俗的表現。

不幸的是,Matlab中的字符串很糟糕。你想擺脫手機。你有幾個選項。

  • 如果列中的字符串是大約相同的長度,並且不具有在其中任何長字符串,可以存儲的字符串作爲2 d char陣列的矢量。這是內存中的一個連續陣列,比單元陣列更節省空間,並且可能比較操作更快等等。它也是Matlab的本地字符串表示之一,所以普通的字符串函數將與它一起工作。
  • 如果字符串的基數很低(也就是說,不同值的數量相對於元素的總數量很小),則可以將它們編碼爲「符號」,將它們存儲爲原始ints數組,它們是索引in到不同的字符串值列表。函數uniqueismember可以幫助實現這些編碼。只要您只是在進行平等測試而不進行排序,這些編碼的字符串列將以數字速度運行。
  • 我相信其中一個工具箱,也許是dataset的工具箱,支持「分類器」或「分類」變量,這些變量基本上是低基數編碼的現成實現。
  • 不要打擾Java字符串;通過Matlab到Java屏障的開銷將使其成爲淨虧損。
  • 希望有人現在已經拿出了別的東西。

如果您必須堅持使用cellstrs,將它們作爲cellstr列向量存儲在結構中,如上所述;這樣,當您實際操作字符串列時,您只需支付單元訪問的價格。

+0

+1上運行R2013a,很好地解釋了這個問題,並提供了深刻的見解。此外,OOP開銷問題明顯改善了性能。舉例來說,在這個測試中(在R2013a中),對於'dataset'訪問只有0.7s,而OP報告的36秒是 – Amro 2013-04-27 14:56:32

+0

。當然,還有改進的空間。如果您有權訪問最新版本 – Amro 2013-04-27 15:03:32

+0

,那麼您可以更新基準測試結果將會很有趣。是的,升級聽起來像個好主意。非OOP性能也應該提高。但是怕別人不得不做更新的基準測試 - 我改變了工作,目前還沒有Matlab許可證。 – 2013-04-27 18:50:03