Я добавил несколько дополнительных строк, чтобы ваши данные лучше работали в этом примере. Стоит отметить, что этот метод потребует значительной оптимизации и обработки ошибок, чтобы хорошо масштабироваться.
ID timestamp lat lon
0 A 2020-03-20 00:17:10 42.360000 -71.090000
1 A 2020-03-20 00:20:51 42.360000 -71.090000
2 A 2020-03-20 00:35:31 42.360000 -71.090000
3 A 2020-03-20 00:35:34 42.360000 -71.090000
4 B 2020-03-20 01:48:14 42.360000 -71.100000
5 B 2020-03-20 03:15:00 42.360000 -71.100000
6 C 2020-03-20 11:05:47 42.365259 -71.103502
7 D 2020-03-20 10:53:43 42.363174 -71.096756
8 D 2020-03-20 10:57:45 42.363260 -71.095598
9 D 2020-03-20 11:04:24 42.363303 -71.094997
10 E 2020-03-20 00:35:33 42.360001 -71.090001
11 F 2020-03-20 01:48:15 42.360003 -71.100099
Далее нам нужно немного задать условие для df.
import pandas as pd
import datetime
import numpy as np
from scipy import spatial
df = pd.read_clipboard(sep=r"[ ]{2,}")
df['lat_fix'] = df['lat'].str[-10:]
df['time'] = df['lat'].str[0:19]
df['ID'] = df['timestamp']
df['lat'] = df['lat_fix']
df = df[['ID', 'time', 'lat', 'lon']]
df['lat'] = pd.to_numeric(df['lat'])
df['time'] = pd.to_datetime(df['time'])
df['idx'] = range(0, df.shape[0])
df.set_index('time', inplace=True)
Затем мы находим точки в пределах порога расстояния. Distance_thresh_list хранит список списков, где подсписки содержат значения idx для каждой группы точек на расстоянии менее чем 100 м друг от друга.
x, y = df['lon'], df['lat']
points = np.array(list(zip(x.ravel(), y.ravel())))
tree = spatial.cKDTree(points)
distance_thresh_list = []
for p in points:
#0.0009 in decimal degrees is very close to 100m
x = tree.query_ball_point(p, 0.0009)
if len(x) > 1 and x not in distance_thresh_list:
distance_thresh_list.append(x)
Затем мы ищем уникальные идентификаторы.
spatial_matches_list = []
df_spatial_match_list = []
for i in distance_thresh_list:
df_slice = df[df['idx'].isin(i)]
uniq_id_list = df_slice.ID.unique().tolist()
if len(uniq_id_list) > 1 and uniq_id_list not in spatial_matches_list:
print(uniq_id_list)
spatial_matches_list.append(uniq_id_list)
df_spatial_match = df[df['ID'].isin(uniq_id_list)]
df_spatial_match = df[df['idx'].isin(i)]
print(i)
print(df_spatial_match)
df_spatial_match_list.append(df_spatial_match)
Наконец, мы ищем совпадения времени.
for df in df_spatial_match_list:
for idx, row in df.iterrows():
before_window = idx + datetime.timedelta(seconds=-10)
after_window = idx + datetime.timedelta(seconds=10)
df_spatial_match_slice = df[(df.index.get_level_values(0) >= before_window) & (df.index.get_level_values(0) <= after_window)]
if len(df_spatial_match_slice['ID'].unique().tolist()) > 1:
print(df_spatial_match_slice)
Вот совпадения (с дубликатами).
ID lat lon idx
time
2020-03-20 00:35:31 A 42.360000 -71.090000 2
2020-03-20 00:35:34 A 42.360000 -71.090000 3
2020-03-20 00:35:33 E 42.360001 -71.090001 10
ID lat lon idx
time
2020-03-20 00:35:31 A 42.360000 -71.090000 2
2020-03-20 00:35:34 A 42.360000 -71.090000 3
2020-03-20 00:35:33 E 42.360001 -71.090001 10
ID lat lon idx
time
2020-03-20 00:35:31 A 42.360000 -71.090000 2
2020-03-20 00:35:34 A 42.360000 -71.090000 3
2020-03-20 00:35:33 E 42.360001 -71.090001 10
ID lat lon idx
time
2020-03-20 01:48:14 B 42.360000 -71.100000 4
2020-03-20 01:48:15 F 42.360003 -71.100099 11
ID lat lon idx
time
2020-03-20 01:48:14 B 42.360000 -71.100000 4
2020-03-20 01:48:15 F 42.360003 -71.100099 11
Таким образом, приведенный выше код только проверяет, находятся ли идентификаторы рядом друг друга в течение времени. Если мы хотим рассчитать, сколько идентификаторов времени находятся рядом друг с другом, мы можем сделать это.
id_min_max_dict = {}
for i in df_spatial_match_slice_list:
for j in i.ID.unique().tolist():
id_slice = i.loc[i['ID'] == j]
id_slice_time_max = id_slice.index.max()
id_slice_time_min = id_slice.index.min()
id_min_max_dict[j] = [id_slice_time_min, id_slice_time_max]
Как только у нас есть требование хранить временные диапазоны, мы можем увидеть, сколько общих секунд между идентификаторами есть в то же местоположение.
for i in spatial_matches_list:
print(i)
time_range1 = pd.date_range(id_min_max_dict[i[0]][0], id_min_max_dict[i[0]][1], freq='S')
time_range2 = pd.date_range(id_min_max_dict[i[1]][0], id_min_max_dict[i[1]][1], freq='S')
time_range_intersection = time_range1.intersection(time_range2)
print(time_range_intersection)
print(str(len(time_range_intersection)) + ' seconds of time within ~100m')
Таким образом, пересечения времени и местоположения выглядят так. Между прочим, это не очень интересно без большего количества строк с образцами данных, и этому методу потребуется дополнительная сложность для работы с более чем 2 уникальными идентификаторами.
['A', 'E']
DatetimeIndex(['2020-03-20 00:35:33'], dtype='datetime64[ns]', freq=None)
1 seconds of time within ~100m
['B', 'F']
DatetimeIndex([], dtype='datetime64[ns]', freq=None)
0 seconds of time within ~100m