Python 围绕第二个2D数组给出的索引,从1D numpy数组高效地切片窗口

Python 围绕第二个2D数组给出的索引,从1D numpy数组高效地切片窗口,python,arrays,numpy,optimization,indexing,Python,Arrays,Numpy,Optimization,Indexing,我想从同一个1D numpy数组中提取多个切片,其中切片索引是从随机分布中提取的。基本上,我希望实现以下目标: import numpy as np import numpy.random # generate some 1D data data = np.random.randn(500) # window size (slices are 2*winsize long) winsize = 60 # number of slices to take from the data inds

我想从同一个1D numpy数组中提取多个切片,其中切片索引是从随机分布中提取的。基本上,我希望实现以下目标:

import numpy as np
import numpy.random

# generate some 1D data
data = np.random.randn(500)

# window size (slices are 2*winsize long)
winsize = 60

# number of slices to take from the data
inds_size = (100, 200)

# get random integers that function as indices into the data
inds = np.random.randint(low=winsize, high=len(data)-winsize, size=inds_size)

# now I want to extract slices of data, running from inds[0,0]-60 to inds[0,0]+60
sliced_data = np.zeros( (winsize*2,) + inds_size )
for k in range(inds_size[0]):
    for l in range(inds_size[1]):
        sliced_data[:,k,l] = data[inds[k,l]-winsize:inds[k,l]+winsize]

# sliced_data.shape is now (120, 100, 200)
上面的嵌套循环工作正常,但速度非常慢。在我的实际代码中,我需要做上千次,因为数据数组比这些数组大得多。有没有办法更有效地做到这一点

请注意,在我的例子中,
inds
始终是2D,但在获得切片后,我将始终在这两个维度中的一个维度上求和,因此只在一个维度上累加和的方法就可以了


我发现,它们看起来几乎一样。然而,问题只是关于一维索引向量(与我的二维索引向量相反)。此外,答案缺乏一点背景,因为我不太明白建议的
as_大踏步
是如何工作的。由于我的问题似乎并不罕见,我想我应该再次提问,希望得到一个更具解释性的答案,而不仅仅是代码。

下面是一个使用-

运行时测试-

In [20]: # generate some 1D data
    ...: data = np.random.randn(500)
    ...: 
    ...: # window size (slices are 2*winsize long)
    ...: winsize = 60
    ...: 
    ...: # number of slices to take from the data
    ...: inds_size = (100, 200)
    ...: 
    ...: # get random integers that function as indices into the data
    ...: inds=np.random.randint(low=winsize,high=len(data)-winsize, size=inds_size)
    ...: 

In [21]: %%timeit 
    ...: sliced_data = np.zeros( (winsize*2,) + inds_size )
    ...: for k in range(inds_size[0]):
    ...:     for l in range(inds_size[1]):
    ...:         sliced_data[:,k,l] = data[inds[k,l]-winsize:inds[k,l]+winsize]
    ...: 
10 loops, best of 3: 66.9 ms per loop

In [22]: %%timeit 
    ...: allinds = inds + np.arange(-60,60)[:,None,None]
    ...: sliced_dataout = data[allinds]
    ...: 
10 loops, best of 3: 24.1 ms per loop

内存消耗:折衷解决方案

如果内存消耗是一个问题,这里有一个折衷的解决方案,只有一个循环-

sliced_dataout = np.zeros( (winsize*2,) + inds_size )
for k in range(sliced_data.shape[0]):
    sliced_dataout[k] = data[inds-winsize+k] 

以这种方式使用
似乎比Divakar的方法快一些(这里是20毫秒对35毫秒),尽管内存使用可能是一个问题

data_wins = as_strided(data, shape=(data.size - 2*winsize + 1, 2*winsize), strides=(8, 8))
inds = np.random.randint(low=0, high=data.size - 2*winsize, size=inds_size)
sliced = data_wins[inds]
sliced = sliced.transpose((2, 0, 1))    # to use the same index order as before
是每个维度中索引的步骤(以字节为单位)。例如,对于形状数组
(x,y,z)
和大小数据类型
d
(8表示float64),跨步通常是
(y*z*d,z*d,d)
,因此第二个索引跨越整个z项行。将这两个值设置为8,
data\u wins[i,j]
data\u wins[j,i]
将引用相同的内存位置

>>> import numpy as np
>>> from numpy.lib.stride_tricks import as_strided
>>> a = np.arange(10, dtype=np.int8)
>>> as_strided(a, shape=(3, 10 - 2), strides=(1, 1))
array([[0, 1, 2, 3, 4, 5, 6, 7],
       [1, 2, 3, 4, 5, 6, 7, 8],
       [2, 3, 4, 5, 6, 7, 8, 9]], dtype=int8)

谢谢,原则上这是一个很好的方法,而且很省时。然而,不幸的是,它使用了大量内存…@EelkeSpaak嗯,我想你也可以像这样内联它-
sliced_dataout=data[inds+np.arange(-60,60)[:,None,None]]
@EelkeSpaak检查一下刚才添加的折衷解决方案。谢谢你的回答和折衷解决方案。我接受了Sammelsurium的回答,因为事实证明,解决方案在所有方面(时间和内存)都是更好的选择。谢谢!我现在明白了,就像你大步向前走一样,非常喜欢它。而且,在我的测试中,你的方法比Divakar的方法快了大约3倍(不过也要感谢他)。最后,使用
可以很好地使用
,因为它不会复制数据。内存使用情况与我自己的循环方法相同(我使用
Memory\u profiler
分析),而Divakar的广播方法使用双内存(它存储所有索引+数据)。
>>> import numpy as np
>>> from numpy.lib.stride_tricks import as_strided
>>> a = np.arange(10, dtype=np.int8)
>>> as_strided(a, shape=(3, 10 - 2), strides=(1, 1))
array([[0, 1, 2, 3, 4, 5, 6, 7],
       [1, 2, 3, 4, 5, 6, 7, 8],
       [2, 3, 4, 5, 6, 7, 8, 9]], dtype=int8)