【问题标题】:Fastest way to slice a ndarray切片ndarray的最快方法
【发布时间】:2014-02-07 04:02:35
【问题描述】:

我有一些来自 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] 比我预期的要慢。关于如何解决这个问题的任何想法?

【问题讨论】:

    标签: python performance numpy hdf5 h5py


    【解决方案1】:

    是的,像您当前所做的那样迭代 numpy 数组相对较慢。通常,您会改用切片(创建视图,而不是将数据复制到列表中)。

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

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

    如果您使用的是 2D 整数数组而不是元组对象数组,您只需这样做:

    field1 = events[:,0]
    

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

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

    编辑

    啊!好的,你有一个structured array。换句话说,它是一个数组(在本例中为一维),其中 每个项目 是一个类似 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 用于“表格”数据。)

    如果您确实想将其转换为整数的二维数组,请执行以下操作:

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

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

    调用events = events.astype(int) 将有效地产生第一列。 (这是因为事件的每一项都是一个类似C的结构,而astype按元素操作,所以每个结构都被转换为一个int。)

    【讨论】:

      【解决方案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
      

      【讨论】:

      • +1 打败我!但是,看起来 OP 有一个对象数组而不是“普通”二维数组。因此,切片不起作用。
      • 不仅仅是速度。 OP 有一个一维数组。做a[:,0] 是行不通的。 OP 的数组可能相当于a = np.array([(1, 2), (3, 4, 5)])
      • @JoeKington 啊!我明白你的意思了。 ;-)
      • @sudo_O - 转换可能相当快,但从您发布的内容很难说。 HDF文件是什么格式的? (我知道它是 hdf,但是数据类型是什么格式?这是一个可变长度数组吗?)另外,print events.dtype 产生什么(在你用events=events[:] 将它读入 numpy 数组之前)?使用 2D 数组会更有效,但如果您要处理 hdf 文件中的可变长度数组,那不是一个选择。
      • @sudo_O - 新信息有很大帮助,谢谢! (我被void dtype 抛弃了。)你只想要events["start"]
      猜你喜欢
      • 2021-11-17
      • 1970-01-01
      • 2012-11-30
      • 2013-06-25
      • 2017-03-20
      • 1970-01-01
      • 1970-01-01
      • 2020-03-02
      • 2019-01-29
      相关资源
      最近更新 更多