0

我有 2 个表示笛卡尔图中点坐标的数字列表。

我的目标是将 10 范围内的许多点视为 1 点。

第一个例子:

  • 第一点 (1,2)
  • 第二点 (2,3)
  • 第三点 (3,4)
  • 第 4 点 (80,90)

坐标列表:

#(1)
x = [1,2,3, 80] 
Y = [2,3,4, 90 ]

我想删除 10 范围内最近的点(在 x 和 y 中),我们可以将前三个数字视为一个数字。

结果是:

x = [1, 80] and y = [2, 90]  or 
x = [2,80] and y = [3, 90] or 
x = [3,80] and y = [4, 90]

如果坐标列表是:

#(2)
x = [1,2,3, 80] 
Y = [2,3,70, 90 ]

我们可以将前 2 个数字视为一个

结果是:

x = [1, 80] and y = [2, 90]  or 
x = [2,80] and y = [3, 90] or 

如果它们是:

#(3)
x = [1,2, 75, 78 , 80, 101] 
Y = [2,3, 81, 86, 90 , 91]

结果:

x = [1,75, 101] and y = [2,81, 91] or
x = [1,78, 101] and y = [2,86, 91] or
x = [1,80, 101] and y = [2,90, 91] or
x = [2,75, 101] and y = [3,81, 91] or
x = [2,78, 101] and y = [3,86, 91] or
x = [2,80, 101] and y = [3,90, 91] or

我只需要这 6 个解决方案中的 1 个。如果我有x = [1,75]或并不重要x = [1,78]。重要的是只有一个接近的数字。

最后一个例子:

x = [ 95, 154, 161, 135, 138, 116]
y = [158, 166, 168, 170, 170, 171]

在这种情况下,只剩下 3 分。

171 - 170 = 1  =>  138 - 116 = 22   both results are in the range of 25 i choose to delete 116 and 171


170 - 170 = 0  => 138 - 135 = 3 both result are in the range of 25 i delete 170 and 138

170 - 168 = 2  =>  135 - 161 = 26  i cannot delete

168 - 166 = 2 =>  161 - 154 = 7  i delete 168 and 161

166 - 158 = 8 =>  154 - 95 = 59 i cannot delete

x = [95, 154, 161, 135]
Y = [158, 166, 168, 170]

我重复该操作,然后删除 x 中的 161 和 y 中的 168,因为:168 - 166 = 2 => 161 - 154 = 7

x = [95, 154, 135]
Y = [158, 166,  170]

y列表按升序排列。

比较它们的最快方法是什么?

4

2 回答 2

5

一般来说,过滤列表比就地删除列表更容易。

但要轻松过滤,您需要一个列表,而不是两个。

这正是zip它的用途。

我不确定我是否完全理解您的要求,因为从描述来看,除了 161 / 168 之外,所有内容都应该保留。我将展示您所描述的规则。

xy = zip(x, y)
new_xy = ((a, b) for a, b in xy if abs(a-b) <= 10)
x, y = zip(*new_xy)

无论您的实际目标是什么,只需将if abs(a-b) <= 10“如果应该保留这对值”的正确规则替换,就可以了。

要了解它是如何工作的,您应该尝试打印出xy(或者,如果您使用的是 Python 3.x,则list(xy))和其他中间位。

(如果您使用的是 Python 2.x,并且您的列表非常大,那么您可能应该import itertools使用它xy = itertools.izip(x, y)来避免无缘无故地创建额外的列表。如果您使用的是 Python 3.x,这不是问题,因为zip不再创建额外的列表。)


从进一步的评论来看,似乎您可能想要检查x[i]反对x[i-1],而不是反对y[i],实际上您根本不看y值 - 只是如果x[i]去,那么也是y[i]

为了简化事情,让我们y完全摆脱等式,只处理过滤x。我们可以回到y后面。

有两种方法可以解决这个问题。第一个是分解并构建一个显式循环,我们在last_value每次循环中跟踪a。第二个是获取 内的相邻对的列表x

答案是,再一次,zip

x_pairs = zip(x, x[1:])

唯一的问题是这并没有给你任何东西来比较第一个元素。例如:

>>> x = [95, 154, 161, 135, 138, 116]
>>> list(zip(x, x[1:]))
[(95, 154), (154, 161), (161, 135), (135, 138), (138, 116)]

这会告诉您是否保留 154、161、135、138 和 116……但那 95 呢?好吧,你从来没有解释过这个规则。如果您想将其与 进行比较0,请执行zip([0]+x, x). 如果您想始终保留它……您可以将其与自身进行比较,所以zip(x[:1]+x, x). 等等。无论您想要什么规则都非常容易编写。我将遵循“比较0”规则。

所以,现在我们得到了这些相邻的值对(95, 95)then (95, 154),依此类推。在每种情况下,如果两个相邻值之间的距离为<= 10,我们希望保留后一个值。这很容易:

x_pairs = zip([0]+x, x)
new_x = [pair[1] for pair in x_pairs if abs(pair[0]-pair[1]) <= 10]

y放入其中与我们最初使用的技巧相同:将其放入zip对子,然后zip将其放回。为了让事情变得更简单,我们不是先拉上拉链然后再拉上拉链y,而是一次将zip它们全部拉上:

x_pairs_y = zip([0]+x, x, y)
new_xy = (xxy[1:] for xxy in x_pairs_y if abs(xxy[0]-xxy[1]) <= 10)
new_x, new_y = zip(*new_xy)

在您的某些解释中,听起来您想要比较相邻的x值,还要比较相邻y的值,如果任何一个差异大于 10,则将它们过滤掉。

如果是这样,那就差不多了:

