如何将按mmsi分组计算的船速序列插入DataFrame新列?
解决按MMSI分组计算连续行船速并赋值到新列的问题
你当前的代码问题在于:groupby.apply返回的是每个分组对应的列表,但直接赋值给新列时,每个分组的所有行都会被填充整个列表,而非将列表元素逐一对应到每行。以下是修正方案:
关键修正点
- 确保分组内的行按时间戳排序(否则连续行的时间逻辑错误)
- 修改速度计算函数,返回与分组行索引匹配的
Series(而非列表),让Pandas能自动对齐行 - 处理分组后的索引,保证结果能正确映射到原DataFrame的每一行
完整代码示例
1. 先实现速度计算函数(以Haversine公式为例,你可替换为自己的计算逻辑)
import pandas as pd import numpy as np def calculate_speed(lons, lats, timestamps): # Haversine公式计算两点间海里距离 def haversine(lon1, lat1, lon2, lat2): lon1, lat1, lon2, lat2 = map(np.radians, [lon1, lat1, lon2, lat2]) dlon = lon2 - lon1 dlat = lat2 - lat1 a = np.sin(dlat/2)**2 + np.cos(lat1) * np.cos(lat2) * np.sin(dlon/2)**2 c = 2 * np.arcsin(np.sqrt(a)) return c * 3440.065 # 地球半径(海里) # 计算时间差(小时) time_diff = timestamps.diff().dt.total_seconds() / 3600 # 计算连续点的距离 dist = [np.nan] for i in range(1, len(lons)): dist.append(haversine(lons.iloc[i-1], lats.iloc[i-1], lons.iloc[i], lats.iloc[i])) # 计算速度(节,海里/小时) speed_knots = np.array(dist) / time_diff # 返回与分组行索引匹配的Series return pd.Series(speed_knots, index=lons.index)
2. 处理DataFrame并赋值新列
# 读取数据 df = pd.read_csv('filtered.csv') # 保留存在重复MMSI的行 df = df[df.duplicated('mmsi', keep=False)] # 转换时间戳为datetime格式 df['unix_datetime'] = pd.to_datetime(df['timestamp'], unit='s') # 关键:按MMSI和时间戳排序,确保分组内的行是时间顺序 df = df.sort_values(['mmsi', 'timestamp']) # 按MMSI分组计算速度,重置索引后赋值到新列 df['speed_knots'] = df.groupby('mmsi').apply( lambda x: calculate_speed(x.lon, x.lat, x.unix_datetime) ).reset_index(level=0, drop=True)
针对你原有函数的适配
如果你不想替换速度计算逻辑,只需修改原有speed函数的返回值,将列表转为对应索引的Series即可:
def speed(lons, lats, timestamps): # 你的原有计算逻辑 ... speed_knots = speed_knots.tolist() # 返回与输入行索引匹配的Series return pd.Series(speed_knots, index=lons.index)
内容的提问来源于stack exchange,提问作者JavierSando
相关产品推荐
相关产品推荐

