Python: группировка по времени и пространству - PullRequest
1 голос
/ 23 апреля 2020
    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

Я хотел бы посмотреть, есть ли совпадение между двумя пользователями в радиусе 100 метров в любое время суток в течение не менее 10 секунд. Я хотел бы иметь вывод, как следующий

df
      usuerI     userJ     centroid.lat  centroid.lon     time
0      A          B         42.360000      -71.094997      33s
1      B          D         42.365259      -71.103502      5s

Ответы [ 2 ]

0 голосов
/ 23 апреля 2020

Я добавил несколько дополнительных строк, чтобы ваши данные лучше работали в этом примере. Стоит отметить, что этот метод потребует значительной оптимизации и обработки ошибок, чтобы хорошо масштабироваться.

   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
0 голосов
/ 23 апреля 2020

Я не знаю, что вы пытались, но вы могли бы, например, начать так. Я не принял во внимание 10-секундное время, но его легко добавить. Я использовал для измерения расстояния geopy.distance.distance. Код ниже сохраняет встречи в списке, из которого вы можете легко создать новый фрейм данных.

import numpy as np
import geopy.distance

# threshold distance in km
threshold_distance = 0.1

# list of IDs
id_list = list(df.index.levels[1])

# combinations of IDs
combs = list(combinations(id_list, 2))

# list to store the indices of the meetings
meetings = []

# go through combinations
for i, j in combs:

    # get the indices (numbers) of both IDs
    i_indices = [a[0] for a in df.iloc[df.index.get_level_values(1) == i].index.values]
    j_indices = [a[0] for a in df.iloc[df.index.get_level_values(1) == j].index.values]


    # go through the ID's data
    for i_index in i_indices:
        for j_index in j_indices:
                # if the date coincides
                if df.at[(i_index, i), "date"]!=df.at[(j_index, j), "date"]:
                    continue

                # use geopy to calculate the distance from the coordinates
                coords1 = (df.at[(i_index,i),"lat"],df.at[(i_index, i),"lon"])
                coords2 = (df.at[(j_index,j),"lat"],df.at[(j_index, j),"lon"])
                if geopy.distance.distance(coords1, coords2).km < threshold_distance:
                    meetings.append((i_index, j_index))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...