从HDF5文件中读取和写入numpy数组

时间:2017-04-26 13:15:14

标签: python numpy hdf5 pytables hdf

我正在构建模拟软件,我需要将(数千个)2D numpy数组写入HDF5文件中的表中,其中数组的一个维度是可变的。传入的array是float32类型;为了节省磁盘空间,每个数组都存储为具有适当数据类型的表(因此不使用数组)。当我读表时,我想检索一个float32类型的numpy.ndarray,所以我可以做很好的分析计算。下面是带有物种A,B和C加时间的数组的示例代码。

我目前阅读和写作的方式“有效”,但速度很慢。因此,问题是:将array快速存储到table中的适当方式是什么,并将其再次读回ndarrays?我一直在尝试使用numpy.recarray,但我不能让它工作(类型错误,尺寸错误,完全错误的数字等)?

代码:

import tables as pt
import numpy as np

# Variable dimension
var_dim=100

# Example array, rows 0 and 3 should be stored as float32, rows 1 and 2 as uint16
array=(np.random.random((4, var_dim)) * 100).astype(dtype=np.float32)

filename='test.hdf5'
hdf=pt.open_file(filename=filename,mode='w')
group=hdf.create_group(hdf.root,"group")

particle={
    'A':pt.Float32Col(),
    'B':pt.UInt16Col(),
    'C':pt.UInt16Col(),
    'time':pt.Float32Col(),
    }
dtypes=np.array([
    np.float32,
    np.uint16,
    np.uint16,
    np.float32
    ])

# This is the table to be stored in
table=hdf.create_table(group,'trajectory', description=particle, expectedrows=var_dim)

# My current way of storing
for i, row in enumerate(array.T):
    table.append([tuple([t(x) for t, x in zip(dtypes, row)])])
table.flush()
hdf.close()


hdf=pt.open_file(filename=filename,mode='r')
array_table=hdf.root.group._f_iter_nodes().__next__()

# My current way of reading
row_list = []
for i, row in enumerate(array_table.read()):
    row_list.append(np.array(list(row)))

#The retreived array
array=np.asarray(row_list).T


# I've tried something with a recarray
rec_array=array_table.read().view(type=np.recarray)

# This gives me errors, or wrong results
rec_array.view(dtype=np.float64)
hdf.close()

我得到的错误:

Traceback (most recent call last):
  File "/home/thomas/anaconda3/lib/python3.6/site-packages/numpy/core/records.py", line 475, in __setattr__
    ret = object.__setattr__(self, attr, val)
ValueError: new type not compatible with array.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/thomas/Documents/Thesis/SO.py", line 53, in <module>
    rec_array.view(dtype=np.float64)
  File "/home/thomas/anaconda3/lib/python3.6/site-packages/numpy/core/records.py", line 480, in __setattr__
    raise exctype(value)
ValueError: new type not compatible with array.
Closing remaining open files:test.hdf5...done

2 个答案:

答案 0 :(得分:2)

作为一种快速而肮脏的解决方案,可以通过暂时将阵列转换为列表来避免循环(如果可以节省内存)。由于某种原因,记录数组很容易转换为列表或从列表转换而不是转换为传统阵列。

储存:

table.append(array.T.tolist())

装载:

loaded_array = np.array(array_table.read().tolist(), dtype=np.float64).T

应该有更多的“Numpythonic”方法来转换记录数组和传统数组,但我不熟悉前者知道如何。

答案 1 :(得分:1)

我没有使用tables,但是用h5py查看了它的文件。我猜你的arrayrecarray是一个dtype类型的结构化数组:

In [131]: dt=np.dtype('f4,u2,u2,f4')
In [132]: np.array(arr.tolist(), float)
Out[132]: 
array([[ 1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.]])
In [133]: arr
Out[133]: 
array([( 1., 1, 1,  1.), ( 1., 1, 1,  1.), ( 1., 1, 1,  1.)], 
      dtype=[('f0', '<f4'), ('f1', '<u2'), ('f2', '<u2'), ('f3', '<f4')])

使用@kazemakase's tolist方法(我在其他帖子中推荐):

In [134]: np.array(arr.tolist(), float)
Out[134]: 
array([[ 1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.]])

astype形状错误

In [135]: arr.astype(np.float32)
Out[135]: array([ 1.,  1.,  1.], dtype=float32)

view在组件dtypes一致时起作用,例如使用2个浮点字段

In [136]: arr[['f0','f3']].copy().view(np.float32)
Out[136]: array([ 1.,  1.,  1.,  1.,  1.,  1.], dtype=float32)

但确实需要重塑。 view使用数据缓冲区字节,只需重新解释。

许多recfunctions函数使用字段副本字段。这里的等价物是

In [138]: res = np.empty((3,4),'float32')
In [139]: for i in range(4):
     ...:     res[:,i] = arr[arr.dtype.names[i]]
     ...:     
In [140]: res
Out[140]: 
array([[ 1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.]], dtype=float32)

如果字段数与记录数相比较小,则此迭代并不昂贵。

def foo(arr):
    res = np.empty((arr.shape[0],4), np.float32)
    for i in range(4):
        res[:,i] = arr[arr.dtype.names[i]]
    return res

使用大型4场阵列,旁场复制显然更快:

In [143]: arr = np.ones(10000, dtype=dt)
In [149]: timeit x1 = foo(arr)
10000 loops, best of 3: 73.5 µs per loop
In [150]: timeit x2 = np.array(arr.tolist(), np.float32)
100 loops, best of 3: 11.9 ms per loop