numpy中列表滑动的矢量化实现

时间:2018-02-24 18:53:50

标签: python loops numpy vectorization

从给定的numpy数组[1,2,3,4]和窗口wz = 2(前两个元素和每个元素后面的两个元素)我必须得到对(中心el,el来自窗口)。具有未存在元素的对可以被跳过或替换为零。所以在这个例子中我必须得到这个:

[[1., 0.]
 [2., 1.]
 [3., 2.]
 [4., 3.]
 [1., 2.]
 [2., 3.]
 [3., 4.]
 [4., 0.]
 [1., 0.]
 [2., 0.]
 [3., 1.]
 [4., 2.]
 [1., 3.]
 [2., 4.]
 [3., 0.]
 [4., 0.]]

我的实施非常低效,看起来像:

x = np.array([1,2,3,4])
l = x.shape[0]
for i in range(1, m):
    init = np.empty((x.shape[0]*2,2))
    init[:,0] = np.append(x, x)
    init[:l,1] = np.pad(x, (i,0), mode='constant')[:l]
    init[-l:,1] = np.pad(x, (0,i), mode='constant')[-l:]
    corpus.extend(init)

有人可以提供更高效的解决方案吗? 在另一个我已实现的简单测试数据和变体上:

285 µs ± 19.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
379 µs ± 7.68 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

3 个答案:

答案 0 :(得分:2)

这是一种Numpythonic方法:

In [23]: a = np.array([1,2,3,4])
In [24]: arr = np.hstack((a-1, a+1, a - 2, a+ 2))
In [25]: mask = ~np.in1d(arr, a)
In [26]: arr[mask] = 0
In [27]: np.column_stack((np.tile(a, 4), arr))
Out[27]: 
array([ [1, 0],
        [2, 1],
        [3, 2],
        [4, 3],
        [1, 2],
        [2, 3],
        [3, 4],
        [4, 0],
        [1, 0],
        [2, 0],
        [3, 1],
        [4, 2],
        [1, 3],
        [2, 4],
        [3, 0],
        [4, 0]])

答案 1 :(得分:1)

 

如果LineBorder是某些数据,例如字词或随机值,我们需要重新组合,我们可以在x中使用reindexing mechanism

由零版本替换

numpy

让我们构建索引矩阵。

x = np.array([1,2,3,4])
wz = 2
zero = 0

输出:

ri = np.arange(-wz,wz+1)+np.arange(x.shape[0]).reshape(-1,1)
print(ri) 

现在,如果我们将 [[-2, -1, 0, 1, 2], [-1, 0, 1, 2, 3], [ 0, 1, 2, 3, 4], [ 1, 2, 3, 4, 5] 作为最后一个元素添加零,我们可以用索引替换错误的索引。

x

输出:

np.place(ri,(ri<0)|(ri>x.shape[0]),x.shape[0]) #replace wrong indexes
np.vstack((
    np.hstack((x,[zero]))[ri].reshape(1,-1),#extending x with zero and reindexing 
    np.tile(x,2*wz+1)) #repeating basic `x` to each window position
    )#.T #uncomment .T to make it vertical   

跳过的版本

同样的想法,但顺序略有不同:产生一个完整的索引矩阵 ([[0, 0, 1, 2, 3, 0, 1, 2, 3, 4, 1, 2, 3, 4, 0, 2, 3, 4, 0, 0], [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]]) 然后排除错误的对,最后重新索引'x'。

[window_index,x_index]

输出:

x = np.array([1,2,3,4])
wz = 2
ri = np.vstack((
    (np.arange(-wz,wz+1)+np.arange(x.shape[0]).reshape(-1,1)).ravel(),#same index matrix flaten 
    np.tile(np.arange(x.shape[0]),2*wz+1) #repeating `x` indexes to each window position
    )) 
x[ri[:,(ri[0]>=0)&(ri[0]<x.shape[0])]]#.T #uncomment .T to make it vertical   

更新1 (错误修复) 从窗口中排除零以避免配对重复。

 [[1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4, 2, 3, 4],
  [3, 4, 1, 3, 4, 1, 2, 3, 4, 1, 2, 4, 1, 2]]

输出:

x = np.array([1,2,3,4])
wz = 2
ri = np.vstack(((
        np.hstack(( np.arange(-wz,0), #remove zero from window
                    np.arange(1,wz+1)))+
        np.arange(x.shape[0]).reshape(-1,1)).ravel(), #same index matrix flaten 
    np.tile(np.arange(x.shape[0]),2*wz) #repeating `x` indexes to each window position
    )) 
x[ri[:,(ri[0]>=0)&(ri[0]<x.shape[0])]]#.T #uncomment .T to make it vertical   

检查使用过的功能np.arangenp.reshapenp.placenp.hstackbroadcasting rulesindexing的文档。

答案 2 :(得分:0)

numpy方法是有利的,但对于那些感兴趣的人来说,这是一种功能性的方法:

<强>鉴于

changeClickFunction() {
    if( this.favoriteID ) {
        this.functionAddress = this.unsetFavorite;    
    } else {
        this.functionAddress = this.setFavorite;
    }
}

<强>代码

import functools as ft


# Helper function
def curry(f):
    @ft.wraps(f)
    def wrapped(arg):
        try:
            return f(arg)
        except TypeError:
            return curry(ft.wraps(f)(ft.partial(f, arg)))
    return wrapped

输出

lst = [1, 2, 3, 4]
c = curry(lambda x, y: x + y)
funcs = [c(-1), c(1), c(-2), c(2)]
set_ = set(lst)


[[x, 0] if fn(x) not in set_ else [x, fn(x)] for fn in funcs for x in lst]

<强>详情

在列表推导的双[[1, 0], [2, 1], [3, 2], [4, 3], [1, 2], [2, 3], [3, 4], [4, 0], [1, 0], [2, 0], [3, 1], [4, 2], [1, 3], [2, 4], [3, 0], [4, 0]] 循环中,迭代curried函数列表,并将每个函数应用于主列表的每个元素(for)。 Currying允许您通过传入一些参数(例如lst)并稍后从主列表传入元素来计算新值。

创建了元组,例如(主要元素,计算元素)。列表推导的条件部分用1, -1, -2, 2替换在主列表中找不到的计算元素。

另见implementation of the curry function