Python:通过以更特定于 Pandas 的方式编写代码来简化代码

Posted

技术标签:

【中文标题】Python:通过以更特定于 Pandas 的方式编写代码来简化代码【英文标题】:Python: simplifying code by writing it in a more Pandas specific way 【发布时间】:2016-03-24 18:31:03 【问题描述】:

我写了一些代码,根据具有相同序列号的机器查找 gps 坐标之间的距离

Fast Haversine Approximation (Python/Pandas)

但我相信如果可以简化为使用iterrowsdf.apply会更有效;但是,我似乎无法弄清楚。

由于我只需要在 ser_no[i] == ser_no[i+1] 时执行函数并在 ser_no 更改的位置插入 NaN 值,因此我似乎无法应用 Pandas 方法来提高代码效率。我看过:

Vectorised Haversine formula with a pandas dataframe Python function to calculate distance using haversine formula in pandas Vectorizing a function in pandas

不幸的是,即使在查看了这些帖子之后,我也看不到我需要做出的飞跃。

我有什么:

def haversine(lat1, long1, lat2, long2):
    r = 6371  # radius of Earth in km
    # convert decimals to degrees
    lat1, long1, lat2, long2 = map(np.radians, [lat1, long1, lat2, long2])
    # haversine formula
    lat = lat2 - lat1
    lon = long2 - long1
    a = np.sin(lat/2)**2 + np.cos(lat1)*np.cos(lat2)*np.sin(lon/2)**2
    c = 2*np.arcsin(np.sqrt(a))
    d = r*c
    return d
# pre-allocate vector    
hdist = np.zeros(len(mttt_pings.index), dtype = float)    
# haversine loop calculation
for i in range(0, len(mttt_pings.index) - 1):
    '''
    when the ser_no from i and i + 1 are the same calculate the distance
    between them using the haversine formula and put the distance in the
    i + 1 location
    '''
    if mttt_pings.ser_no.loc[i] == mttt_pings.ser_no[i + 1]:
        hdist[i + 1] = haversine(mttt_pings.EQP_GPS_SPEC_LAT_CORD[i], \
        mttt_pings.EQP_GPS_SPEC_LONG_CORD[i], \
        mttt_pings.EQP_GPS_SPEC_LAT_CORD[i + 1], \
        mttt_pings.EQP_GPS_SPEC_LONG_CORD[i + 1])
    else:
        hdist = np.insert(hdist, i, np.nan)
    '''
    when ser_no i and i + 1 are not the same, insert NaN at the ith location
    '''

【问题讨论】:

您可以发布您的数据样本吗? 【参考方案1】:

主要思想是利用shift 来检查连续的行。我还在编写一个 get_dist 函数,它只是包装了您现有的距离函数,以便在我使用 apply 计算距离时使事情更具可读性。

def get_dist(row):
    lat1 = row['EQP_GPS_SPEC_LAT_CORD']
    long1 = row['EQP_GPS_SPEC_LONG_CORD']
    lat2 = row['EQP_GPS_SPEC_LAT_CORD_2']
    long2 = row['EQP_GPS_SPEC_LONG_CORD_2']
    return haversine(lat1, long1, lat2, long2)

# Find consecutive rows with matching ser_no, and get coordinates.
coord_cols = ['EQP_GPS_SPEC_LAT_CORD', 'EQP_GPS_SPEC_LONG_CORD']
matching_ser = mttt_pings['ser_no'] == mttt_pings['ser_no'].shift(1)
shift_coords = mttt_pings.shift(1).loc[matching_ser, coord_cols]

# Join shifted coordinates and compute distances.
mttt_pings_shift = mttt_pings.join(shift_coords, how='inner', rsuffix='_2')
mttt_pings['hdist'] = mttt_pings_shift.apply(get_dist, axis=1)

在上面的代码中,我已将距离添加到您的数据框。如果你想得到一个 numpy 数组的结果,你可以这样做:

hdist = mttt_pings['hdist'].values

附带说明,您可能需要考虑使用geopy.distance.vincenty 来计算纬度/经度坐标之间的距离。一般来说,vincentyhaversine 更准确,尽管计算时间可能更长。要使用vincenty,需要对get_dist 函数进行非常小的修改。

from geopy.distance import vincenty

def get_dist(row):
    lat1 = row['EQP_GPS_SPEC_LAT_CORD']
    long1 = row['EQP_GPS_SPEC_LONG_CORD']
    lat2 = row['EQP_GPS_SPEC_LAT_CORD_2']
    long2 = row['EQP_GPS_SPEC_LONG_CORD_2']
    return vincenty((lat1, long1), (lat2, long2)).km 

【讨论】:

以上是关于Python:通过以更特定于 Pandas 的方式编写代码来简化代码的主要内容,如果未能解决你的问题,请参考以下文章

Python库Pandas数据可视化实战案例

python pandas数据框联合合并列表到索引

将 Pandas 数据框转换为包含 ID 和权重的元组列表

我可以用不同的方式/以更蛋糕的方式重构我的多个用户类型的设置吗?

为啥codeigniter2不以更安全的方式存储csrf_hash,比如session?

使用 pandas 绘图时,图例仅显示一个标签