与 Matlab 相比,Numpy 加载 csv 太慢了

Posted

技术标签:

【中文标题】与 Matlab 相比,Numpy 加载 csv 太慢了【英文标题】:Numpy loading csv TOO slow compared to Matlab 【发布时间】:2013-08-18 00:55:12 【问题描述】:

我发布这个问题是因为我想知道我是否做错了什么才能得到这个结果。

我有一个中等大小的 csv 文件,我尝试使用 numpy 来加载它。为了说明,我使用python制作了文件:

import timeit
import numpy as np

my_data = np.random.rand(1500000, 3)*10
np.savetxt('./test.csv', my_data, delimiter=',', fmt='%.2f')

然后,我尝试了两种方法:numpy.genfromtxt,numpy.loadtxt

setup_stmt = 'import numpy as np'
stmt1 = """\
my_data = np.genfromtxt('./test.csv', delimiter=',')
"""
stmt2 = """\
my_data = np.loadtxt('./test.csv', delimiter=',')
"""

t1 = timeit.timeit(stmt=stmt1, setup=setup_stmt, number=3)
t2 = timeit.timeit(stmt=stmt2, setup=setup_stmt, number=3)

结果显示t1 = 32.159652940464184, t2 = 52.00093725634724。 但是,当我尝试使用 matlab 时:

tic
for i = 1:3
    my_data = dlmread('./test.csv');
end
toc

结果显示:经过的时间是3.196465秒

我了解加载速度可能存在一些差异,但是:

    这远远超出了我的预期; 不是说np.loadtxt应该比np.genfromtxt快吗? 我还没有尝试过 python csv 模块,因为加载 csv 文件是我经常做的事情,而且使用 csv 模块,编码有点冗长......但如果是这样的话,我很乐意尝试唯一的办法。目前我更担心是不是我做错了什么。

任何意见将不胜感激。提前非常感谢!

【问题讨论】:

【参考方案1】:

如果您只想保存和读取 numpy 数组,最好根据大小将其保存为二进制或压缩二进制:

my_data = np.random.rand(1500000, 3)*10
np.savetxt('./test.csv', my_data, delimiter=',', fmt='%.2f')
np.save('./testy', my_data)
np.savez('./testz', my_data)
del my_data

setup_stmt = 'import numpy as np'
stmt1 = """\
my_data = np.genfromtxt('./test.csv', delimiter=',')
"""
stmt2 = """\
my_data = np.load('./testy.npy')
"""
stmt3 = """\
my_data = np.load('./testz.npz')['arr_0']
"""

t1 = timeit.timeit(stmt=stmt1, setup=setup_stmt, number=3)
t2 = timeit.timeit(stmt=stmt2, setup=setup_stmt, number=3)
t3 = timeit.timeit(stmt=stmt3, setup=setup_stmt, number=3)

genfromtxt 39.717250824
save 0.0667860507965
savez 0.268463134766

【讨论】:

谢谢 Ophion!这是一个很好的答案,而且非常有用——我一直在使用 cPickle,但现在意识到 np.savez 比 cPickle 更快、更紧凑,只要只使用 ndarray。我没有标记“接受”,因为在这个问题中,我试图从 LabVIEW 保存的实验数据中读取数据。不过,还是非常感谢! 我相信应该选这个作为正确答案!谢谢@Ophion【参考方案2】:

是的,将csv 文件读入numpy 非常慢。代码路径上有很多纯 Python。这些天来,即使我使用纯 numpy,我仍然使用 pandas 进行 IO:

>>> import numpy as np, pandas as pd
>>> %time d = np.genfromtxt("./test.csv", delimiter=",")
CPU times: user 14.5 s, sys: 396 ms, total: 14.9 s
Wall time: 14.9 s
>>> %time d = np.loadtxt("./test.csv", delimiter=",")
CPU times: user 25.7 s, sys: 28 ms, total: 25.8 s
Wall time: 25.8 s
>>> %time d = pd.read_csv("./test.csv", delimiter=",").values
CPU times: user 740 ms, sys: 36 ms, total: 776 ms
Wall time: 780 ms

或者,在像这样一个足够简单的情况下,您可以使用 Joe Kington 写的 here:

>>> %time data = iter_loadtxt("test.csv")
CPU times: user 2.84 s, sys: 24 ms, total: 2.86 s
Wall time: 2.86 s