xy_pairs = zip([0]+x, [0]+y, x, y)
new_xy = (xyxy[2:] for xyxy in xy_pairs 
          if abs(xyxy[0]-xyxy[2]) <= 10 and abs(xyxy[1]-xyxy[3]) <= 10)
new_x, new_y = zip(*new_xy)

但是,当事情变得如此复杂以至于您的简单单行不适合一行时,您应该考虑将事情分解一下。例如,为什么不创建一个类,而不是一个x值列表和一个值列表?yPoint

class Point(object):
    def __init__(self, x, y):
        self.x, self.y = x, y
    def long_diff(self, other):
        return max(abs(self.x-other.x), abs(self.y-other.y))
points = (Point(x, y) for x, y in zip(x, y))
point_pairs = zip([Point(0, 0)]+points, points)
new_points = (pair[1] for pair in point_pairs if pair[0].long_diff(pair[1]) <= 10)
new_x, new_y = zip(*((point.x, point.y) for point in points))

它有点长,但更容易理解。

Point如果您首先使用对象列表,而不是单独的列表xy值,则更容易理解。


从进一步的评论中,听起来您想要的条件与我最后两个示例中显示的条件完全相反,并且您不知道如何否定条件。

首先,not接受任何表达式并返回相反的真值。所以:

new_xy = (xyxy[2:] for xyxy in xy_pairs 
          if not(abs(xyxy[0]-xyxy[2]) <= 10 and abs(xyxy[1]-xyxy[3]) <= 10))

或者,“如果 x 距离 <= 10 并且 y 距离 <= 10 则删除”与“如果 x 距离 > 10 或 y 距离 > 10 则保留”相同,对吧?所以:

new_xy = (xyxy[2:] for xyxy in xy_pairs 
          if abs(xyxy[0]-xyxy[2]) > 10 or abs(xyxy[1]-xyxy[3]) > 10)

另外,如果你确定你的序列总是单调递增的(也就是说,任何元素总是大于它之前的元素),你并不真的需要abs这里(只要你确保得到操作数的顺序正确的)。

于 2013-05-08T22:12:56.817 回答
1

我不知道你为什么“重复操作”,或者什么时候停止,所以我已经编码了,所以操作重复,直到没有更多的点被删除。中间点在列表中按顺序显示为 (x, y) 坐标的二元素元组,而不是单独的 x 和 y 列表。

def outrange(pts, rnge):
    partial = pts[::-1]  # current points in reverse order
    i, lastp = 0, []
    while lastp != partial:
        print('%i times around we have: %r' % (i, partial[::-1]))
        i, lastp, partial = (i+1, 
                            partial, 
                            [(pn1x, pn1y) 
                            for (pnx, pny), (pn1x, pn1y) in zip(partial[1:], partial) 
                            if abs(pn1x - pnx) > rnge or abs(pn1y - pny) > rnge
                            ] + partial[-1:])
    return partial[::-1]

if __name__ == '__main__':
    j = 0
    for rnge, x, y in [(10, [1, 2, 3, 80] , [2, 3, 4, 90 ]),
                        (10, [1, 2, 3, 80] , [2, 3, 70, 90 ]),
                        (10, [1,2, 75, 78 , 80, 101], [2,3, 81, 86, 90 , 91]),
                        (25, [ 95, 154, 161, 135, 138, 116], [158, 166, 168, 170, 170, 171])]:
        j += 1
        print('\n## Example %i: Points outside range %s of:' % (j, rnge))
        print('  x = %r\n  y = %r' % (x, y))
        pts = [(xx, yy) for xx, yy in zip(x,y)]
        ans_x, ans_y = [list(z) for z in zip(*outrange(pts, rnge))]
        print('  Answer: x = %r\n          y = %r' % (ans_x, ans_y))

输出是:

## Example 1: Points outside range 10 of:
  x = [1, 2, 3, 80]
  y = [2, 3, 4, 90]
0 times around we have: [(1, 2), (2, 3), (3, 4), (80, 90)]
1 times around we have: [(1, 2), (80, 90)]
  Answer: x = [1, 80]
          y = [2, 90]

## Example 2: Points outside range 10 of:
  x = [1, 2, 3, 80]
  y = [2, 3, 70, 90]
0 times around we have: [(1, 2), (2, 3), (3, 70), (80, 90)]
1 times around we have: [(1, 2), (3, 70), (80, 90)]
  Answer: x = [1, 3, 80]
          y = [2, 70, 90]

## Example 3: Points outside range 10 of:
  x = [1, 2, 75, 78, 80, 101]
  y = [2, 3, 81, 86, 90, 91]
0 times around we have: [(1, 2), (2, 3), (75, 81), (78, 86), (80, 90), (101, 91)]
1 times around we have: [(1, 2), (75, 81), (101, 91)]
  Answer: x = [1, 75, 101]
          y = [2, 81, 91]

## Example 4: Points outside range 25 of:
  x = [95, 154, 161, 135, 138, 116]
  y = [158, 166, 168, 170, 170, 171]
0 times around we have: [(95, 158), (154, 166), (161, 168), (135, 170), (138, 170), (116, 171)]
1 times around we have: [(95, 158), (154, 166), (135, 170)]
2 times around we have: [(95, 158), (154, 166)]
  Answer: x = [95, 154]
          y = [158, 166]

该算法按照您对示例 4 的解释中所述执行了您所做的操作,但是我在对主要问题的评论中质疑您的逻辑,因为您说您放弃了一个点然后在中间步骤中显示它。

如果您计算的其他结果与我的不同,请说明您为什么得到您的差异,即您的算法中我没有做过什么,或者仔细查看您的答案是否有错误。

于 2013-05-09T15:58:01.800 回答