【问题标题】:Python Numpy. Delete an element (or elements) in a 2D array if said element is located between a pair of specified elementsPython 麻木。如果该元素位于一对指定元素之间,则删除二维数组中的一个(或多个元素)
【发布时间】:2022-01-19 03:04:32
【问题描述】:

我有一个 2D NumPy 数组,专门用 1 和 0 填充。

a = [[0 0 0 0 1 0 0 0 1]
     [1 1 1 1 1 1 1 1 1]
     [1 1 1 1 1 1 1 1 1]
     [1 1 1 1 0 0 0 0 1]
     [1 1 1 1 1 1 1 1 1]
     [1 1 1 0 1 1 1 1 1]
     [1 1 1 1 1 1 0 0 1]
     [1 1 1 1 1 1 1 1 1]]

为了获取 0 的位置,我使用了以下代码:

new_array = np.transpose(np.nonzero(a==0))

正如预期的那样,我得到以下结果,显示了数组中 0 的位置

new_array = [[0 0]
             [0 1]
             [0 2]
             [0 3]
             [0 5]
             [0 6]
             [0 7]
             [3 4]
             [3 5]
             [3 6]
             [3 7]
             [5 3]
             [6 6]
             [6 7]]

现在我的问题来了: 如果所述组大于2,有没有办法在水平组的开始和结束处获取0的位置?

编辑:如果组要在一行的末尾完成并继续在它下面的一个,它将计为 2 个单独的组。

我的第一个想法是实现一个过程,如果它们位于 0 之间,将删除 0,但我无法弄清楚如何做到这一点。

我希望“new_array”输出为:

new_array = [[0 0]
             [0 3]
             [0 5]
             [0 7]
             [3 4]
             [3 7]
             [5 3]
             [6 6]
             [6 7]]

先谢谢了!!

编辑 2:

感谢大家提供的非常有用的见解,我能够解决我遇到的问题。

为了满足好奇心,这些数据代表音乐信息。我正在开发的程序的目的是根据图像(仅由水平线组成)创建乐谱。

图像到 1 和 0 的转换完成后,我需要从中提取以下信息:Onset、Pitch 和 Duration。这转化为“x”轴上的位置、“y”轴上的位置和组的总长度。

由于 X 和 Y 位置相当容易获得,因此我决定将它们与“持续时间”计算分开处理(这是本文要解决的主要问题)。

感谢您的帮助,我能够解决 Duration 问题并创建一个包含所有必要信息的新数组:

[[0 0 4]
 [5 0 3]
 [4 3 4]
 [6 6 2]]

请注意,第 1 列代表 Onset,第 2 列代表 Pitch,第 3 列代表 Duration。

我还注意到建议为每个事件添加标识符的评论。最终我需要实现它以区分不同的乐器(然后将它们发送到单独的 Midi 通道)。但是,对于仅旨在为单个乐器创建乐谱的程序的第一次迭代,这不是必需的,因为所有事件都属于单个乐器。

我对编程的经验很少,我不知道这是否是实现我目标的最有效方式。欢迎提出任何建议。

谢谢!

【问题讨论】:

  • 真的想要一个交替运行开始和结束的一维序列吗?元组序列或宽度为 2 的数组不是更有意义吗?另外,如果一次跑步在一行结束时结束,并在下一行继续,那是一次跑步,还是两次跑步?
  • 您需要避免在numpy 中进行类似for 的迭代,因为它们很慢。请改用np.transpose(np.nonzero(a==0))
  • @Grismar 实际上,如提供的示例所示,我希望“new_array”的宽度为 2,显示运行的开始和结束,如果它们运行的​​长度大于2. 对于您问题的第二部分,出于数据的目的,它将计为两次单独的运行。
  • FWIW:我对您提出的输出感到担忧。它无法正确区分单次运行和较长时间的运行。考虑[0 1 0 1][0 0 0 1]。它们都将在您的最终表示中产生[[0 0] [0 2]]。最好表示为开始位置和运行次数。我可能将第一个表示为[[0 0 1] [0 2 1]],将第二个表示为[[0 0 3]]。尽管我可能不会将其保留为 numpy 格式;相反会做[[0 0], 3] 或者[0, 0], 3]。我的意思是,在那个阶段我会编写普通的 Python 代码来操作简单的对象。
  • @I-like-weird-sounds 道歉,我的澄清问题措辞不佳-我认为(5, 2, 2) 的形状数组更有意义[[[0 0][0 3]][[0 5][0 7]][[3 4][3 7]][[5 3][5 3]][[6 6][6 7]]],尤其是因为在您的格式中建议,不清楚[5 3][6 6] 是否是一个区间,或者[5 3] 应该被解释为单个0 - 你只能判断你是否回到原始数据。

