【问题标题】:"Sensibly" remove points in a Python list“明智地”删除 Python 列表中的点
【发布时间】:2017-08-22 00:50:09
【问题描述】:

假设我有两个数组表示校准曲线的 x 和 y 坐标。

X = [1,2,3,4,5,6,7,8,9,10,12,14,16,18,20,30,40,50]
Y = [2,4,6,8,10,12,14,16,18,20,24,28,32,36,40,60,80,100]

我上面的示例数组包含 18 个点。您会注意到 x 值不是线性间隔的; x 值越低,点越多。

假设我需要将校准曲线中的点数减少到 13 个点。显然,我可以只删除前五个或最后五个点,但这会缩短我的 x 值的整体范围。为了保持范围和最小化 x 值之间的空间,我会优先删除值 x= 2、4、6、8、10。删除这些 x 点及其各自的 y 值将根据需要在曲线中留下 13 个点。

如何在 Python 中自动选择和删除这个点? IE。是否有一种算法可以从列表中选择最佳 x 点,其中“最佳”被定义为在保持整体范围并遵守新点数的同时保持点尽可能接近。

请注意,剩余的点必须在原始列表中,因此我无法将 18 个点插入到 13 个点的网格中。

【问题讨论】:

  • 对不起 - 我已经编辑了我的原始问题(希望如此!)澄清事情。基本上我想减少值的数量,但保持在整个范围内(即最小 x 和最大 x)。为了实现这一点,我想删除靠近的点

标签: python list filter reduce


【解决方案1】:

这将使所选点之间的平方根距离最大化。从某种意义上说,它尽可能地分散了要点。

import itertools
list(max(itertools.combinations(sorted(X), 13), i
         key=lambda l: sum((a - b) ** 2 for a, b in zip(l, l[1:]))))

请注意,这仅适用于小问题。选择k个点的时间复杂度是O(k * (len(X) choose k)),所以基本上是O(exp(len(X))。因此,甚至不要考虑将其用于 len(X) == 100k == 10

【讨论】:

  • 这是一个非常聪明的想法,具有直观的动机,所以 +1。对于给定的问题大小,它会很好地工作,当然如果 18 变得更大,它很快就会变得不可行。我不太确定在这种情况下您将如何计算它。也许一些爬山方法会起作用,或者至少提供一种合理的启发式方法。
  • 当然你是对的。我在答案中添加了注释。
  • 这仍然是一个很好的标准,即使对于 100、10 的情况。你只需要一种非蛮力的方法来找到它,或者,如果做不到,至少近似它。
【解决方案2】:
X = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 20, 30, 40, 50]
Y = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 24, 28, 32, 36, 40, 60, 80, 100]

assert len(X) == len(set(X)), "Duplicate X values found"

points = list(zip(X, Y))
points.sort()  # sorts by X

while len(points) > 13:
    # Find index whose neighbouring X values are closest together
    i = min(range(1, len(points) - 1), key=lambda p: points[p + 1][0] - points[p - 1][0])
    points.pop(i)

print(points)

输出:

[(1, 2), (3, 6), (5, 10), (7, 14), (10, 20), (12, 24), (14, 28), (16, 32), (18, 36), (20, 40), (30, 60), (40, 80), (50, 100)]

如果你又想要原版系列:

X, Y = zip(*points)

【讨论】:

    【解决方案3】:

    实现这一目标的算法:

    1. 将每个数字转换为左右数字的绝对差之和。如果缺少数字,第一种或最后一种情况,则使用 MAX_INT。例如,1 将变为 MAX_INT; 2 会变成 2,10 会变成 3。
    2. 删除总和最小的第一个案例。
    3. 如果您需要删除更多号码,请转到 1。

    这将删除 2,4,6,8,10,3,...

    【讨论】:

      【解决方案4】:

      这是一种递归方法,它反复删除最不遗漏的点:

      def mostRedundantPoint(x):
          #returns the index, i, in the range 0 < i < len(x) - 1
          #that minimizes x[i+1] - x[i-1]
          #assumes len(x) > 2 and that x
          #is sorted in ascending order
      
          gaps = [x[i+1] - x[i-1] for i in range(1,len(x)-1)]
          i = gaps.index(min(gaps))
          return i+1
      
      def reduceList(x,k):
          if len(x) <= k:
              return x
          else:
              i = mostRedundantPoint(x)
              return reduceList(x[:i]+x[i+1:],k)
      
      X = [1,2,3,4,5,6,7,8,9,10,12,14,16,18,20,30,40,50]
      print(reduceList(X,13))
      #prints [1, 3, 5, 7, 10, 12, 14, 16, 18, 20, 30, 40, 50]
      

      此列表与您的预期输出基本一致,因为 7 与 8 具有相同的净效果。从某种意义上说,将sorted([random.randint(1,10**6) for i in range(1000)]) 从 1000 个元素减少到 100 个元素几乎是瞬间的,这是相当快的。它是递归的这一事实意味着,如果您尝试删除比这更多的点,它将破坏堆栈,但是您的预期问题大小似乎不应该成为问题。如果需要,您当然可以用循环替换递归。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-11-14
        • 2013-10-25
        • 1970-01-01
        • 2020-05-08
        相关资源
        最近更新 更多