【问题标题】:Assigning values to discontinued slices in a ndarray将值分配给 ndarray 中的中断切片
【发布时间】:2020-07-03 06:55:56
【问题描述】:

我有一个包含数据的基本数组。该数组中的一些索引需要重新分配一个新值,并且停止使用的索引。我想避免对所有这些进行 for 循环并使用切片表示法,因为它可能会更快。

例如:

arr = np.zeros(100)
sl_obj_1 = slice(2,5)
arr[sl_obj_1] = 42

适用于单个切片。但是我有另一个已停产的切片可以应用于同一个数组,比如说

sl_obj_2 = slice(12,29)
arr[sl_obj_1] = 55

我想完成以下几件事:

arr[sl_obj_1, sl_obj_2] = 42, 55

有什么想法吗?

编辑:更改示例以强调序列是或变化的长度。

【问题讨论】:

  • 所有切片的长度是否相同?
  • 没有。更改哪些索引,每个单独序列的长度,如果只有或更多这样的序列,则基于对数据的先前计算。所以解决方案需要是通用的。可能没有一种聪明的方法可以做到这一点,我需要循环。我将进行编辑以强调这一点 - 我的例子似乎暗示了这一点。
  • 虽然您可以将切片转换为索引列表,但这仍然涉及切片上的循环。但是由于您为每个切片分配不同的值,因此请坚持使用最明确的循环。 for s, val in zip(slice_list, val_list): ...

标签: python-3.x numpy slice


【解决方案1】:

没有直接从 NumPy 数组中提取多个切片的好方法,更不用说不同大小的切片了。但是您可以通过将切片转换为索引并使用索引数组来作弊。

对于一维数组,使用index arrays相对简单。

import numpy as np

def slice_indices(some_list, some_slice):
    """Convert a slice into indices of a list"""
    return np.arange(len(some_list))[some_slice]
    # For a non-NumPy solution, use this:
    # return range(*some_slice.indices(len(some_list)))

arr = np.arange(10)

# We'll make [1, 2, 3] and [8, 7] negative.
slice1, new1 = np.s_[1:4],    [-1, -2, -3]
slice2, new2 = np.s_[8:6:-1], [-8, -7]
# (Here, np.s_ is just a nicer notation for slices.[1])

# Get indices to replace
idx1 = slice_indices(arr, slice1)
idx2 = slice_indices(arr, slice2)

# Use index arrays to assign to all of the indices
arr[[*idx1, *idx2]] = *new1, *new2

# That line expands to this:
arr[[1, 2, 3, 8, 7]] = -1, -2, -3, -8, -7

请注意,这并不能完全避免 Python 迭代——星号运算符仍然创建迭代器,索引数组是一个常规的 Python 列表。在有大量数据的情况下,这可能比手动方法慢得多,因为它会计算将分配的每个索引。

您还需要确保替换数据已经是正确的形状,或者您可以使用 NumPy 的手动广播功能(例如np.broadcast_to)来修复形状。这会带来额外的开销——如果您依赖自动广播,您最好在循环中执行分配。

arr = np.zeros(100)

idx1 = slice_indices(arr, slice(2, 5))
idx2 = slice_indices(arr, slice(12, 29))

new1 = np.broadcast_to(42, len(idx1))
new2 = np.broadcast_to(55, len(idx2))

arr[*idx1, *idx2] = *new1, *new2

为了推广到更多维度,slice_indices 需要注意形状,并且您必须更加小心地连接多组索引(而不是arr[[i1, i2, i3]],您需要arr[[i1, i2, i3], [j1, j2, j3]],不能直接连接)。

实际上,如果您经常需要这样做,最好使用一个简单的函数来封装您要避免的循环。

def set_slices(arr, *indices_and_values):
    """Set multiple locations in an array to their corresponding values.

    indices_and_values should be a list of index-value pairs.
    """
    for idx, val in indices_and_values:
        arr[idx] = val

# Your example:
arr = np.zeros(100)
set_slices(arr, (np.s_[2:5], 42), (np.s_[12:29], 55))

(如果您的唯一目标是看起来就像您同时使用多个索引,here 是两个尝试为您做所有事情的函数,包括广播和处理多维数组。)


1np.s_

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-08-09
    • 2018-01-31
    • 1970-01-01
    • 2016-04-18
    • 2019-05-09
    • 1970-01-01
    • 2014-01-28
    • 2021-11-17
    相关资源
    最近更新 更多