还有 Warren Weckesser 的 textreader 库,以防 pandas 依赖太重:

>>> import textreader
>>> %time d = textreader.readrows("test.csv", float, ",")
readrows: numrows = 1500000
CPU times: user 1.3 s, sys: 40 ms, total: 1.34 s
Wall time: 1.34 s

【讨论】:

非常感谢! pd.read_csv 对我来说非常有用——事实上,它只用了 MATLAB 一半的时间就完成了!还要感谢其他两种非常有用且重量更轻的方法。 速度并不是唯一需要关心的事情。对于我来说,np.genfromtxtpd.read_csv 需要的 RAM 比读取 1,209,836,036 字节文本文件所需的 RAM 还要多。前者不关心并挂起系统,但后者抛出错误。 np.fromfile 几乎比 np.loadtxt 快 4 倍。这两个运行起来并不占用太多内存。【参考方案3】:

也许最好安装一个简单的 c 代码,将数据转换为二进制文件并让 `numpy' 读取二进制文件。我有一个 20GB 的 CSV 文件要读取,CSV 数据是 int、double、str 的混合体。 Numpy 读取结构数组需要一个多小时,而转储到二进制文件大约需要 2 分钟,加载到 numpy 需要不到 2 秒!

例如,我的特定代码可用here。

【讨论】:

效果不错。考虑为其他人删除示例代码。【参考方案4】:

FWIW 内置的 csv 模块效果很好,而且真的没有那么冗长。

csv 模块:

%%timeit
with open('test.csv', 'r') as f:
    np.array([l for l in csv.reader(f)])


1 loop, best of 3: 1.62 s per loop

np.loadtext:

%timeit np.loadtxt('test.csv', delimiter=',')

1 loop, best of 3: 16.6 s per loop

pd.read_csv:

%timeit pd.read_csv('test.csv', header=None).values

1 loop, best of 3: 663 ms per loop

我个人喜欢使用 pandas read_csv,但是当我使用纯 numpy 时,csv 模块很好。

【讨论】:

我知道这是一个老问题,但如果你还在使用纯numpy,你仍然可以使用pandas进行IO,然后使用`pd.DataFrame.values提取numpy数组。【参考方案5】:

我用perfplot(我的一个小项目)对建议的解决方案进行了性能测试,发现

pandas.read_csv(filename)

确实是最快的解决方案(如果读取的条目超过 2000 个,那么在此之前,一切都在毫秒范围内)。它比 numpy 的变体高出大约 10 倍。(numpy.fromfile 在这里只是为了比较,它无法读取实际的 csv 文件。)

重现情节的代码:

import numpy
import pandas
import perfplot

numpy.random.seed(0)
filename = "a.txt"


def setup(n):
    a = numpy.random.rand(n)
    numpy.savetxt(filename, a)
    return None


def numpy_genfromtxt(data):
    return numpy.genfromtxt(filename)


def numpy_loadtxt(data):
    return numpy.loadtxt(filename)


def numpy_fromfile(data):
    out = numpy.fromfile(filename, sep=" ")
    return out


def pandas_readcsv(data):
    return pandas.read_csv(filename, header=None).values.flatten()


def kington(data):
    delimiter = " "
    skiprows = 0
    dtype = float

    def iter_func():
        with open(filename, "r") as infile:
            for _ in range(skiprows):
                next(infile)
            for line in infile:
                line = line.rstrip().split(delimiter)
                for item in line:
                    yield dtype(item)
        kington.rowlength = len(line)

    data = numpy.fromiter(iter_func(), dtype=dtype).flatten()
    return data


b = perfplot.bench(
    setup=setup,
    kernels=[numpy_genfromtxt, numpy_loadtxt, numpy_fromfile, pandas_readcsv, kington],
    n_range=[2 ** k for k in range(23)],
)
b.save("out.png")

【讨论】:

以上是关于与 Matlab 相比,Numpy 加载 csv 太慢了的主要内容,如果未能解决你的问题,请参考以下文章

将 csv 文件加载到 numpy 并按名称访问列

如何将 CSV 文件中的数据加载到 numpy 数组中[重复]

Numpy - 将具有第一行的csv作为名称立即加载到结构化数组中?

Numpy - 将具有第一行的csv作为名称立即加载到结构化数组中?

使用 pandas 或 numpy 从一个 csv 加载多个数据帧

使用 numpy 将 csv 加载到二维矩阵中以进行绘图