对haversine公式使用列表推导

Posted

技术标签:

【中文标题】对haversine公式使用列表推导【英文标题】:using list comprehensions for haversine formula 【发布时间】:2021-08-31 17:54:18 【问题描述】:

我有两个数据框。房屋位置之一和餐厅位置之一,其坐标均以纬度/经度为单位。我需要创建一个新列来计算它们之间的距离。例如,如果我有一个包含 5 个房屋位置的列表,则预期结果将是每个餐厅的 5 次距离计算(25 个值)。 df1 是位置,df2 是餐厅。

我的距离计算在这里,但我确实改变了几次:

版本 1:

def distance(location, restaurant): 
    lat1, lon1 = location
    lat2, lon2 = restaurant
    radius = 6371 *1000# km
    dlat = math.radians(lat2-lat1)
    dlon = math.radians(lon2-lon1)
    a = math.sin(dlat/2) * math.sin(dlat/2) + math.cos(math.radians(lat1)) \
        * math.cos(math.radians(lat2)) * math.sin(dlon/2) * math.sin(dlon/2)
    c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
    d = radius * c
    return d

版本 2:

def haversine(lat1, lon1, lat2, lon2):
    radius = 6371 
    dlat = math.radians(lat2-lat1)
    dlon = math.radians(lon2-lon1)
    a = math.sin(dlat/2) * math.sin(dlat/2) + math.cos(math.radians(lat1)) \
        * math.cos(math.radians(lat2)) * math.sin(dlon/2) * math.sin(dlon/2)
    c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
    d = radius * c
    return d

我试过写一个循环,但它返回'Series object is not callable'错误:

ll = [] 
for index,rows in df2.iterrows():
        lat1 = rows['Latitude']
        lon1 = rows['Longitude']
        for i,r in df1.iterrows():
                dist = distance((lat1,lon1),(r['Latitude'],r['Longitude']))
                ll.append(rows(float(dist)))

然后我尝试使用列表推导,两种不同的方式:

df1['result'] = df1.apply(lambda x: float(haversine(df1['Latitude'], df1['Longitude'], df2['Latitude'], df2['Longitude']), axis=1)) 

第一个返回错误'cannot convert the series to

第二个没有给我想要的结果:

Dist = []
for w, x, y, z in zip(df1['Latitude'], df2['Longitude'], df2['Latitude'], df2['Longitude']):
    Dist.extend([distance((w,x),(y,z))])
print(Dist)

output: [515.38848499753, 54.26312420254462, 10.563518031233743, 374.5045129388741, 451.6737920301973]

这样做的正确方法是什么?最终,我将不得不将其扩展到 10 万间房屋和 2480 家餐厅。很遗憾,我没有共享数据的权限。

【问题讨论】:

当你扩大规模时,结果将有 2.48 亿个条目。您可能应该找到一种优化它的方法 - 可能只是按街道或社区进行优化,而不是获取每个房子的距离。 ll.append(rows(float(dist))) 应该是 ll.append(dist)。您为什么要尝试将rows 用作函数? @Barmar 我的错误,我可以删除那个错字。 & 我同意,但是,我不负责这个项目,我只是想完成对我的要求 【参考方案1】:

您可以使用运行速度更快的矢量化操作,这是一个 sn-p,它采用两个维度为 nX2、mX2 的数组,即保存 n 和 m 个位置

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


def haversine(locations1, locations2):
    locations1 = np.deg2rad(locations1)
    locations2 = np.deg2rad(locations2)
    return haversine_distances(locations1, locations2) * 6371000

使用您的尺寸,它会在 10 秒内在我的机器上运行

【讨论】:

我没有能力在我的电脑上导入这个包 这是一种更强大的解决方案,如果您计划在更大的数据帧上扩展此脚本,它可能具有更快的执行时间。如果可能,我建议您在虚拟环境中安装 scikit-learn。 @PreciXon 我刚刚获得使用此软件包的许可,并将继续使用此选项,但我仍然没有看到正确的输出,我觉得我错过了一些东西 尝试将>>>haversine_distances(locations1, locations2)更改为>>>haversine_distances([locations1, locations2]) @amanda 我刚刚尝试使用此代码,它给了我一个错误,说输入的尺寸有误。我很确定您的数据集中缺少值。【参考方案2】:

您首先必须将两个数据框都转换为浮点数

df1 = df1.astype(float)
df2 = df2.astype(float)

您尝试的第一种方法现在应该可以了。如果没有,这里有一个 sn-p 可以

distances, empty_value_indexes = [], []
for i in range(len(df1['Latitude'])):
    try: 
        d = haversine(df1['Latitude'][i], df1['Longitude'][i], df2['Latitude'][i], df2['Longitude'][i])
        distances.append(d)
    except KeyError as e:
        print("Encounted KeyError in the i'th iteration, appending 0 to list")
        distances.append(0)
        empty_values_indexes.append(i)
    except Exception as e:
        print(d"Encountered a different error message - \nstr(e)")
        


df1['results'] = results
print(f"Indexes of empty Values: empty_values_indexes")

【讨论】:

我喜欢您提供的第二个 sn-p,但它返回“关键错误:5”。我也得到了我的第一个方法,但它以列表的形式返回距离,我希望它作为 df1 中的另一列返回。建议? sn-p >>>df1['results'] = results 中的最后一行应该以列表作为其值。至于关键错误,我没有遇到过。可能是您的数据框有空值。我将更新 sn-p 以处理此类区域。【参考方案3】:
import pandas as pd
import numpy as np


# haversine formula
def haversine(lat1, lon1, lat2, lon2):
    radius = 6371
    lat1, lon1, lat2, lon2 = map(np.deg2rad, [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))
    total_dist = radius * c
    return total_dist


df1 = pd.DataFrame(
    'lat':[-14.345234, -12.456345, -20.111111, -15.222222, -16.111111],
    'lon':[145.632423, 143.653535, 147.111111, 146.222222, 148.111111]
)

df2 = pd.DataFrame(
    'lat':[-14.345234, -12.456345, -20.111111, -15.222222, -16.111111],
    'lon':[145.632423, 143.653535, 147.111111, 146.222222, 148.111111]
)

# New column for df1, just a list of zeros
df1['distance'] = np.zeros(len(df1))

# Iterate over the rows of df1.
for index, row in df1.iterrows():
    # For each row in df1, iterate over the rows of df2.
    for index2, row2 in df2.iterrows():
        # Calculate the distance between each pair of locations.
        df1.loc[index, 'distance'] += haversine(row['lat'], row['lon'],
                                                row2['lat'], row2['lon'])
# New column for df2, just a list of zeros
df2['distance'] = np.zeros(len(df2))

# Iterate over the rows of df2.
for index, row in df2.iterrows():
    # For each row in df2, iterate over the rows of df1.
    for index2, row2 in df1.iterrows():
        # Calculate the distance between each pair of locations.
        df2.loc[index, 'distance'] += haversine(row['lat'], row['lon'],
                                                row2['lat'], row2['lon'])


"""
The above is a bit of a mess, but it does work. I am not sure how to convert it to a class method though.
"""

【讨论】:

这并没有达到目的。我需要从每个餐厅到每个位置的距离。即 5x5 = 25,而不是 5x2 = 10

以上是关于对haversine公式使用列表推导的主要内容,如果未能解决你的问题,请参考以下文章

Haversine 公式单元错误 - PL/SQL

求解Haversine公式的分量时,sympy挂起[重复]

如何使用 Haversine 公式计算行驶距离(不是位移)?

计算两个latitude-longitude点之间的距离? (Haversine公式)

使用haversine公式,结果太高

Haversine 公式 - 结果太高