【问题标题】:Python vectorized running max of parallel line segmentsPython矢量化并行线段的最大运行
【发布时间】:2018-07-12 03:10:23
【问题描述】:

我在一个 numpy 数组中有大量独立的平行水平线段。每个段都有一个起点和一个终点(x 坐标),以及一个值(y 坐标)。段不一定具有相同的长度(长度 = 结束 - 开始)。

一个指定段的示例矩阵,每行一个段,如下所示:

Start End Value
0     10  4
5     19  3
6     25  2
7     16  1
12    21  5

在代码中

A = np.array([[0,10,4],
[5,19,3],
[6,25,2],
[7,16,1],
[12,21,5]])

我想计算线段上的运行最大值。也就是说,在上面的例子中,对于 [0,25) 范围内的 x,我想要对应的最大 y。与示例对应的示例输出将是

Start End Max
0     10  4
10    12  3
12    21  5
21    25  2

我可以在 for 循环中执行此操作,但这很慢,因为我有数万个段。我似乎想不出一种方法来矢量化它。有人可以吗?

循环代码示例:

x = np.arange(np.min(A[:,0]), np.max(A[:,1]))
maxes = np.zeros((x.shape[0], 2))
maxes[:,0] = x
maxes[:,1] = -np.inf

for a in A:
    ix = (x >= a[0]) & (x < a[1]) & (maxes[:,1] < a[2])
    maxes[ix,1] = a[2]

此代码输出一个数组,范围内的每个 x 对应一行,与上面的输出示例相反。两者都很好(并且等效)。

【问题讨论】:

  • 为 numpy 数组添加示例案例?另外,段之间会不会有重叠?
  • @Divakar:感谢您的提问。如果您指的是示例数组,请参阅我在第一个代码块中提供的示例。是的,可以有重叠;请参阅第一个代码块中的示例。
  • @Matt 我猜 Divakar 正在谈论为数组添加一个 executable 示例案例,可以将其复制到 python 控制台并生成一个看起来与您的完全一样的数组提供了样本数据。
  • @Scotty1- 好的,添加了代码 sn-p。希望澄清一下。
  • @Scotty1- 完成。感谢您帮助使问题更清晰。

标签: python numpy


【解决方案1】:

您可以使用布尔数组来确定空间中的给定点是否在给定的线段中。该布尔数组可以与段值相乘以生成一个数组,其中线上的每个点都有一个段值向量,如果段不包括该点,则该段的值被清零。从那里可以沿单个轴应用数组的max 方法。

import numpy as np

A = np.array([[0,10,4],
[5,19,3],
[6,25,2],
[7,16,1],
[12,21,5]])

# get the dimension of the space
seg_left = A[:, 0, None]
seg_right = A[:, 1, None]
seg_val = A[:, 2, None]

# set the left edge of the space and reset the axes
left_edge = seg_left.min()
seg_left -= left_edge
seg_right -= left_edge
right_edge = seg_right.max()


# generate an array of coordinates and repeat it for each defined segment. This 
# can then be used to determine what segments are on for each point
space = np.tile(np.arange(right_edge+1), (seg_val.size, 1))
space_bool = np.logical_and(space >= seg_left,
                            space < seg_right)

# find the maximum of the on segments
seg_max = (seg_val * space_bool).max(axis=0)

# determine the continuous segments. The +1 ensures that the correct value is
# selected
steps = np.r_[0, np.where(np.diff(seg_max))[0]+1]
seg_val = seg_max[steps[:-1]]

# reset the left edge to the original left edge
steps += left_edge

print(np.c_[steps[:-1], steps[1:], seg_val])

# [[ 0 10  4]
#  [10 12  3]
#  [12 21  5]
#  [21 25  2]]

【讨论】:

  • 太棒了!谢谢。为任何范围的 x 坐标制作通用解决方案的道具。我唯一的问题是,因为我有数万个段,space 数组将占用约 47GB 的内存,这不适合。但也许我可以以某种方式将其分块,然后应用修正来处理分块转换。
  • 分块应该可以工作。您可以研究的另一件事是稀疏矩阵。 docs.scipy.org/doc/scipy/reference/sparse.html 如果您的段相对较短,则会释放大量空间。我不太确定如何在没有 for 循环的情况下构建稀疏矩阵,但这仍然可以节省大量时间。
  • 仅供参考,最后,我完成了以下操作:创建 形式的段数组,其中输入 {start,end},坐标为 start或结束坐标取决于类型。然后循环遍历它,并使用sortedcontainers.SortedDict 来保持最大值。这需要 3 秒,并且没有内存问题。 (您的方法耗时约 60 秒,原始 for 循环耗时约 6 分钟。)。
【解决方案2】:

您可以使用booleans 的数组来索引数组。这意味着您可以一次根据您的条件检查所有坐标,然后使用结果索引值列 (A[2])。从您的示例结果中,我认为不应包含线段的端点,因此以下代码:

import numpy as np

A = np.array(
    [[0,10,4],
     [5,19,3],
     [6,25,2],
     [7,16,1],
     [12,21,5]]
)

ranges = np.array([
    [0,10], [10,12], [12,21], [21,25]
])

for xmin,xmax in ranges:
    print(xmin,xmax, np.max(A[~np.logical_or(A[:,1]<=xmin, A[:,0]>=xmax),2]))

重现您想要的结果:

0 10 4
10 12 3
12 21 5
21 25 2

【讨论】:

  • 感谢您的回答,但问题的主要组成部分之一实际上是找出您已硬编码的ranges :)
  • @Matt 哦,那我误解了你的问题。进一步考虑这一点是否有任何意义,或者您对其他答案是否满意?
猜你喜欢
  • 2016-06-20
  • 1970-01-01
  • 2015-04-08
  • 2011-11-28
  • 1970-01-01
  • 2016-09-28
  • 1970-01-01
  • 2023-03-11
  • 2012-11-19
相关资源
最近更新 更多