提取那些至少相距 3 度的点

Extract those points which are at least 3 degrees far from each other

我在地球表面有9个点(经度,纬度以度为单位)如下。

XY = [(100, 10), (100, 11), (100, 13), (101, 10), (101, 11), (101, 13), (103, 10), (103, 11), (103, 13)]
print (len(XY))
# 9

我想提取那些彼此相距至少 3 度的点。

我试过如下

results = []

for point in XY:
    x1,y1 = point

    for result in results:
        x2,y2 = result

        distance = math.hypot(x2 - x1, y2 - y1)

        if distance >= 3:
            results.append(point)

print (results)

但输出为空。

编辑 2

from sklearn.metrics.pairwise import haversine_distances
from math import radians

results = []

for point in XY:
    x1,y1 = [radians(_) for _ in point]

    for result in results:  
        distance = haversine_distances((x1,y1), (x2,y2))    
        print (distance)

        if distance >= 3:
            results.append(point)

print (results)

结果还是空的

编辑 3

results = []

for point in XY:
    x1,y1 = point

    for point in XY:        
        x2,y2 = point

        distance = math.hypot(x2 - x1, y2 - y1)
        print (distance)

        if distance >= 3:
            results.append(point)

print (results)
print (len(results))
# 32   # unexpected len

重要:你说过你想“提取那些至少相距3度的点”但是那么你已经使用了 Euclidean distance with math.hypot(). As , this should use the Haversine angular distance.

由于您的点是 "(经度,以度为单位的纬度)",它们首先需要是 converted to radians。并且应该翻转这些对,以便按照 haversine_distances() 函数的要求,纬度排在第一位。这可以通过以下方式完成:

XY_r = [(math.radians(lat), math.radians(lon)) for lon, lat in XY]

这是关键 - none 组合或循环是必要的。如果 haversine_distances() 在点列表中传递,它将计算它们之间的距离 all 并将结果作为数组的数组提供。然后可以将这些转换回度数并进行检查;或将 3 degrees 转换为弧度,然后检查 h-dists。

import math
import numpy as np
from sklearn.metrics.pairwise import haversine_distances

XY = [(100, 10), (100, 11), (100, 13), (101, 10), (101, 11), (101, 13), (103, 10), (103, 11), (103, 13)]

# convert to radians and flip so that latitude is first
XY_r = [(math.radians(lat), math.radians(lon)) for lon, lat in XY]
distances = haversine_distances(XY_r)  # distances array-of-arrays in RADIANS
dist_criteria = distances >= math.radians(3)  # at least 3 degrees (in radians) away
results = [point for point, result in zip(XY, dist_criteria) if np.any(result)]

print(results)
print(len(results))
print('<3 away from all:', set(XY) - set(results))

输出:

[(100, 10), (100, 11), (100, 13), (101, 10), (101, 13), (103, 10), (103, 11), (103, 13)]
8
<3 away from all: {(101, 11)}

根据之前的编辑和您的原始代码:

您的前两次尝试给出的结果都是空的,原因是:

results = []

for point in XY:
    ...
    for result in results:

results 被初始化为一个空列表。所以for result in results循环会直接退出。循环内没有任何内容执行。

由于重复,第三次尝试得到了 32 个结果。你有:

for point in XY:
    ...
    for point in XY:

所以你得到的一些 point 将是相同的点数。

避免 循环中的重复:

  1. 为其添加检查并转到下一个迭代:

    if (x1, y1) == (x2, y2):
        continue
    

    顺便说一句,您正在破坏 point 变量,因为它在两个循环中都被重用了。它不会导致问题,但会使您的代码更难调试。要么让它们成为 point1point2,或者更好,而不是 for point in XY: x1, y1 = point,你可以直接做 for x1, y1 in XY - 这就是所谓的 tuple unpacking.

    for x1, y1 in XY:
        for x2, y2 in XY:
            if (x1, y1) == (x2, y2):
                continue
            ...
    
  2. 您还需要将result改为set而不是列表,这样当超过3个时,相同的点就不会重新添加到结果中远离另一点。集合不允许重复,这样点就不会在结果中重复

    使用itertools.combinations() 获得独特的点对,没有重复。这允许您跳过重复检查(除非 XY 实际上有重复点)并将前一个块减少到一个 for 循环:

    import itertools
    import math
    
    results = set()  # unique results
    for (x1, y1), (x2, y2) in itertools.combinations(XY, r=2):
        distance = math.hypot(x2 - x1, y2 - y1)  # WRONG! see above
        if distance >= 3:
            # add both points
            results.update({(x1, y1), (x2, y2)})
    
    print(results)
    print(len(results))
    print('<3 away from all:', set(XY) - results)
    

    (错误的)输出:

    {(103, 11), (100, 13), (101, 13), (100, 10), (103, 10), (101, 10), (103, 13), (100, 11)}
    8
    <3 away from all: {(101, 11)}
    

    (结果相同,只是输入数据巧合而已。)