基于距离与时间的事件分组高效实现方案咨询
高效分组150万条时空事件数据的方案
我有一组包含year(年)、month(月)、day(日)、hour(时)、minute(分)、second(秒)、latitude(纬度)、longitude(经度)字段的事件数据集,需要按以下规则分组:事件彼此间距在20公里以内,且时间间隔在1秒以内则归为同一组。之前用嵌套for循环实现,但150万条数据处理速度极慢,求更高效的分析方法。
数据样本
| year | month | day | hour | minute | second | latitude | longitude |
|---|---|---|---|---|---|---|---|
| 10 | 1 | 2 | 4 | 23 | 46.2452316 | 43.35 | 26.846 |
| 10 | 1 | 2 | 4 | 23 | 46.5867386 | 43.24 | 26.883 |
| 10 | 1 | 2 | 4 | 25 | 47.2160149 | 43.281 | 26.959 |
| 10 | 1 | 2 | 5 | 0 | 38.0627136 | 43.346 | 27.24 |
| 10 | 1 | 5 | 23 | 32 | 37.9809761 | 43.033 | 19.12 |
| 10 | 1 | 5 | 23 | 32 | 37.9879913 | 43.045 | 18.998 |
| 10 | 1 | 5 | 23 | 39 | 35.3192711 | 42.943 | 18.934 |
| 10 | 1 | 6 | 1 | 6 | 57.6164627 | 42.979 | 19.291 |
| 10 | 1 | 6 | 1 | 11 | 7.6762986 | 42.928 | 19.431 |
| 10 | 1 | 6 | 1 | 18 | 32.5538177 | 42.883 | 19.734 |
| 10 | 1 | 6 | 6 | 28 | 7.1016998 | 42.95 | 19.634 |
高效解决方案
1. 预处理:合并时间字段为统一时间戳
先把分散的时间字段合并成毫秒级时间戳,方便快速计算时间差:
import pandas as pd # 读取数据 df = pd.read_csv('your_data.csv') # 合并时间字段为datetime对象(注意year=10需根据实际业务调整为对应年份,比如2010) df['datetime'] = pd.to_datetime(df[['year', 'month', 'day', 'hour', 'minute', 'second']], errors='coerce', yearfirst=False) # 转换为毫秒级时间戳 df['timestamp'] = df['datetime'].astype('int64') // 10**6
2. 时间预过滤:缩小待匹配数据范围
先按时间戳排序,只对时间差≤1秒的事件进行空间匹配,直接过滤掉时间不达标数据:
# 按时间戳排序 df = df.sort_values('timestamp').reset_index(drop=True) # 计算每个事件的时间范围(前后1秒) df['time_low'] = df['timestamp'] - 1000 df['time_high'] = df['timestamp'] + 1000
3. 核心聚类:用DBSCAN实现时空分组
DBSCAN是适合这种密度聚类场景的算法,结合空间(米)和时间(毫秒)维度,直接输出分组ID:
from sklearn.cluster import DBSCAN import pyproj # 经纬度转UTM坐标(米),需根据数据所在区域选择对应UTM带(示例为EPSG:32634,对应北半球34带) proj = pyproj.Transformer.from_crs("EPSG:4326", "EPSG:32634", always_xy=True) df['x'], df['y'] = proj.transform(df['longitude'], df['latitude']) # 构建时空特征矩阵:x(米)、y(米)、timestamp(毫秒) X = df[['x', 'y', 'timestamp']].values # 自定义时空距离函数:满足空间≤20000米且时间≤1000毫秒则视为同类 def spatiotemporal_dist(a, b): spatial = ((a[0]-b[0])**2 + (a[1]-b[1])**2)**0.5 temporal = abs(a[2]-b[2]) # 只要有一个超过阈值,就返回大于eps的数值,DBSCAN会判定为非同类 return max(spatial, temporal) # 运行DBSCAN:eps设为20000(空间阈值),min_samples=1表示单个事件也自成一组 dbscan = DBSCAN(eps=20000, min_samples=1, metric=spatiotemporal_dist) df['group_id'] = dbscan.fit_predict(X)
4. 性能优化补充
- 内存不足时用Dask:如果150万条数据内存放不下,用Dask进行分布式计算,兼容pandas和scikit-learn API。
- Numba加速自定义函数:给
spatiotemporal_dist加上@numba.jit(nopython=True)装饰器,大幅提升计算速度。 - 提前过滤无效数据:删除缺失经纬度、时间的行,减少计算量。
内容的提问来源于stack exchange,提问作者user2096665
相关产品推荐
相关产品推荐

