一次从 numpy 数组中选择多个切片

2025-03-11 08:54:00
admin
原创
63
摘要:问题描述:我正在寻找一种一次性从 numpy 数组中选择多个切片的方法。假设我们有一个 1D 数据数组,并想提取其中的三个部分,如下所示:data_extractions = [] for start_index in range(0, 3): data_extractions.append(dat...

问题描述:

我正在寻找一种一次性从 numpy 数组中选择多个切片的方法。假设我们有一个 1D 数据数组,并想提取其中的三个部分,如下所示:

data_extractions = []

for start_index in range(0, 3):
    data_extractions.append(data[start_index: start_index + 5])

之后data_extractions将是:

data_extractions = [
    data[0:5],
    data[1:6],
    data[2:7]
]

有没有办法不使用 for 循环来执行上述操作?numpy 中的某种索引方案可以让我从数组中选择多个切片并将它们作为多个数组返回,比如在 n+1 维数组中?


我想也许我可以复制我的数据,然后从每一行中选择一个跨度,但下面的代码会引发 IndexError

replicated_data = np.vstack([data] * 3)
data_extractions = replicated_data[[range(3)], [slice(0, 5), slice(1, 6), slice(2, 7)]

解决方案 1:

您可以使用索引来选择您想要的行,并将其转换为适当的形状。例如:

 data = np.random.normal(size=(100,2,2,2))

 # Creating an array of row-indexes
 indexes = np.array([np.arange(0,5), np.arange(1,6), np.arange(2,7)])
 # data[indexes] will return an element of shape (3,5,2,2,2). Converting
 # to list happens along axis 0
 data_extractions = list(data[indexes])

 np.all(data_extractions[1] == data[1:6])
 True

最后的比较是针对原始数据的。

解决方案 2:

stride_tricks可以做到

a = np.arange(10)
b = np.lib.stride_tricks.as_strided(a, (3, 5), 2 * a.strides)
b
# array([[0, 1, 2, 3, 4],
#        [1, 2, 3, 4, 5],
#        [2, 3, 4, 5, 6]])

请注意b引用与 相同的内存a,事实上是多次引用(例如b[0, 1]b[1, 0]是相同的内存地址)。因此,在使用新结构之前进行复制是最安全的。

nd 可以以类似的方式完成,例如 2d -> 4d

a = np.arange(16).reshape(4, 4)
b = np.lib.stride_tricks.as_strided(a, (3,3,2,2), 2*a.strides)
b.reshape(9,2,2) # this forces a copy
# array([[[ 0,  1],
#         [ 4,  5]],

#        [[ 1,  2],
#         [ 5,  6]],

#        [[ 2,  3],
#         [ 6,  7]],

#        [[ 4,  5],
#         [ 8,  9]],

#        [[ 5,  6],
#         [ 9, 10]],

#        [[ 6,  7],
#         [10, 11]],

#        [[ 8,  9],
#         [12, 13]],

#        [[ 9, 10],
#         [13, 14]],

#        [[10, 11],
#         [14, 15]]])

解决方案 3:

这篇文章中介绍的方法是strided-indexing scheme使用np.lib.stride_tricks.as_strided,基本上是在输入数组中创建一个视图,这样创建起来非常高效,而且视图不会占用更多内存空间。此外,这适用于具有一般维数的 ndarray。

以下是具体实现:

def strided_axis0(a, L):
    # Store the shape and strides info
    shp = a.shape
    s  = a.strides

    # Compute length of output array along the first axis
    nd0 = shp[0]-L+1

    # Setup shape and strides for use with np.lib.stride_tricks.as_strided
    # and get (n+1) dim output array
    shp_in = (nd0,L)+shp[1:]
    strd_in = (s[0],) + s
    return np.lib.stride_tricks.as_strided(a, shape=shp_in, strides=strd_in)

数组情况的样例运行4D-

In [44]: a = np.random.randint(11,99,(10,4,2,3)) # Array

In [45]: L = 5      # Window length along the first axis

In [46]: out = strided_axis0(a, L)

In [47]: np.allclose(a[0:L], out[0])  # Verify outputs
Out[47]: True

In [48]: np.allclose(a[1:L+1], out[1])
Out[48]: True

In [49]: np.allclose(a[2:L+2], out[2])
Out[49]: True

解决方案 4:

一般情况下,您必须在构建索引或收集结果时进行某种迭代和连接。只有当切片模式本身是规则的时,您才可以使用通用切片as_strided

可接受的答案构造一个索引数组,每个切片一行。因此,这是对切片的迭代,arange本身就是一个(快速)迭代。并将np.array它们连接到一个新轴上(np.stack概括这一点)。

In [264]: np.array([np.arange(0,5), np.arange(1,6), np.arange(2,7)])
Out[264]: 
array([[0, 1, 2, 3, 4],
       [1, 2, 3, 4, 5],
       [2, 3, 4, 5, 6]])

indexing_tricks方便的方法来做同样的事情:

In [265]: np.r_[0:5, 1:6, 2:7]
Out[265]: array([0, 1, 2, 3, 4, 1, 2, 3, 4, 5, 2, 3, 4, 5, 6])

这需要切片符号,将其展开arange并连接起来。它甚至允许我展开并连接成 2d

In [269]: np.r_['0,2',0:5, 1:6, 2:7]
Out[269]: 
array([[0, 1, 2, 3, 4],
       [1, 2, 3, 4, 5],
       [2, 3, 4, 5, 6]])

In [270]: data=np.array(list('abcdefghijk'))
In [272]: data[np.r_['0,2',0:5, 1:6, 2:7]]
Out[272]: 
array([['a', 'b', 'c', 'd', 'e'],
       ['b', 'c', 'd', 'e', 'f'],
       ['c', 'd', 'e', 'f', 'g']], 
      dtype='<U1')
In [273]: data[np.r_[0:5, 1:6, 2:7]]
Out[273]: 
array(['a', 'b', 'c', 'd', 'e', 'b', 'c', 'd', 'e', 'f', 'c', 'd', 'e',
       'f', 'g'], 
      dtype='<U1')

索引后连接结果也有效。

In [274]: np.stack([data[0:5],data[1:6],data[2:7]])

我从其他 SO 问题中记得,相对时间处于同一数量级。例如,它可能随着切片数量与其长度的变化而变化。总体而言,必须从源复制到目标的值的数量将是相同的。

如果切片的长度不同,则必须使用平面索引。

解决方案 5:

您可以使用准备好的切片数组对数组进行切片

a = np.array(list('abcdefg'))

b = np.array([
        [0, 1, 2, 3, 4],
        [1, 2, 3, 4, 5],
        [2, 3, 4, 5, 6]
    ])

a[b]

但是,b不必以这种方式手动生成。它可以更加动态地使用

b = np.arange(5) + np.arange(3)[:, None]

解决方案 6:

无论你选择哪种方法,如果两个切片包含相同的元素,它都无法正确支持数学运算,除非你使用ufunc.at比循环效率更低的方法。对于测试:

def as_strides(arr, window_size, stride, writeable=False):
    '''Get a strided sub-matrices view of a 4D ndarray.

    Args:
        arr (ndarray): input array with shape (batch_size, m1, n1, c).
        window_size (tuple): with shape (m2, n2).
        stride (tuple): stride of windows in (y_stride, x_stride).
        writeable (bool): it is recommended to keep it False unless needed
    Returns:
        subs (view): strided window view, with shape (batch_size, y_nwindows, x_nwindows, m2, n2, c)

    See also numpy.lib.stride_tricks.sliding_window_view
    '''
    batch_size = arr.shape[0]
    m1, n1, c = arr.shape[1:]
    m2, n2 = window_size
    y_stride, x_stride = stride

    view_shape = (batch_size, 1 + (m1 - m2) // y_stride,
                  1 + (n1 - n2) // x_stride, m2, n2, c)
    strides = (arr.strides[0], y_stride * arr.strides[1],
               x_stride * arr.strides[2]) + arr.strides[1:]
    subs = np.lib.stride_tricks.as_strided(arr,
                                           view_shape,
                                           strides=strides,
                                           writeable=writeable)
    return subs


import numpy as np
np.random.seed(1)

Xs = as_strides(np.random.randn(1, 5, 5, 2), (3, 3), (2, 2), writeable=True)[0]
print('input
0,0
', Xs[0, 0])
np.add.at(Xs, np.s_[:], 5)
print('unbuffered sum output
0,0
', Xs[0,0])
np.add.at(Xs, np.s_[:], -5)
Xs = Xs + 5
print('normal sum output
0,0
', Xs[0, 0])

解决方案 7:

我们可以使用列表推导来实现这一点

data=np.array([1,2,3,4,5,6,7,8,9,10])
data_extractions=[data[b:b+5] for b in [1,2,3,4,5]]
data_extractions

结果

[array([2, 3, 4, 5, 6]), array([3, 4, 5, 6, 7]), array([4, 5, 6, 7, 8]), array([5, 6, 7, 8, 9]), array([ 6,  7,  8,  9, 10])]
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2787  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1710  
  产品配置管理在企业产品研发与生产过程中扮演着至关重要的角色,它能够确保产品在不同阶段的一致性、可追溯性以及高效的变更管理。PLM(产品生命周期管理)系统作为整合产品全生命周期信息的平台,为产品配置管理提供了强大的支持。随着技术的不断发展,到2025年,PLM系统在支持产品配置管理方面将有一系列值得关注的技术实践。基于人...
plm系统主要干什么的   11  
  敏捷迭代周期与 PLM 流程的适配是现代企业在产品开发过程中面临的重要课题。随着市场竞争的加剧和技术的快速发展,企业需要更加高效、灵活的产品开发模式,以满足客户不断变化的需求。敏捷迭代周期强调快速响应变化、持续交付价值,而 PLM 流程则侧重于产品全生命周期的管理和控制。如何将两者有机结合,优化交付节奏,成为提升企业竞...
plm是什么意思   10  
  在企业的数字化转型进程中,PLM(产品生命周期管理)与ERP(企业资源计划)作为两款重要的企业级系统,发挥着关键作用。然而,很多企业人员对它们之间的区别以及协同逻辑并不十分清晰。深入了解这两者的差异与协同方式,有助于企业更好地规划信息化建设,提升整体运营效率。PLM系统概述PLM系统聚焦于产品从概念设计到退役的全生命周...
国产plm软件   12  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用