2013-06-12 66 views
2

我有一個CSV文件1.6 GB大,我需要飼料到matlab。我將不得不經常這樣做,我需要它快速運行。該文件的格式爲:海量CSV文件到Matlab

20111205 00:00.2 99.18 6 E 
20111205 00:00.2 99.18 5 E 
20111205 00:00.2 99.18 1 E 
20111205 00:00.2 99.195 5 E 
20111205 00:00.2 99.195 5 E 
20111205 01:27.0 99.19 5 E 
20111205 02:01.4 99.185 1 E 
20111205 02:01.4 99.185 1 E 
20111205 02:01.4 99.185 1 E 
20111205 02:01.4 99.185 1 E 

我現在所擁有的代碼如下:

tic; 
format long g 
fid = fopen('C:\Program Files\MATLAB\R2013a\EDU13.csv','r'); 
[c] = fscanf(fid, '%d,%d:%d.%d,%f,%d,%c'); 
c = reshape(c, 7, length(c)/7) 
toc; 

但這實在太慢。我希望能夠以最有效的方式將此CSV文件轉換爲matlab。謝謝!

+2

「太慢了」/「快速運行」 - >你能用一個單位時間表示嗎? –

+0

另外,重塑命令需要多長時間?我想大多數時間都在閱讀文件,但你可以檢查嗎? – Engineero

+0

你有足夠的內存來保存整個陣列嗎?大約35字節的行中的1.6 GB(目測)意味着大約5000萬行 - 次8個字節的7個數= 2.8 GB。在您重新塑造時,可能需要兩個陣列副本。大到足以檢查這不是問題... – Floris

回答

1

