numpy:将行值广播到频道

时间:2018-12-01 16:53:19

标签: python performance numpy vectorization

我有一个数据集,其中前48个观测值是时间序列,其他12个是静态变量:

h1 h2 h3 h4 ... h48 v1 v2 v3 v4 v5 v6 .. vn
h1 h2 h3 h4 ... h48 v1 v2 v3 v4 v5 v6 .. vn

一项的形状为(367, 60)

我想将变量v1 v2 v3 v4 v5 v6 .. vn作为时间序列的附加通道传递,即创建形状为(367, 48, 13)的数组。我想即时执行此操作,因为完全转换的数据集不适合我的RAM。

我现在使用的代码效率很低(items是批处理的):

def preprocessor(items):

    items_new = np.zeros(shape=(items.shape[0], 367, 48, 13), dtype=np.float32)

    for idx_item, item in enumerate(items):

        train_data = item[:,:48]
        train_vars = item[:,48:]

        train_new = np.zeros((train_data.shape[0], train_data.shape[1],(train_vars.shape[1]+1)))
        for idx_row, row in enumerate(train_data):
            for idx_col, elem in enumerate(row):
                train_new[idx_row, idx_col, :] = np.concatenate([[elem], train_vars[idx_row]])

        items_new[idx_item] = train_new

    return items_new

我可以更快地进行循环吗?

编辑:

最小的可复制示例:

arr = np.random.randn(5,367,60)

arr2 = preprocessor(arr)

print(arr2.shape) # (5, 367, 48, 13)

2 个答案:

答案 0 :(得分:1)

方法1

我们可以将广播的数组分配用于矢量化解决方案-

def array_assign(items):    
    L = 48 # slice at this column ID
    N = items.shape[-1]
    out = np.empty(shape= items.shape[:2] + (L,N-L+1), dtype=np.float32)
    out[...,1:] = items[...,None,L:]
    out[...,0] = items[...,:L]
    return out

方法2

我们还可以使用广播视图,然后进行串联-

def broadcast_concat(items):    
    L = 48 # slice at this column ID
    N = items.shape[-1]
    a = items[...,:L,None]
    shp_b = items.shape[:2] + (L,N-L)
    b = np.broadcast_to(items[...,None,L:],shp_b)
    out = np.concatenate((a,b),axis=-1)
    return out

时间-

In [321]: items = np.random.rand(5,367,60)

In [322]: %timeit array_assign(items)
1000 loops, best of 3: 923 µs per loop

In [323]: %timeit broadcast_concat(items)
1000 loops, best of 3: 781 µs per loop

为了公平地比较,我们应该让第二种方法也使用更有效的float32 dtype。让我们使用该dtype设置输入数据并再次进行测试-

In [335]: items = np.random.rand(5,367,60).astype(np.float32)

In [336]: %timeit array_assign(items)
1000 loops, best of 3: 897 µs per loop

In [337]: %timeit broadcast_concat(items)
1000 loops, best of 3: 348 µs per loop

因此,对于大多数需要dtype转换的情况,我们可以在方法2的开头使用items = np.asarray(items, dtype=np.float32)

答案 1 :(得分:1)

这是另一个使用重复和连接的解决方案。

a = items[:,:,:48, np.newaxis]
b = items[:,:,48:].repeat(a.shape[2], axis=1).reshape(*a.shape[:-1], -1)
return np.concatenate([a,b], axis=3)