簡體   English   中英

從第二個2D陣列給出的索引周圍的1D numpy數組中有效切片窗口

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

我想從同一個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,但在獲得切片之后,我將總是在這兩個維度中的一個上求和,因此只在一維上累積總和的方法就可以了。

我發現這個問題答案看起來幾乎一樣。 然而,問題只是關於1D索引向量(與我的2D相反)。 此外,答案缺少一些上下文,因為我真的不明白建議的as_strided是如何工作的。 由於我的問題似乎並不常見,我想我會再次提出問題,希望得到更具解釋性的答案,而不僅僅是代碼。

以這種方式使用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是每個維度中索引的步驟(以字節為單位)。 例如,對於形狀(x, y, z)的數組和大小為d的數據類型(float64為8),步幅通常為(y*z*d, z*d, d) ,因此第二個索引跨越z行的整行。 將兩個值都設置為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)

這是使用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]

運行時測試 -

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] 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM