【问题标题】:Access multiple elements of an array访问数组的多个元素
【发布时间】:2015-03-16 07:56:56
【问题描述】:

有没有办法在一次操作中为这些元素的已知行和列获取数组元素?在每一行中,我想访问从 col_start 到 col_end 的元素(每行都有不同的开始和结束索引)。每行的元素数量相同,元素是连续的。 示例:

[ . . . . | | | . . . . . ]
[ | | | . . . . . . . . . ]
[ . . | | | . . . . . . . ]
[ . . . . . . . . | | | . ]

一种解决方案是获取元素的索引(行列对),然后使用 my_array[row_list,col_list]。

有没有其他(更简单)的方式不使用 for 循环?

【问题讨论】:

  • 是的,但是你能提供一个更好的例子吗?
  • 在示例中 ( | ) 是我要访问的元素, ( . ) 是其他元素。你还想知道什么吗?
  • @tjons:是什么让您相信我们正在使用字典? OP 重复引用一个数组; OP 添加了numpy 标签;表示看起来更像是数组而不是字典;等
  • @DSM 我自己的困惑。我错了,我已经删除了其他的cmets。感谢您指出这一点!最重要的是,我不是指字典——我指的是列表。哎呀!

标签: python arrays numpy


【解决方案1】:
A = np.arange(40).reshape(4,10)*.1
startend = [[2,5],[3,6],[4,7],[5,8]]
index_list = [np.arange(v[0],v[1]) + i*A.shape[1] 
                 for i,v in enumerate(startend)]
# [array([2, 3, 4]), array([13, 14, 15]), array([24, 25, 26]), array([35, 36, 37])]
A.flat[index_list]

生产

array([[ 0.2,  0.3,  0.4],
       [ 1.3,  1.4,  1.5],
       [ 2.4,  2.5,  2.6],
       [ 3.5,  3.6,  3.7]])

这仍然有一个迭代,但它是一个列表中相当基本的迭代。 我正在索引A 的扁平化 1d 版本。 np.take(A, index_list) 也可以。

如果行间隔大小不同,我可以使用np.r_ 连接它们。这不是绝对必要的,但在从多个区间和值构建索引时会很方便。

A.flat[np.r_[tuple(index_list)]]
# array([ 0.2,  0.3,  0.4,  1.3,  1.4,  1.5,  2.4,  2.5,  2.6,  3.5,  3.6, 3.7])

ajcr使用的idx可以不用choose

idx = [np.arange(v[0], v[1]) for i,v in enumerate(startend)]
A[np.arange(A.shape[0])[:,None], idx]

idx 和我的index_list 一样,只是它不增加行长。

np.array(idx)

array([[2, 3, 4],
       [3, 4, 5],
       [4, 5, 6],
       [5, 6, 7]])

由于每个arange的长度相同,所以idx无需迭代即可生成:

col_start = np.array([2,3,4,5])
idx = col_start[:,None] + np.arange(3)

第一个索引是一个列数组,它广播以匹配这个idx

np.arange(A.shape[0])[:,None] 
array([[0],
       [1],
       [2],
       [3]])

有了这个Aidx,我得到了以下时间:

In [515]: timeit np.choose(idx,A.T[:,:,None])
10000 loops, best of 3: 30.8 µs per loop

In [516]: timeit A[np.arange(A.shape[0])[:,None],idx]
100000 loops, best of 3: 10.8 µs per loop

In [517]: timeit A.flat[idx+np.arange(A.shape[0])[:,None]*A.shape[1]]
10000 loops, best of 3: 24.9 µs per loop

flat 索引更快,但计算更高级的索引需要一些时间。

对于大型数组,flat 索引的速度占主导地位。

A=np.arange(4000).reshape(40,100)*.1
col_start=np.arange(20,60)
idx=col_start[:,None]+np.arange(30)

In [536]: timeit A[np.arange(A.shape[0])[:,None],idx]
10000 loops, best of 3: 108 µs per loop

In [537]: timeit A.flat[idx+np.arange(A.shape[0])[:,None]*A.shape[1]]
10000 loops, best of 3: 59.4 µs per loop

np.choose 方法遇到硬编码限制:Need between 2 and (32) array objects (inclusive).


什么越界idx

col_start=np.array([2,4,6,8])
idx=col_start[:,None]+np.arange(3)
A[np.arange(A.shape[0])[:,None], idx]

产生错误,因为最后一个 idx 值是 10,太大。

你可以clipidx

idx=idx.clip(0,A.shape[1]-1)

在最后一行产生重复值

[ 3.8,  3.9,  3.9]

您也可以在索引之前填充A。更多选项请参见np.pad

np.pad(A,((0,0),(0,2)),'edge')[np.arange(A.shape[0])[:,None], idx]

另一种选择是删除超出范围的值。然后idx 将变成一个参差不齐的列表列表(或列表数组)。 flat 方法可以处理这个问题,但结果不会是矩阵。