标签: python arrays numpy conditional-statements


【解决方案1】:

一种更容易遵循的可能解决方案是:

b = np.diff(a, prepend=1)  # prepend a column of 1s and detect
                           # jumps between adjacent columns (left to right)
y, x = np.where(b > 0)  # find positions of the jumps 0->1 (left to right)
# shift positive jumps to the left by 1 position while filling gaps with 0:
b[y, x - 1] = 1
b[y, x] = 0
new_array = list(zip(*np.where(b)))

另一个是:

new_array = list(zip(*np.where(np.diff(a, n=2, prepend=1, append=1) > 0)))

两种解决方案都基于计算连续列之间差异的np.diff(当axis=-1 用于二维数组时)。

【讨论】:

  • 恕我直言,该代码需要逐行解释。有样本数据。我没有看到 np.where(b > 0) 是如何做需要的。
  • 这几乎可以得到 OP 的要求。然而,它也有 cmets 中指出的问题,即不可能从结果中重建原始数组(给定原始数组的形状),因为不清楚应该如何配对这些值,因为单个值只有一组坐标。我觉得这使得解决方案完美,但无用。不过值得称赞,非常简洁。
  • @Grismar 谢谢! “从结果中重建原始数组是不可能的” - 我不认为无法重建原始数组会使这个解决方案(或问题)无用。这可能是解决用户前向问题的有用解决方案。
  • 'Useless' 可能有点强,但我的意思是不可能明确地理解结果。例如,[3 4] 是否表示单个 0[3 7],或者它是否表示从 [3 4][3 7] 的一系列零 - 这是不可能的,并且带有任何一个的输入数组都会产生同样的结果。然而,这是 OP 所要求的,所以尽管模棱两可,但也许他们有它的用处。
  • @Grismar 我理解(并同意)您的推理,但我所说的只是 OP 可能有有效(针对他们的具体问题)这种特定方式的原因做事。
【解决方案2】:

另一个解决方案的一个缺陷是它报告了所有序列 零,不管它们的长度。 您的预期输出还包含这样的组,由 1 或 2 零,但我认为不应该。

我的解决方案没有上述缺陷。

处理相邻相等元素组的优雅工具是 itertools.groupby,所以从:

import itertools

然后将您的预期结果生成为:

res = []
for rowIdx, row in enumerate(a):
    colIdx = 0  # Start column index
    for k, grp in itertools.groupby(row):
        vals = list(grp)        # Values in the group
        lgth = len(vals)        # Length of the group
        colIdx2 = colIdx + lgth - 1  # End column index
        if k == 0 and lgth > 2: # Record this group
            res.append([rowIdx, colIdx])
            res.append([rowIdx, colIdx2])
        colIdx = colIdx2 + 1    # Advance column index
result = np.array(res)

对于您的源数据,结果是:

array([[0, 0],
       [0, 3],
       [0, 5],
       [0, 7],
       [3, 4],
       [3, 7]])

如您所见,它不包含较短的零序列 在第 5 行和第 6 行。

【讨论】:

  • 啊,我错过了 "如果所述组大于 2" 在 OP 中。我以与声明相矛盾的期望输出为指导。 @I-like-weird-sounds 可以澄清问题。
猜你喜欢
  • 2019-12-12
  • 1970-01-01
  • 2013-03-25
  • 2012-06-19
  • 2016-03-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多