在 Pandas 中按纬度、纬度和迭代数查找最接近的行

Find Closest Rows by Lat, Lon and Iterations in Pandas

提问人:user2100039 提问时间:9/15/2023 最后编辑:user2100039 更新时间:9/15/2023 访问量:36

问:

在比较了两个 DataFrame - DF1、DF2 后,我正在尝试开发一个新的 DataFrame (DF3)。我的 df1 看起来像这样:

                num       step       latitude           longitude        time                     height   valid_time           windspeed
0              1              0 days   46.0        -122.0    2023-08-23          10.0        2023-08-23          1.2482048
1              1              0 days   45.5        -121.5    2023-08-23          10.0        2023-08-23          0.34045473
2              1              0 days   45.0        -121.0    2023-08-23          10.0        2023-08-23          0.63618374
3              2              0 days   46.0        -122.0    2023-08-23          10.0        2023-08-23          0.79829866
4              2              0 days   45.5        -121.5    2023-08-23          10.0        2023-08-23          0.7331676
5              2              0 days   45.0        -121.0    2023-08-23          10.0        2023-08-23          1.3981003
6              3              0 days   46.0        -122.0    2023-08-23          10.0        2023-08-23          1.0158184
7              3              0 days   45.5        -121.5    2023-08-23          10.0        2023-08-23          1.1108123
8              3              0 days   45.0        -121.0    2023-08-23          10.0        2023-08-23          3.4528110

我的匹配或引用数据帧 - df2 如下所示:

        site  latitude  longitude
0  Stevenson     45.69    -121.89
1  Rainier       45.00    -115.00

我正在尝试开发一个 pyhton 脚本,以根据 df1 中的每组“纬度”和“经度”以及每个“num”和“valid_time”过滤 df2。因此,“df3”应如下所示:

enter image description here

我尝试了很多东西。这是最新的错误:

from scipy.spatial.distance import cdist
df1 = pf_new
df2 = df_sites

# Function to calculate Haversine distance between two sets of coordinates
def haversine(lat1, lon1, lat2, lon2):
    lat1, lon1, lat2, lon2 = map(np.radians, [lat1, lon1, lat2, lon2])
    dlat = lat2 - lat1
    dlon = lon2 - lon1
    a = np.sin(dlat / 2) ** 2 + np.cos(lat1) * np.cos(lat2) * np.sin(dlon / 2) ** 2
    c = 2 * np.arcsin(np.sqrt(a))
    r = 6371  # Radius of Earth in kilometers
    return c * r

    
distances = cdist(df1[['latitude','longitude']],df2[['latitude','longitude']],haversine)
closest_row_indices = np.argmin(distances, axis=0)
    

df3 = df1.iloc[closest_row_indices].reset_index(drop=True)
print(df3)

并且,下面的错误。感谢您对如何进行的任何想法!

Traceback (most recent call last):

  Cell In[22], line 16
    distances = cdist(df1[['latitude','longitude']],df2[['latitude','longitude']],haversine)

  File ~\Anaconda3\envs\Stats\lib\site-packages\scipy\spatial\distance.py:2933 in cdist
    return _cdist_callable(XA, XB, metric=metric, out=out, **kwargs)

  File ~\Anaconda3\envs\Stats\lib\site-packages\scipy\spatial\distance.py:2604 in _cdist_callable
    dm[i, j] = metric(XA[i], XB[j], **kwargs)

TypeError: haversine() missing 2 required positional arguments: 'lat2' and 'lon2'
pandas scikit-learn 匹配 最近邻

评论

0赞 user2100039 9/15/2023
“过滤” = 根据“num”和“valid_time”的每个值的纬度和经度的最接近值制作 DF1 数据的子集。

答:

0赞 Corralien 9/15/2023 #1

错误非常清楚:

TypeError: haversine() missing 2 required positional arguments: 'lat2' and 'lon2'

lat2并且在调用函数时未设置,因此参数数量不足。这是因为包含 的纬度/经度值和 的值。lon2cdisthaversinelat1df1lat2df2

使用下面的代码来修复它:

def haversine(coord1, coord2):
    (lat1, lon1), (lat2, lon2) = coord1, coord2
    # the rest of your code

distances = ...
closest_row_indices = np.argmin(distances, axis=1)  # modify the axis here

# Get site name by position (and not label): iloc vs loc
df1['site'] = df2.iloc[closest_row_indices, df2.columns.get_loc('site')].values

输出:

>>> df1
   num    step  latitude  longitude        time  height  valid_time  windspeed       site
0    1  0 days      46.0     -122.0  2023-08-23    10.0  2023-08-23   1.248205  Stevenson
1    1  0 days      45.5     -121.5  2023-08-23    10.0  2023-08-23   0.340455  Stevenson
2    1  0 days      45.0     -121.0  2023-08-23    10.0  2023-08-23   0.636184  Stevenson
3    2  0 days      46.0     -122.0  2023-08-23    10.0  2023-08-23   0.798299  Stevenson
4    2  0 days      45.5     -121.5  2023-08-23    10.0  2023-08-23   0.733168  Stevenson
5    2  0 days      45.0     -121.0  2023-08-23    10.0  2023-08-23   1.398100  Stevenson
6    3  0 days      46.0     -122.0  2023-08-23    10.0  2023-08-23   1.015818  Stevenson
7    3  0 days      45.5     -121.5  2023-08-23    10.0  2023-08-23   1.110812  Stevenson
8    3  0 days      45.0     -121.0  2023-08-23    10.0  2023-08-23   3.452811  Stevenson

详:

>>> distances
array([[ 35.50785241, 556.58217101],
       [ 36.97464075, 511.72360862],
       [103.55808238, 471.6522885 ],
       [ 35.50785241, 556.58217101],
       [ 36.97464075, 511.72360862],
       [103.55808238, 471.6522885 ],
       [ 35.50785241, 556.58217101],
       [ 36.97464075, 511.72360862],
       [103.55808238, 471.6522885 ]])

顺便说一句,使用半正弦距离可以考虑地球的曲率,但在计算长距离时很有价值。如果使用欧几里得距离(默认值为 ),则漂移为每公里 8 厘米,因此对于 35 公里,您的误差小于 3 米(2.88 米)。除非您的网站真的关闭了,否则我认为使用半正弦距离(恕我直言)不是很有用。cdist

最重要的一点是,有一种更有效的方法来找到最近的站点。您可以使用空间分区。我已经回答了这种问题:

使用 BallTree (scikit-learn):

使用 KDTree (scipy):