如何配对船舶MMSI并生成含时间范围的新DataFrame
船舶配对组聚合解决方案
问题描述
我有一个包含两艘船舶MMSI、坐标及datetime字段的DataFrame,想要生成一个新DataFrame,首列为船舶配对组,同时包含starttime、endtime、own_coord、target_coord字段。试过groupby等多种逻辑都没成功,求实现配对及时间范围提取的方法。
已通过以下代码处理初始DataFrame:
df2 = result.groupby(['own_mmsi', 'target_mmsi', 'datetime']).apply( lambda x: pd.Series({ 'own_lat': x['own_geometry'].iloc[0].y, # 从own_geometry提取纬度 'own_lon': x['own_geometry'].iloc[0].x, # 从own_geometry提取经度 'target_lat': x['target_geometry'].iloc[0].y, # 从target_geometry提取纬度 'target_lon': x['target_geometry'].iloc[0].x # 从target_geometry提取经度 })).reset_index()
可通过以下示例代码复现当前DataFrame:
data = { 'own_mmsi': [212979000, 212979000, 212979000, 219592000, 236385000, 236385000, 244013009, 244389000, 244870428], 'target_mmsi': [219592000, 245451000, 245451000, 212979000, 244013009, 244013009, 236385000, 244870428, 249557000], 'datetime': [ '2019-01-01 19:07:00', '2019-01-01 22:40:00', '2019-01-01 22:41:00', '2019-01-01 19:07:00', '2019-01-01 08:03:00', '2019-01-01 08:04:00', '2019-01-01 08:04:00', '2019-01-01 20:51:00', '2019-01-01 21:17:00', ], 'own_lat': [59.148778, 59.704833, 59.707727, 59.150080, 59.330711, 59.330400, 59.327970, 59.111900, 59.106500], 'own_lon': [10.665456, 10.595289, 10.593991, 10.645380, 10.515833, 10.514336, 10.515833, 9.606330, 9.623235], 'target_lat': [59.150080, 59.706086, 59.702300, 59.148778, 59.327478, 59.327970, 59.330400, 59.105400, 59.098800], 'target_lon': [10.645380, 10.579714, 10.582900, 10.665456, 10.515475, 10.515833, 10.514336, 9.627594, 9.599260], } df = pd.DataFrame(data)
解决方案
1. 统一船舶配对组标识
首先解决双向配对去重问题(如MMSI A→B和B→A视为同一组),通过排序MMSI生成固定格式的配对组ID:
import pandas as pd # 转换datetime为时间类型 df['datetime'] = pd.to_datetime(df['datetime']) # 生成配对组:将两个MMSI排序后拼接 df['pair_group'] = df.apply( lambda row: f"{min(row['own_mmsi'], row['target_mmsi'])}_{max(row['own_mmsi'], row['target_mmsi'])}", axis=1 )
2. 按配对组聚合提取数据
按配对组分组,计算时间范围并整理坐标序列:
# 定义聚合逻辑 def aggregate_pair(group): # 按时间排序保证坐标时序正确 group_sorted = group.sort_values('datetime') return pd.Series({ 'starttime': group_sorted['datetime'].min(), 'endtime': group_sorted['datetime'].max(), 'own_coord': list(zip(group_sorted['own_lat'], group_sorted['own_lon'])), 'target_coord': list(zip(group_sorted['target_lat'], group_sorted['target_lon'])) }) # 分组聚合并重置索引 result_df = df.groupby('pair_group').apply(aggregate_pair).reset_index() # 调整列顺序,将配对组放在首列 result_df = result_df[['pair_group', 'starttime', 'endtime', 'own_coord', 'target_coord']]
3. 最终结果示例
运行后result_df的输出结构如下:
| pair_group | starttime | endtime | own_coord | target_coord |
|---|---|---|---|---|
| 212979000_219592000 | 2019-01-01 19:07:00 | 2019-01-01 19:07:00 | [(59.148778, 10.665456), (59.15008, 10.64538)] | [(59.15008, 10.64538), (59.148778, 10.665456)] |
| 212979000_245451000 | 2019-01-01 22:40:00 | 2019-01-01 22:41:00 | [(59.704833, 10.595289), (59.707727, 10.593991)] | [(59.706086, 10.579714), (59.7023, 10.5829)] |
| 236385000_244013009 | 2019-01-01 08:03:00 | 2019-01-01 08:04:00 | [(59.330711, 10.515833), (59.3304, 10.514336), (59.32797, 10.515833)] | [(59.327478, 10.515475), (59.32797, 10.515833), (59.3304, 10.514336)] |
| 244389000_244870428 | 2019-01-01 20:51:00 | 2019-01-01 20:51:00 | [(59.1119, 9.60633)] | [(59.1054, 9.627594)] |
| 244870428_249557000 | 2019-01-01 21:17:00 | 2019-01-01 21:17:00 | [(59.1065, 9.623235)] | [(59.0988, 9.59926)] |
关键说明
- 配对组生成逻辑确保双向配对被归为同一组,避免重复统计。
- 聚合前按时间排序,保证坐标序列的时间顺序正确。
- 若需将坐标序列展开为每行一个时间点的格式,可去掉聚合中的
list(zip(...)),保留时间维度进行分组。
内容的提问来源于stack exchange,提问作者sneha_jerin
相关产品推荐
相关产品推荐

