我有一个这样的数组:
A = array([1,2,3,4,5,6,7,8,9,10])
我正试图得到一个这样的数组:
B = array([[1,2,3],
[2,3,4],
[3,4,5],
[4,5,6]])
每行(固定任意宽度)移动一行。 A的数组是10k记录长,我试图在Numpy中找到一种有效的方法。目前我正在使用vstack和一个缓慢的for循环。有更快的方法吗?
编辑:
width = 3 # fixed arbitrary width
length = 10000 # length of A which I wish to use
B = A[0:length + 1]
for i in range (1, length):
B = np.vstack((B, A[i, i + width + 1]))
答案 0 :(得分:51)
实际上,有一种更有效的方法可以做到这一点......使用vstack
等的缺点是你正在制作数组的副本。
顺便提一下,这与@Paul的答案实际上完全相同,但我发布的内容只是为了更详细地解释一下......
有一种方法只使用视图来执行此操作,以便 no 内存重复。
我是直接从Erik Rigtorp's post to numpy-discussion借来的,后者又从Keith Goodman的Bottleneck借来了它(这非常有用!)。
基本技巧是直接操纵strides of the array(对于一维数组):
import numpy as np
def rolling(a, window):
shape = (a.size - window + 1, window)
strides = (a.itemsize, a.itemsize)
return np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)
a = np.arange(10)
print rolling(a, 3)
a
是您的输入数组,window
是您想要的窗口长度(3,在您的情况下)。
这会产生:
[[0 1 2]
[1 2 3]
[2 3 4]
[3 4 5]
[4 5 6]
[5 6 7]
[6 7 8]
[7 8 9]]
但是,原始a
和返回的数组之间绝对没有重复的内存。这意味着它比其他选项快,并且比其他选项更好地缩放 。
例如(使用a = np.arange(100000)
和window=3
):
%timeit np.vstack([a[i:i-window] for i in xrange(window)]).T
1000 loops, best of 3: 256 us per loop
%timeit rolling(a, window)
100000 loops, best of 3: 12 us per loop
如果我们将这个概括为沿着N维数组的最后一个轴的“滚动窗口”,我们得到了Erik Rigtorp的“滚动窗口”功能:
import numpy as np
def rolling_window(a, window):
"""
Make an ndarray with a rolling window of the last dimension
Parameters
----------
a : array_like
Array to add rolling window to
window : int
Size of rolling window
Returns
-------
Array that is a view of the original array with a added dimension
of size w.
Examples
--------
>>> x=np.arange(10).reshape((2,5))
>>> rolling_window(x, 3)
array([[[0, 1, 2], [1, 2, 3], [2, 3, 4]],
[[5, 6, 7], [6, 7, 8], [7, 8, 9]]])
Calculate rolling mean of last dimension:
>>> np.mean(rolling_window(x, 3), -1)
array([[ 1., 2., 3.],
[ 6., 7., 8.]])
"""
if window < 1:
raise ValueError, "`window` must be at least 1."
if window > a.shape[-1]:
raise ValueError, "`window` is too long."
shape = a.shape[:-1] + (a.shape[-1] - window + 1, window)
strides = a.strides + (a.strides[-1],)
return np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)
所以,让我们看看这里发生了什么......操纵数组的strides
可能看起来有些神奇,但是一旦你理解了发生了什么,它根本就不存在。 numpy数组的步幅描述了沿给定轴增加一个值必须采取的步骤的大小(以字节为单位)。因此,对于64位浮点数的一维数组,每个项的长度为8个字节,x.strides
为(8,)
。
x = np.arange(9)
print x.strides
现在,如果我们将其重塑为2D,3x3数组,则步幅将为(3 * 8, 8)
,因为我们必须跳过24个字节以沿第一个轴增加一步,并且8个字节增加一个步骤沿着第二轴。
y = x.reshape(3,3)
print y.strides
类似地,转置与仅反转数组的步幅相同:
print y
y.strides = y.strides[::-1]
print y
显然,数组的步幅和数组的形状密切相关。如果我们改变一个,我们必须相应地改变另一个,否则我们将没有实际保存数组值的内存缓冲区的有效描述。
因此,如果您想同时更改两者阵列的形状和大小,您只能通过设置x.strides
和x.shape
来执行此操作,即使新的步伐和形状是兼容的。
这就是numpy.lib.as_strided
的用武之地。它实际上是一个非常简单的功能,可以同时设置数组的步幅和形状。
它检查两者是否兼容,但不是旧的步幅和新形状是兼容的,如果你单独设置两个就会发生。 (它实际上通过numpy's __array_interface__
执行此操作,它允许任意类将内存缓冲区描述为numpy数组。)
所以,我们所做的就是沿着一个轴向前移动一个项目(在64位数组的情况下为8个字节),但也只沿另一个轴向前移动8个字节。
换句话说,如果“窗口”大小为3,则数组的形状为(whatever, 3)
,但不是为第二个维度步进完整的3 * x.itemsize
,而是只前进一个项目,有效地使新数组的行成为原始数组中的“移动窗口”视图。
(这也意味着x.shape[0] * x.shape[1]
与新数组的x.size
不同。)
无论如何,希望这会让事情变得更加清晰......
答案 1 :(得分:10)
这个解决方案没有通过python循环有效实现,因为它提供了在使用numpy数组时最好避免的各种类型检查。如果你的阵列非常高,你会注意到这个很快:
newshape = (4,3)
newstrides = (A.itemsize, A.itemsize)
B = numpy.lib.stride_tricks.as_strided(A, shape=newshape, strides=newstrides)
这给出了阵列A的视图。如果你想要一个可以编辑的新阵列,那就做同样的事情但最后用.copy()
。
有关进步的详情:
在这种情况下,newstrides
元组将是(4,4),因为数组有4个字节的项目,并且您希望继续在i维度中以单项步骤逐步执行数据。第二个值'4'指的是j维度中的步幅(在正常的4x4阵列中它将是16)。因为在这种情况下,您还希望在j维度中以4字节步长递增读取缓冲区。
Joe给出了一个很好的,详细的描述,当他说所有这些技巧都是同时改变步幅和形状时,事情变得清晰。
答案 2 :(得分:2)
您使用的是哪种方式?
import numpy as np
A = np.array([1,2,3,4,5,6,7,8,9,10])
width = 3
np.vstack([A[i:i-len(A)+width] for i in xrange(len(A)-width)])
# needs 26.3µs
np.vstack([A[i:i-width] for i in xrange(width)]).T
# needs 13.2µs
如果您的宽度相对较低(3)并且您有一个很大的A
(10000个元素),则差异更为重要:第一个为32.4毫秒,第二个为44微秒。
答案 3 :(得分:2)
进一步回答@Joe general
的答案import numpy as np
def rolling(a, window):
step = 2
shape = ( (a.size-window)/step + 1 , window)
strides = (a.itemsize*step, a.itemsize)
return np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)
a = np.arange(10)
print rolling(a, 3)
输出:
[[0 1 2]
[2 3 4]
[4 5 6]
[6 7 8]]
进一步概括2d情况,即用它来从图像中提取斑块
def rolling2d(a,win_h,win_w,step_h,step_w):
h,w = a.shape
shape = ( ((h-win_h)/step_h + 1) * ((w-win_w)/step_w + 1) , win_h , win_w)
strides = (step_w*a.itemsize, h*a.itemsize,a.itemsize)
return np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)
a = np.arange(36).reshape(6,6)
print a
print rolling2d (a,3,3,2,2)
输出:
[[ 0 1 2 3 4 5]
[ 6 7 8 9 10 11]
[12 13 14 15 16 17]
[18 19 20 21 22 23]
[24 25 26 27 28 29]
[30 31 32 33 34 35]]
[[[ 0 1 2]
[ 6 7 8]
[12 13 14]]
[[ 2 3 4]
[ 8 9 10]
[14 15 16]]
[[ 4 5 6]
[10 11 12]
[16 17 18]]
[[ 6 7 8]
[12 13 14]
[18 19 20]]]
答案 4 :(得分:1)
我认为当宽度固定在较低的数字时,这可能比循环更快......
import numpy
a = numpy.array([1,2,3,4,5,6])
b = numpy.reshape(a, (numpy.shape(a)[0],1))
b = numpy.concatenate((b, numpy.roll(b,-1,0), numpy.roll(b,-2,0)), 1)
b = b[0:(numpy.shape(a)[0]/2) + 1,:]
编辑显然,使用跨步的解决方案优于此,唯一的主要缺点是它们尚未得到充分记录......
答案 5 :(得分:1)
我使用了类似于@JustInTime的更通用的函数,但适用于ndarray
def sliding_window(x, size, overlap=0):
step = size - overlap # in npts
nwin = (x.shape[-1]-size)//step + 1
shape = x.shape[:-1] + (nwin, size)
strides = x.strides[:-1] + (step*x.strides[-1], x.strides[-1])
return stride_tricks.as_strided(x, shape=shape, strides=strides)
一个例子,
x = np.arange(10)
M.sliding_window(x, 5, 3)
Out[1]:
array([[0, 1, 2, 3, 4],
[2, 3, 4, 5, 6],
[4, 5, 6, 7, 8]])
x = np.arange(10).reshape((2,5))
M.sliding_window(x, 3, 1)
Out[2]:
array([[[0, 1, 2],
[2, 3, 4]],
[[5, 6, 7],
[7, 8, 9]]])
答案 6 :(得分:1)
看看:view_as_windows。
import numpy as np
from skimage.util.shape import view_as_windows
window_shape = (4, )
aa = np.arange(1000000000) # 1 billion
bb = view_as_windows(aa, window_shape)
大约1秒钟。