startend = [[2,5],[4,7],[6,9],[8,10]]
index_list = [np.arange(v[0],v[1]) + i*A.shape[1] 
                 for i,v in enumerate(startend)]
# [array([2, 3, 4]), array([14, 15, 16]), array([26, 27, 28]), array([38, 39])]

A.flat[np.r_[tuple(index_list)]]
# array([ 0.2,  0.3,  0.4,  1.4,  1.5,  1.6,  2.6,  2.7,  2.8,  3.8,  3.9])

【讨论】:

  • 你认为使用列表推导会比简单地使用 for 循环更快吗?
  • 对于恒定长度范围,您不需要任何迭代 - 只需矩阵相加。
  • 感谢您的回答和时间安排。
  • 我自己测量了,你的方法确实更快。您对如何防止索引越界有什么建议吗?
  • 我添加了一些处理越界的例子。
【解决方案2】:

您可以使用np.choose

这是一个示例 NumPy 数组 arr

array([[ 0,  1,  2,  3,  4,  5,  6],
       [ 7,  8,  9, 10, 11, 12, 13],
       [14, 15, 16, 17, 18, 19, 20]])

假设我们要从第一行中选择值[1, 2, 3],从第二行中选择[11, 12, 13],从第三行中选择[17, 18, 19]

换句话说,我们将从arr 的每一行中挑选出索引,如数组idx 所示:

array([[1, 2, 3],
       [4, 5, 6],
       [3, 4, 5]])

然后使用np.choose:

>>> np.choose(idx, arr.T[:,:,np.newaxis])
array([[ 1,  2,  3],
       [11, 12, 13],
       [17, 18, 19]])

解释刚刚发生的事情:arr.T[:,:,np.newaxis] 表示 arr 暂时被视为形状为 (7, 3, 1) 的 3D 数组。您可以将其想象为 3D 数组,其中原始 arr 的每一列现在是具有三个值的 2D 列向量。 3D 数组看起来有点像这样:

#  0       1       2       3       4       5       6
[[ 0]   [[ 1]   [[ 2]   [[ 3]   [[ 4]   [[ 5]   [[ 6]   # choose values from 1, 2, 3
 [ 7]    [ 8]    [ 9]    [10]    [11]    [12]    [13]   # choose values from 4, 5, 6
 [14]]   [15]]   [16]]   [17]]   [18]]   [19]]   [20]]  # choose values from 3, 4, 5

为了获取输出数组的第 zeroth 行,choose 从索引 1 处的二维列中选择 zeroth 元素,即 zeroth 元素来自索引2 处的二维列,以及来自二维列索引3 处的zeroth 元素。

为了获得输出数组的第一行,choose从索引4处的二维列中选择first元素,first 来自索引5 的二维列中的元素,...等等。

【讨论】:

  • 谢谢,这看起来像我想的那样。现在我必须检查给定解决方案的性能。
  • 我还有一个问题。如果我有 col_start 向量和 col_end 向量等于 (col_start + n),创建 idx 数组的最佳方法是什么?
  • @soccersniper:一种方法是使用np.vstack 和列表理解,例如np.vstack([np.arange(x, x+n) for x in col_start])。所以在我上面的例子中,n3col_start[1, 4, 3]
  • 因为 n
  • 可以在没有choose 的情况下使用idx 索引arr - 只需为第一维使用匹配的列数组。
【解决方案3】:

我认为您正在寻找类似下面的内容。不过,我不确定您在访问它们时想对它们做什么。

indexes = [(4,6), (0,2), (2,4), (8, 10)]
arr = [
    [ . . . . | | | . . . . . ],
    [ | | | . . . . . . . . . ],
    [ . . | | | . . . . . . . ],
    [ . . . . . . . . | | | . ]
]

for x in zip(indexes, arr):
    index = x[0]
    row = x[1]
    print row[index[0]:index[1]+1]

【讨论】:

  • 唯一的问题是你现在没有一个 numpy 数组
  • 我想为每一行中的“屏蔽”元素找到最大值。如果所有行的列都相同,则访问这些元素的解决方案将很简单:my_array[:,col_start:col_end]。我正在寻找的是在不同列索引的情况下对先前语句的修改。
  • numpy 数组从何而来? OP对此只字未提? @tjons:我的回答中没有字典?
  • 原始数组包含从中心向外指向给定角度的“射线”上的方向向量和梯度向量之间的点积。因此 dot_product 数组的第 i 行包含沿“射线”的第 i 个角度的点积。
  • @mattm 我自己的困惑。我错了,我已经删除了其他的cmets。感谢您指出这一点!最重要的是,我不是指字典——我指的是列表。哎呀!
猜你喜欢
  • 2014-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-11
  • 1970-01-01
  • 1970-01-01
  • 2022-07-15
  • 2015-07-05
相关资源
最近更新 更多