推薦的語法textscan(http://www.mathworks.com/help/matlab/ref/textscan.html

您的代碼應該是這樣的:

fid = fopen('C:\Program Files\MATLAB\R2013a\EDU13.csv','r'); 
c = textscan(fid, '%d,%d:%d.%d,%f,%d,%c'); 
fclose(fid); 

你結束了一個單元陣列......是否值得是轉換到另一個形狀實際上取決於關於以後如何訪問數據。

如果包含一個循環,可以讓您在大部分操作中使用較小的固定內存量,這很可能會更快。閱讀大文件的一個問題是,事先你不知道它會有多大 - 這很可能意味着Matlab猜測它需要的內存量,並且經常需要重新調整。這是一個非常緩慢的操作 - 如果每1MB發生一次,那麼它會複製1MB一次,接下來的2MB,然後再複製3MB等等 - 正如您所看到的,它是陣列大小的二次方。

如果您爲最終結果分配固定數量的內存,並以較小的批次進行處理,則可避免所有這些開銷。我很確定它會更快 - 但你必須嘗試一下塊大小。這看起來像這樣:

block = 1000; 
Nlines = 35E6; 
fid = fopen('C:\Program Files\MATLAB\R2013a\EDU13.csv','r'); 
c = struct(field1, field2, fieldn, value); %... initialize structure array or other storage for c ... 
c_offset = 0; 
while ~feof(fid) 
    temp = textscan(fid, '%d,%d:%d.%d,%f,%d,%c', block); 
    bt = size(temp, 1); % first dimension - should be `block`, except for last loop 
    %... extract, process, store in c(c_offset + (1:bt))... 
    c_offset = c_offset + bt; 
end 
fclose(fid); 
+0

感謝您的迴應!我認爲你對大部分時間都需要重新調整是正確的。我試圖弄清楚如何運行你的代碼,你能告訴我第四行是幹什麼的嗎? field1等應該是什麼? – siegel

+0

第4行的想法是一次創建存儲空間。我不知道你想要什麼內存中的數據格式 - 所以我建議「一般」結構初始化。我爲速記道歉。下面一行在語法上是正確的,並且顯示了這樣一個想法:'c = struct('date',cell(1,Nlines),'time',cell(1,Nlines),'effort',cell(1,Nlines), 'flag',cell(1,Nlines));' - 它創建一個'Nlines'元素結構數組,並且可以使用'c(4).date'等來訪問元素,但是請參閱我的其他答案以獲得更快的速度...... – Floris

3

考慮使用二進制文件格式。二進制文件要小得多,不需要用MATLAB轉換成二進制格式。因此他們閱讀和寫作要快得多。它們也可能更準確(精度可能更高)。

http://www.mathworks.com.au/help/matlab/ref/fread.html

+0

+ +1爲一個非常合理的建議,尤其是當你「需要做很多事情」時,即使它不是OP所要求的...不需要轉換ASCII字符將節省大量時間 - 使用二進制文件你通常是I/O速度受限的。 – Floris

+0

謝謝軸突!我應該如何將我的CSV文件轉換爲二進制文件? – siegel

1

通過@軸突的回答啓發,我實現了一個「快」 C程序將文件轉換爲二進制文件,然後使用Matlab的fread功能閱讀。擾流警報:讀數然後快20倍......雖然初始轉換需要一點時間。

爲了使Matlab中的工作更容易,文件大小更小,我將每個數字字段轉換爲一個int16(短整數)。對於第一個領域 - 看起來像一個yyyymmdd領域 - 涉及分裂成兩個較小的數字;同樣的十進制數字轉換爲兩個短整數(給定我認爲有效的表觀範圍)。所有這些都意識到「要真正優化,你必須真正瞭解你的問題」 - 所以如果假設無效,結果也會如此。

這裏是C代碼:

#include <stdio.h> 
int main(){ 
    FILE *fp, *fo; 
    long int ld1; 
    int d2, d3, d4, d5, d6, d7; 
    short int buf[9]; 
    char c8; 
    int n; 
    short int year, monthday; 
    fp = fopen("bigdata.txt", "r"); 
    fo = fopen("bigdata.bin", "wb"); 
    if (fp == NULL || fo == NULL) { 
    printf("unable to open file\n"); 
    return 1; 
    } 
    while(!feof(fp)) { 
    n = fscanf(fp, "%ld %d:%d.%d %d.%d %d %c\n", \ 
     &ld1, &d2, &d3, &d4, &d5, &d6, &d7, &c8); 
    year = d1/10000; 
    monthday = d1 - 10000 * year; 
    // move everything into buffer for single call to fwrite: 
    buf[0] = year; 
    buf[1] = monthday; 
    buf[2] = d2; 
    buf[3] = d3; 
    buf[4] = d4; 
    buf[5] = d5; 
    buf[6] = d6; 
    buf[7] = d7; 
    buf[8] = c8; 
    fwrite(buf, sizeof(short int), 9, fo); 
    } 
    fclose(fp); 
    fclose(fo); 
    return 0; 
} 

得到的文件是原來的一半大小 - 這是令人鼓舞的,將加快進入。請注意,如果輸出文件可以寫入與輸入文件不同的磁盤,這將是一個不錯的主意 - 它確實有助於在查找操作中浪費大量時間而不浪費大量時間。基準:使用一個2 M行的文件作爲輸入,它在大約2秒鐘內運行(相同的磁盤)。產生的二進制文件與下面的閱讀在Matlab:

tic 
fid = fopen('bigdata.bin'); 
d = fread(fid, 'int16'); 
d = reshape(d, 9, []); 
toc 

當然,現在如果你要恢復的數字爲浮點數,你必須做的工作一點點;但我認爲這是值得的。您需要解決的一個可能的問題是小數點後的值具有不同數字位數的情況:當b> 100時,將(a,b)轉換爲浮點數不像「a + b/100」那麼簡單...「學生練習」?

有點基準:上面的代碼花了大約0.4秒。相比之下,我對textread的第一個建議在同一個文件上花了大約9秒;並且您的原始代碼需要超過11秒。當文件變大時,差異可能會變大。

如果你這麼做(如你所說),顯然值得將文件轉換爲二進制格式,並以這種方式使用它們。特別是如果文件只需轉換一次,並多次讀取,則節省的成本相當可觀。

更新

我反覆基準用13M線文件。轉換花費了13秒,二進制讀取了3秒<。相比之下,其他兩種方法都花了一分鐘(textscan:61s; fscanf:77s)。看起來事情是線性縮放的(文件大小470M文本,240M二進制文件)

+0

使用此代碼: 'tic; format long g fid = fopen('C:\ Program Files \ MATLAB \ R2013a \ TUM12.txt','r'); Nrows = numel(textread('TUM12.txt','%1c%* [^ \ n]')) [c] = fscanf(fid,'%d,%d:%d:%d。 %d,%F,%d,%C」,NROWS); toc;' 對一個文件1050萬行(400,000kb)需要25秒 – siegel

+0

是否比以前快得多?或者是你稱之爲「太慢」?它比我測量的速度快了2倍(我的速度比你的420k大約每秒210k) - 但是會有硬件差異(我在單核上運行--CPU = 100%)。我會很好奇,看看二進制解決方案是否可以幫助你 - 它應該有所作爲,特別是如果你多次閱讀相同的文件。 – Floris

+0

我真的很感謝你的C解決方案!你知道是否有可能使用matlab轉換爲二進制文件?這隻需要做一次,所以我不介意轉換成二進制文件需要多長時間。我將以二進制格式存儲文件並多次讀取它們。 'fid = fopen('myFile.txt','r'); F = FREAD(FID,「字符=> UINT32」)'' 似乎這樣的伎倆,但我不知道如何轉換回再次浮點。 – siegel