我有一个1.6 GB大的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的方法。谢谢!
答案 0 :(得分:3)
考虑使用二进制文件格式。二进制文件要小得多,不需要通过MATLAB转换为二进制格式。因此,它们的读写速度要快得多。它们也可能更准确(精度可能更高)。
答案 1 :(得分: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发生一次,那么它会复制1 MB一次,接下来2 MB,然后再复制3 MB等等 - 你可以看到它是数组大小的二次方。
如果您为最终结果分配固定数量的内存,并以较小批量处理,则可以避免所有开销。我很确定它会更快 - 但你必须尝试一下块大小。这看起来像这样:
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);
答案 2 :(得分:1)
受@ Axon的回答启发,我实现了一个“快速”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
当然,现在如果你想将数字恢复为浮点数,你将不得不做一些工作;但我认为这是值得的。您必须解决的一个可能的问题是小数点后的值具有不同的位数:将(a,b)转换为浮点数并不像“a + b / 100”那样简单,当b> 100 ......“为学生锻炼”?
一点基准:上面的代码花了大约0.4秒。相比之下,我对textread
的第一个建议在同一个文件上花了大约9秒;你的原始代码花了11秒多一点。当文件变大时,差异可能会变大。
如果你这么做(如你所说),显然值得将你的文件转换为二进制格式,并以这种方式使用它们。特别是如果文件只需要转换一次,并且多次读取,那么节省的费用就相当可观。
<强>更新强>
我用13M的行文件重复了基准测试。转换花了13秒,二进制读取&lt; 3秒。相比之下,另外两种方法中的每一种都需要一分钟(文本扫描:61s; fscanf:77s)。事情似乎是线性扩展(文件大小470M文本,240M二进制)