NumPy:每隔 m 个点选择 n 个点

5
如果我有一个大小为300个点的numpy.ndarray(目前是1 x 300),我想选择每30个点中的10个,我该怎么做?
换句话说:我想先选取前10个点,然后跳过20个点,再获取10个点,然后跳过10个...直到数组的末尾。
3个回答

5
为了从每个包含30个元素的块中选择10个元素,我们可以将其简单地转换成2D并从每行中切出前10列。
a.reshape(-1,30)[:,:10]

优点是输出将成为对输入的视图,因此几乎免费且不需要额外的内存开销。让我们进行一个示例运行以展示并证明这些内容 -

In [43]: np.random.seed(0)

In [44]: a = np.random.randint(0,9,(1,300))
    
In [48]: np.shares_memory(a,a.reshape(10,30)[0,:,:10])
Out[48]: True

如果您需要一个扁平化的版本,请使用.ravel() -
a.reshape(-1,30)[:,:10].ravel()

时间 -

In [38]: a = np.random.randint(0,9,(300))

# @sacul's soln
In [39]: %%timeit
    ...: msk = [True] * 10 + [False] * 20
    ...: out = a[np.tile(msk, len(a)//len(msk))]
100000 loops, best of 3: 7.6 µs per loop

# From this post
In [40]: %timeit a.reshape(-1,30)[:,:10].ravel()
1000000 loops, best of 3: 1.07 µs per loop

In [41]: a = np.random.randint(0,9,(3000000))

# @sacul's soln
In [42]: %%timeit
    ...: msk = [True] * 10 + [False] * 20
    ...: out = a[np.tile(msk, len(a)//len(msk))]
100 loops, best of 3: 3.66 ms per loop

# From this post
In [43]: %timeit a.reshape(-1,30)[:,:10].ravel()
100 loops, best of 3: 2.32 ms per loop

# If you are okay with `2D` output, it is virtually free
In [44]: %timeit a.reshape(-1,30)[:,:10]
1000000 loops, best of 3: 519 ns per loop

使用1D数组的通用情况

A. 元素数量是块长度的倍数

对于一个元素数量为n1D数组a,如果要从每个包含n个元素的块中选择m个元素,并获得一个1D数组输出,则需要:

a.reshape(-1,n)[:,:m].ravel()

请注意,ravel() 函数会进行复制来实现扁平化。因此,如果可能的话,请保留未扁平化的 2D 版本以提高内存效率。
示例运行结果 -
In [59]: m,n = 2,5

In [60]: N = 25

In [61]: a = np.random.randint(0,9,(N))

In [62]: a
Out[62]: 
array([5, 0, 3, 3, 7, 3, 5, 2, 4, 7, 6, 8, 8, 1, 6, 7, 7, 8, 1, 5, 8, 4,
       3, 0, 3])

# Select 2 elements off each block of 5 elements
In [63]: a.reshape(-1,n)[:,:m].ravel()
Out[63]: array([5, 0, 3, 5, 6, 8, 7, 7, 8, 4])

B. 元素的通用编号

我们将利用np.lib.stride_tricks.as_strided,受到这篇帖子的启发,从每个包含n个元素的块中选择m个元素-

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]

def slice_m_everyn(a, m, n):
    a_slice2D = skipped_view(a,m,n)
    extra = min(m,len(a)-n*(len(a)//n))
    L = m*(len(a)//n) + extra
    return a_slice2D.ravel()[:L]

注意,skipped_view让我们可以查看输入数组,可能还可以查看未分配给输入数组的内存区域,但之后我们会对其进行展平和切片以将其限制在所需的输出范围内,这会生成一个副本。
示例运行 -
In [170]: np.random.seed(0)
     ...: a = np.random.randint(0,9,(16))

In [171]: a
Out[171]: array([5, 0, 3, 3, 7, 3, 5, 2, 4, 7, 6, 8, 8, 1, 6, 7])

# Select 2 elements off each block of 5 elements
In [172]: slice_m_everyn(a, m=2, n=5)
Out[172]: array([5, 0, 3, 5, 6, 8, 7])

In [173]: np.random.seed(0)
     ...: a = np.random.randint(0,9,(19))

In [174]: a
Out[174]: array([5, 0, 3, 3, 7, 3, 5, 2, 4, 7, 6, 8, 8, 1, 6, 7, 7, 8, 1])

# Select 2 elements off each block of 5 elements
In [175]: slice_m_everyn(a, m=2, n=5)
Out[175]: array([5, 0, 3, 5, 6, 8, 7, 7])

2
您可以创建一个掩码,并通过掩码进行索引,重复此过程直到达到数组的长度:
msk = [True] * 10 + [False] * 20

arr[np.tile(msk, len(arr)//len(msk))]

最简单的例子:

从一个包含30个元素的数组中选择1个元素,然后跳过2个元素:

>>> arr
array([6, 7, 2, 7, 1, 9, 1, 4, 4, 8, 6, 5, 2, 6, 3, 6, 8, 5, 6, 7, 2, 1, 9,
       6, 7, 2, 1, 8, 2, 2])

msk = [True] * 1 + [False] * 2

>>> arr[np.tile(msk, len(arr)//len(msk))]
array([6, 7, 1, 8, 2, 6, 6, 1, 7, 8])

解释:

msk 是一个布尔掩码。

>>> msk
[True, False, False]

您可以使用np.tile重复该掩码,直到它与您的原始数组长度相同(即您的数组长度除以掩码长度):

>>> np.tile(msk, len(arr)//len(msk))
array([ True, False, False,  True, False, False,  True, False, False,
        True, False, False,  True, False, False,  True, False, False,
        True, False, False,  True, False, False,  True, False, False,
        True, False, False], dtype=bool)

然后,只需按布尔值进行索引,这是 numpy 擅长的简单操作。


0

IIUC

get = 10
skip = 20
k = [item for z in [np.arange(get) + idx for idx in np.arange(0, x.size, skip+get)] for item in z]

然后只需切片

x[k]

例子:

x = np.arange(100)
x[k]

array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 30, 31, 32, 33, 34, 35, 36,
       37, 38, 39, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 90, 91, 92, 93,
       94, 95, 96, 97, 98, 99])

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接