切片ndarray的最快方法

时间:2014-01-17 17:44:04

标签: python performance numpy hdf5 h5py

我有来自HDF5文件的一些事件数据:

>>> events
<class 'h5py._hl.dataset.Dataset'>

我得到的数组数据如下:

>>> events = events[:]

结构是这样的:

>>> type(events)
<type 'numpy.ndarray'>
>>> events.shape
(273856,)
>>> type(events[0])
<type 'numpy.void'>
>>> events[0]
(0, 30, 3523, 5352)
>>> # More information on structure 
>>> [type(e) for e in events[0]]    
[<type 'numpy.uint64'>, 
 <type 'numpy.uint32'>, 
 <type 'numpy.float64'>, 
 <type 'numpy.float64'>]   
>>> events.dtype 
[('start', '<u8'), 
 ('length', '<u4'), 
 ('mean', '<f8'), 
 ('variance', '<f8')]

我需要获得特定事件的最大索引,其中第一个字段小于某个值。蛮力方法是:

>>> for i, e in enumerate(events):
>>>     if e[0] >= val:
>>>         break

元组的第一个索引是排序的,所以我可以做二分,所以加快速度:

>>> field1 = [row[0] for row in events]
>>> index = bisect.bisect_right(field1, val)

这显示改善,但[row[0] for row in event]比我预期的要慢。关于如何解决这个问题的任何想法?

2 个答案:

答案 0 :(得分:4)

是的,当你正在做的时,迭代numpy数组相对较慢。通常,您将使用切片(创建视图,而不是将数据复制到列表中)。

看起来你有一个对象数组。这会让事情变得更慢。你真的需要一个对象数组吗?看起来所有值都是int s。 (这是“vlen”hdf5数据集吗?)

对象数组有意义的用例是events的每个元素中有不同数量的项。如果不这样做,则没有理由使用它。

如果您使用的是int数组而不是元组的对象数组,那么您只需:

field1 = events[:,0]

但是,在这种情况下,您可以这样做:(searchsorted使用二分法)

index = np.searchsorted(events[:,0], val)

修改

啊!好的,你有一个structured array。换句话说,它是一个数组(在这种情况下为1D),其中每个项是一个类似C的结构。从:

>>> events.dtype 
[('start', '<u8'), 
 ('length', '<u4'), 
 ('mean', '<f8'), 
 ('variance', '<f8')]

...我们可以看到第一个字段名为“start”。

因此,您只想:

index = np.searchsorted(events["start"], val)

更一般地说,如果我们不知道该字段的名称,但知道它是某种结构化数组,那么你就可以了(将事情简化为切片步骤):

events[event.dtype.names[0]]

至于将所有内容转换为“普通”2D整数数组是一个好主意,这取决于您的用例。对于基本切片和调用searchsorted,没有理由。不应该(未经测试)任何显着的速度增加。

根据你目前所做的事情,我只是保持原样。

但是,结构化数组通常很难处理。

有很多情况下结构化数组非常有用(例如从磁盘读取某些二进制格式),但如果你想把它想象成“类似表”的数组,你很快就会遇到痛点。您通常最好将列存储为单独的数组。 (或者更好的是,使用pandas.DataFrame表示“表格”数据。)

如果您确实想将其转换为2D数组,请执行以下操作:

events = np.hstack([events[name] for name in events.dtype.names])

这将自动为新数组找到兼容的数据类型(在本例中为int64),并将结构化数组的字段“堆叠”为2D数组中的列。

调用events = events.astype(int)实际上只会产生第一列。 (这是因为事件的每个项都是类似C的结构,而astype是按元素运行的,因此每个结构都转换为单个int。)

答案 1 :(得分:2)

您可以使用numpy.searchsorted

>>> a = np.arange(10000).reshape(2500,4)
>>> np.searchsorted(a[:,0], 1000)
250

时间比较

>>> %timeit np.searchsorted(a[:,0], 1000)
100000 loops, best of 3: 11.7 µs per loop
>>> %timeit field1 = [row[0] for row in a];bisect.bisect_right(field1, 1000)
100 loops, best of 3: 2.63 ms per loop