Numpy数组切片

4
我有一个1D numpy数组和一些偏移/长度值。我想要从这个数组中提取所有在offset和offset+length之间的条目,并用它们来构建原始数组的新“缩小”数组,该数组仅由偏移/长度对选择的那些值组成。
对于单个偏移/长度对,使用标准数组切片[offset: offset+length]很容易实现。但是如何高效地(即不使用任何循环)为许多偏移/长度值执行此操作呢?
谢谢, 马克

那么,理想情况下,你最后会得到什么?一个二维数组吗? - Henry Gomersall
不,再次使用一个一维数组,该数组仅由根据偏移/长度值从原始一维数组中挑选出的值组成。 - Mark
我理解 offset/lentgh values 是某种类型的数组,或者你只是想将你的数组分成一系列更小的数组。 - Samy Vilar
是的,偏移量/长度都是数组。我不想进行分区,因为最终我想要一个一维数组。所以我需要拼接你提到的部分较小的数组,但都不需要循环。 - Mark
2个回答

6
>>> import numpy as np
>>> a = np.arange(100)
>>> ind = np.concatenate((np.arange(5),np.arange(10,15),np.arange(20,30,2),np.array([8])))
>>> a[[ind]]
array([ 0,  1,  2,  3,  4, 10, 11, 12, 13, 14, 20, 22, 24, 26, 28,  8])

1
顺便说一下,np.r_ 对于你使用 concatenate 做的事情非常不错。你冗长的连接行可以缩减为 ind = np.r_[:5, 10:15, 20:30:2, 8] - Joe Kington

5

有一种朴素的方法; 就是直接切片:

>>> import numpy as np
>>> a = np.arange(100)
>>> 
>>> offset_length = [(3,10),(50,3),(60,20),(95,1)]
>>>
>>> np.concatenate([a[offset:offset+length] for offset,length in offset_length])
array([ 3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 50, 51, 52, 60, 61, 62, 63,
       64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 95])

以下可能会更快,但需要测试/基准测试。

它的工作原理是构建所需索引列表,这是一种有效的numpy数组索引方法。

>>> indices = [offset + i for offset,length in offset_length for i in xrange(length)]
>>> a[indices]
array([ 3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 50, 51, 52, 60, 61, 62, 63,
       64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 95])

目前还不清楚这种方法是否比朴素方法更快,但如果你有很多非常短的时间间隔,那么可能会更快。但我不确定。

(这种最后的方法基本上与@fraxel的解决方案相同,只是使用了一种不同的制作索引列表的方法。)


性能测试

我测试了几种不同情况:少数短时间间隔、少数长时间间隔、大量短时间间隔。我使用了以下脚本:

import timeit

setup = 'import numpy as np; a = np.arange(1000); offset_length = %s'

for title, ol in [('few short', '[(3,10),(50,3),(60,10),(95,1)]'),
                  ('few long', '[(3,100),(200,200),(600,300)]'),
                  ('many short', '[(2*x,1) for x in range(400)]')]:
  print '**',title,'**'
  print 'dbaupp 1st:', timeit.timeit('np.concatenate([a[offset:offset+length] for offset,length in offset_length])', setup % ol, number=10000)
  print 'dbaupp 2nd:', timeit.timeit('a[[offset + i for offset,length in offset_length for i in xrange(length)]]', setup % ol, number=10000)
  print '    fraxel:', timeit.timeit('a[np.concatenate([np.arange(offset,offset+length) for offset,length in offset_length])]', setup % ol, number=10000)

这将输出:
** few short **
dbaupp 1st: 0.0474979877472
dbaupp 2nd: 0.190793991089
    fraxel: 0.128381967545
** few long **
dbaupp 1st: 0.0416231155396
dbaupp 2nd: 1.58000087738
    fraxel: 0.228138923645
** many short **
dbaupp 1st: 3.97210478783
dbaupp 2nd: 2.73584890366
    fraxel: 7.34302687645

这表明当你只有几个间隔时(而且速度显著更快),我的第一种方法是最快的,而当你有很多间隔时,我的第二种方法是最快的。

这正是我想要的,但有没有一种方法可以不使用for循环来获得它? - Mark
1
@MarkVogelsberger,您是因为性能原因要删除for循环吗?如果是这样,您应该测试这些(以及fraxel的)以查看它们是否足够快,以便您可以避免进行不必要的微观优化:只有在这些都不够快时,您才需要担心完全删除for循环。 - huon
@MarkVogelsberger,我在我的回答中添加了一些性能统计数据。 - huon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接