【问题标题】:Accessing chunks at once in a numpy array在 numpy 数组中一次访问块
【发布时间】:2018-08-09 12:58:30
【问题描述】:

提供了一个numpy数组:

arr = np.array([0,1,2,3,4,5,6,7,8,9,10,11,12])

我想知道如何通过选择的分隔访问选择的大小 chunks,包括串联和切片:

例如:获得由两个值分隔的大小为 3 的块:

arr_chunk_3_sep_2 = np.array([0,1,2,5,6,7,10,11,12])
arr_chunk_3_sep_2_in_slices = np.array([[0,1,2],[5,6,7],[10,11,12])

最有效的方法是什么?如果可能,我想尽可能避免复制或创建新对象。也许 Memoryviews 可以在这里有所帮助?

【问题讨论】:

  • 对于第一部分,arr[[1, 3, 7, 11]] 有什么问题?第二,你试过什么? [为什么你认为可以就地进行?]
  • 但是,这并不能解决任意大小的块的问题。我不清楚如何在不提供每个值的索引的情况下对数组的各个部分进行切片。
  • 我看不出第二部分与第一部分有什么关系。为什么不在第二个问题上单独提出一个问题?
  • arbitrary 中的 arbitrary size chunks 看起来很可疑。你不是说given 块大小吗? arbitrary size chunks 可能意味着输出中的块可能具有可变数量的元素,这似乎与发布的预期输出不同。

标签: python arrays python-3.x numpy


【解决方案1】:

方法#1

这是一个masking -

def slice_grps(a, chunk, sep):
    N = chunk + sep
    return a[np.arange(len(a))%N < chunk]

示例运行 -

In [223]: arr
Out[223]: array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12])

In [224]: slice_grps(arr, chunk=3, sep=2)
Out[224]: array([ 0,  1,  2,  5,  6,  7, 10, 11, 12])

方法 #2

如果输入数组使得最后一个块有足够的跑道,我们可以利用np.lib.stride_tricks.as_strided,受this post的启发,从n元素的每个块中选择m元素-

# https://stackoverflow.com/a/51640641/ @Divakar
def skipped_view(a, m, n):
    s = a.strides[0]
    strided = np.lib.stride_tricks.as_strided
    shp = ((a.size+n-1)//n,n)
    return strided(a,shape=shp,strides=(n*s,s), writeable=False)[:,:m]

out = skipped_view(arr,chunk,chunk+sep)

请注意,输出将是输入数组的视图,因此没有额外的内存开销并且几乎是免费的!

示例运行以使事情变得清晰 -

In [255]: arr
Out[255]: array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12])

In [256]: chunk = 3

In [257]: sep = 2

In [258]: skipped_view(arr,chunk,chunk+sep)
Out[258]: 
array([[ 0,  1,  2],
       [ 5,  6,  7],
       [10, 11, 12]])

# Let's prove that the output is a view indeed
In [259]: np.shares_memory(arr, skipped_view(arr,chunk,chunk+sep))
Out[259]: True

【讨论】:

  • 好!使用 Modulo 意味着它可以在 Cython 中使用类似 C 的除法来加速。
  • 我仍然没有看到切片版本的明确解决方案
  • 我的意思是如何到达np.array([[0,1,2],[5,6,7],[10,11,12])
  • @ibarrond 那将是slice_grps(arr, chunk=3, sep=2).reshape(-1,chunk)。我解决了通用扁平案例,因为可能存在输出大小不是chunk 的倍数的情况。
  • 我明白了!我只是错过了重塑。关于输出大小是块的倍数,我会将其留给实际实现。如果我将其应用于大小为 10.000 的向量,我可以在不严重影响性能的情况下手工挑选最后一个块的行为
【解决方案2】:

重塑和切片怎么样?

In [444]: arr = np.array([0,1,2,3,4,5,6,7,8,9,10,11,12])
In [445]: arr.reshape(-1,5)
...
ValueError: cannot reshape array of size 13 into shape (5)

一个问题 - 你的数组不够大,无法进行这种重塑 - 所以我们必须填充它:

In [446]: np.concatenate((arr,np.zeros(2,int))).reshape(-1,5)
Out[446]: 
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12,  0,  0]])
In [447]: np.concatenate((arr,np.zeros(2,int))).reshape(-1,5)[:,:-2]
Out[447]: 
array([[ 0,  1,  2],
       [ 5,  6,  7],
       [10, 11, 12]])

as_strided 可以通过在数据缓冲区之外包含字节来解决此问题。通常这被视为一个错误,尽管在这里它可以是一种资产——​​只要你真的把这些垃圾扔掉。

或者扔掉最后一个不完整的行:

In [452]: arr[:-3].reshape(-1,5)[:,:3]
Out[452]: 
array([[0, 1, 2],
       [5, 6, 7]])

【讨论】:

    猜你喜欢
    • 2022-01-20
    • 1970-01-01
    • 2015-05-28
    • 1970-01-01
    • 1970-01-01
    • 2017-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多