将 csv 列加载到 numpy 内存映射中(快速)

loading csv column into numpy memmap (fast)

我有一个包含两列的 csv 文件,其中包含来自示波器的测量值:

Model,MSO4034
Firmware Version,2.48
# ... (15 lines of header) ...
-5.0000000e-02,-0.0088
-4.9999990e-02,0.0116
-4.9999980e-02,0.006
-4.9999970e-02,-0.0028
-4.9999960e-02,-0.002
-4.9999950e-02,-0.0028
-4.9999940e-02,0.0092
-4.9999930e-02,-0.0072
-4.9999920e-02,-0.0008
-4.9999910e-02,-0.0056

我想将此数据加载到 numpy 数组中。我可以使用 np.loadtxt:

np.loadtxt('data.csv', delimiter=',', skiprows=15, usecols=[1])

但是,我的数据文件很大(100 MSamples),加载和解析需要 numpy 超过半小时(每 1000 行 21.5 毫秒)。

我的首选方法是直接为 numpy 创建一个 Memory Map 文件,它只包含 二进制值,并连接成一个文件 。它基本上是内存中的数组,只是它不是在内存中而是在磁盘上。


问题

有什么方便的方法吗? 使用 Linux,我可以 tail away the header and cut 出第二列,但在将其写入磁盘上的二进制文件之前,我仍然需要解析值字符串表示形式:

$ tail -n +16 data.csv | cut -d',' -f2
-0.0088
0.0116
0.006
-0.0028
-0.002
-0.0028
0.0092
-0.0072
-0.0008
-0.0056

是否有Linux命令解析浮点数的字符串表示并将它们写入磁盘

由于你的数据在磁盘上,你必须先导入它,这会很昂贵。

我认为今天最好的 csv reader 是 pandas

In [7]: %timeit v=pd.read_csv('100ksamples.csv',sep=',')
1 loop, best of 3: 276 ms per loop # for 100k lines

这似乎比您的测试好 10 倍(但它取决于磁盘)。

之后可以使用pickle之类的工具以二进制方式保存,节省时间。

In [8]: %timeit with open('e.pk','bw') as f : pickle.dump(v,f)
100 loops, best of 3: 16.2 ms per loop

In [9]: %timeit with open('e.pk','br') as f : v2=pickle.load(f)
100 loops, best of 3: 8.64 ms per loop 

我还建议使用 Pandas' CSV 解析器,但我不会一次将整个文件读入内存,而是分块迭代它并将它们写入内存映射数组飞:

import numpy as np
from numpy.lib.format import open_memmap
import pandas as pd

# make some test data
data = np.random.randn(100000, 2)
np.savetxt('/tmp/data.csv', data, delimiter=',', header='foo,bar')

# we need to specify the shape and dtype in advance, but it would be cheap to
# allocate an array with more rows than required since memmap files are sparse.
mmap = open_memmap('/tmp/arr.npy', mode='w+', dtype=np.double, shape=(100000, 2))

# parse at most 10000 rows at a time, write them to the memmaped array
n = 0
for chunk in pd.read_csv('/tmp/data.csv', chunksize=10000):
    mmap[n:n+chunk.shape[0]] = chunk.values
    n += chunk.shape[0]

print(np.allclose(data, mmap))
# True

您可以根据一次可以放入内存的文件大小来调整块大小。请记住,在解析块时,您需要将原始文本和转换后的值保存在内存中。