简体   繁体   English

从第二个2D阵列给出的索引周围的1D numpy数组中有效切片窗口

[英]Efficiently slice windows from a 1D numpy array, around indices given by second 2D array

I want to extract multiple slices from the same 1D numpy array, where the slice indices are drawn from a random distribution. 我想从同一个1D numpy数组中提取多个切片,其中切片索引是从随机分布中提取的。 Basically, I want to achieve the following: 基本上,我想实现以下目标:

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)

The above nested loop works fine, but is very slow. 上面的嵌套循环工作正常,但速度很慢。 In my real code, I will need to do this thousands of times, for data arrays a lot bigger than these. 在我的真实代码中,我需要做数千次,因为数据阵列要比这些大得多。 Is there any way to do this more efficiently? 有没有办法更有效地做到这一点?

Note that inds will always be 2D in my case, but after getting the slices I will always be summing over one of these two dimensions, so an approach that only accumulates the sum across the one dimension would be fine. 请注意,在我的情况下, inds总是2D,但在获得切片之后,我将总是在这两个维度中的一个上求和,因此只在一维上累积总和的方法就可以了。

I found this question and this answer which seem almost the same. 我发现这个问题答案看起来几乎一样。 However, the question is only about a 1D indexing vector (as opposed to my 2D). 然而,问题只是关于1D索引向量(与我的2D相反)。 Also, the answer lacks a bit of context, as I don't really understand how the suggested as_strided works. 此外,答案缺少一些上下文,因为我真的不明白建议的as_strided是如何工作的。 Since my problem does not seem uncommon, I thought I'd ask again in the hope of a more explanatory answer rather than just code. 由于我的问题似乎并不常见,我想我会再次提出问题,希望得到更具解释性的答案,而不仅仅是代码。

Using as_strided in this way appears to be somewhat faster than Divakar's approach (20 ms vs 35 ms here), although memory usage might be an issue. 以这种方式使用as_strided似乎比Divakar的方法(20 ms vs 35 ms)更快,尽管内存使用可能是一个问题。

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

Strides are the steps in bytes for the index in each dimension. Strides是每个维度中索引的步骤(以字节为单位)。 For example, with an array of shape (x, y, z) and a data type of size d (8 for float64), the strides will ordinarily be (y*z*d, z*d, d) , so that the second index steps over whole rows of z items. 例如,对于形状(x, y, z)的数组和大小为d的数据类型(float64为8),步幅通常为(y*z*d, z*d, d) ,因此第二个索引跨越z行的整行。 Setting both values to 8, data_wins[i, j] and data_wins[j, i] will refer to the same memory location. 将两个值都设置为8, data_wins[i, j]data_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)

Here's a vectorized approach using broadcasting - 这是使用broadcasting的矢量化方法 -

# Get 3D offsetting array and add to inds for all indices
allinds = inds + np.arange(-60,60)[:,None,None]

# Index into data with all indices for desired output
sliced_dataout = data[allinds]

Runtime test - 运行时测试 -

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

Memory consumption : Compromise solution 内存消耗:妥协解决方案

If memory consumption is an issue, here's a compromise solution with one loop - 如果内存消耗是一个问题,这里是一个折衷的解决方案,一个循环 -

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM