python - Pandas 数据框 : join items in range based on their geo coordinates (longitude and latitude)

标签 python pandas latitude-longitude haversine

我得到了一个数据框,其中包含带有纬度和经度的地点。想象一下城市。

df = pd.DataFrame([{'city':"Berlin", 'lat':52.5243700, 'lng':13.4105300},
                   {'city':"Potsdam", 'lat':52.3988600, 'lng':13.0656600},
                   {'city':"Hamburg", 'lat':53.5753200, 'lng':10.0153400}]);

现在我试图让所有城市都在一个半径范围内。假设距离柏林 500 公里、汉堡 500 公里等的所有城市。我会通过复制原始数据帧并将两者与距离函数连接来做到这一点。

中间结果大概是这样的:

Berlin --> Potsdam
Berlin --> Hamburg
Potsdam --> Berlin
Potsdam --> Hamburg
Hamburg --> Potsdam
Hamburg --> Berlin

这个经过分组(归约)后的最终结果应该是这样的。 备注:如果值列表包含城市的所有列,那就太好了。

Berlin --> [Potsdam, Hamburg]
Potsdam --> [Berlin, Hamburg]
Hamburg --> [Berlin, Potsdam]

或者只是一个城市周围 500 公里范围内的城市数。

Berlin --> 2
Potsdam --> 2
Hamburg --> 2

由于我是 Python 的新手,我将不胜感激任何起点。我熟悉 haversine 距离。但不确定 Scipy 或 Pandas 中是否有有用的距离/空间方法。

很高兴你能给我一个起点。到目前为止,我尝试关注 this post .

更新:这个问题背后的最初想法来自 Two Sigma Connect Rental Listing Kaggle Competition .这个想法是让那些列表在另一个列表周围 100m。其中 a) 表示密度,因此是一个受欢迎的区域,b) 如果比较地址,您可以找出是否有交叉路口,因此是一个嘈杂的区域。因此,您不需要完整的项目到项目关系,因为您不仅需要比较距离,还需要比较地址和其他元数据。 PS:我不会将解决方案上传到 Kaggle。我只是想学习。

最佳答案

您可以使用:

from math import radians, cos, sin, asin, sqrt

def haversine(lon1, lat1, lon2, lat2):

    lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])

    # haversine formula 
    dlon = lon2 - lon1 
    dlat = lat2 - lat1 
    a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
    c = 2 * asin(sqrt(a)) 
    r = 6371 # Radius of earth in kilometers. Use 3956 for miles
    return c * r

首先需要与merge交叉连接,通过 boolean indexing 删除 city_xcity_y 中具有相同值的行:

df['tmp'] = 1
df = pd.merge(df,df,on='tmp')
df = df[df.city_x != df.city_y]
print (df)
    city_x     lat_x     lng_x  tmp   city_y     lat_y     lng_y
1   Berlin  52.52437  13.41053    1  Potsdam  52.39886  13.06566
2   Berlin  52.52437  13.41053    1  Hamburg  53.57532  10.01534
3  Potsdam  52.39886  13.06566    1   Berlin  52.52437  13.41053
5  Potsdam  52.39886  13.06566    1  Hamburg  53.57532  10.01534
6  Hamburg  53.57532  10.01534    1   Berlin  52.52437  13.41053
7  Hamburg  53.57532  10.01534    1  Potsdam  52.39886  13.06566

然后申请haversine功能:

df['dist'] = df.apply(lambda row: haversine(row['lng_x'], 
                                            row['lat_x'], 
                                            row['lng_y'], 
                                            row['lat_y']), axis=1)

过滤距离:

df = df[df.dist < 500]
print (df)
    city_x     lat_x     lng_x  tmp   city_y     lat_y     lng_y        dist
1   Berlin  52.52437  13.41053    1  Potsdam  52.39886  13.06566   27.215704
2   Berlin  52.52437  13.41053    1  Hamburg  53.57532  10.01534  255.223782
3  Potsdam  52.39886  13.06566    1   Berlin  52.52437  13.41053   27.215704
5  Potsdam  52.39886  13.06566    1  Hamburg  53.57532  10.01534  242.464120
6  Hamburg  53.57532  10.01534    1   Berlin  52.52437  13.41053  255.223782
7  Hamburg  53.57532  10.01534    1  Potsdam  52.39886  13.06566  242.464120

最后使用 groupby 创建 list 或获取 size:

df1 = df.groupby('city_x')['city_y'].apply(list)
print (df1)
city_x
Berlin     [Potsdam, Hamburg]
Hamburg     [Berlin, Potsdam]
Potsdam     [Berlin, Hamburg]
Name: city_y, dtype: object

df2 = df.groupby('city_x')['city_y'].size()
print (df2)
city_x
Berlin     2
Hamburg    2
Potsdam    2
dtype: int64

也可以使用 numpy haversine solution :

def haversine_np(lon1, lat1, lon2, lat2):
    """
    Calculate the great circle distance between two points
    on the earth (specified in decimal degrees)

    All args must be of equal length.    

    """
    lon1, lat1, lon2, lat2 = map(np.radians, [lon1, lat1, lon2, lat2])

    dlon = lon2 - lon1
    dlat = lat2 - lat1

    a = np.sin(dlat/2.0)**2 + np.cos(lat1) * np.cos(lat2) * np.sin(dlon/2.0)**2

    c = 2 * np.arcsin(np.sqrt(a))
    km = 6367 * c
    return km

df['tmp'] = 1
df = pd.merge(df,df,on='tmp')
df = df[df.city_x != df.city_y]
#print (df)

df['dist'] = haversine_np(df['lng_x'],df['lat_x'],df['lng_y'],df['lat_y'])
    city_x     lat_x     lng_x  tmp   city_y     lat_y     lng_y        dist
1   Berlin  52.52437  13.41053    1  Potsdam  52.39886  13.06566   27.198616
2   Berlin  52.52437  13.41053    1  Hamburg  53.57532  10.01534  255.063541
3  Potsdam  52.39886  13.06566    1   Berlin  52.52437  13.41053   27.198616
5  Potsdam  52.39886  13.06566    1  Hamburg  53.57532  10.01534  242.311890
6  Hamburg  53.57532  10.01534    1   Berlin  52.52437  13.41053  255.063541
7  Hamburg  53.57532  10.01534    1  Potsdam  52.39886  13.06566  242.311890

关于python - Pandas 数据框 : join items in range based on their geo coordinates (longitude and latitude),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42877802/

相关文章:

python - 提取字典中的值

python - 使用python检测音频文件完整性

python - 如何编写永远不会产生任何东西的 Python 生成器函数

python - 计算时间戳与月份的差异

c# - 使用 haversine 公式计算地理坐标距离给出了错误的输出

google-maps - 如果我知道 GPS 的中心位置和左上角位置,如何获得矩形的纬度和经度?

python - 数据添加到Django DB后自动调用方法

python - 将 Pandas DataFrame 转换为 Orange Table

python - 如何使用 Pandas 获取条件行每组每 n 天的斜率?

MySql - innodb - 复合键上的行级锁定如何用简单的话工作