Делает несколько шагов, но в конечном итоге добирается до места ..
Инициализация данных:
import pandas as pd
from pandas import Timestamp
import numpy as np
dict_ ={'id': {0: 1, 1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1, 8: 1, 9: 1, 10: 1, 11: 2, 12: 2, 13: 2, 14: 2, 15: 2, 16: 2, 17: 2, 18: 2, 19: 2}, 'class_name': {0: 'foo', 1: 'bar', 2: 'foo', 3: 'baz', 4: 'bar', 5: 'foo', 6: 'bar', 7: 'baz', 8: 'foo', 9: 'bar', 10: 'baz', 11: 'foo', 12: 'bar', 13: 'foo', 14: 'bar', 15: 'baz', 16: 'baz', 17: 'bar', 18: 'bar', 19: 'foo'}, 'created_at': {0: Timestamp('2019-02-08 19:11:04'), 1: Timestamp('2019-02-08 19:11:34'), 2: Timestamp('2019-02-08 19:12:04'), 3: Timestamp('2019-02-08 19:12:35'), 4: Timestamp('2019-02-08 19:13:05'), 5: Timestamp('2019-02-08 19:13:35'), 6: Timestamp('2019-02-08 19:14:04'), 7: Timestamp('2019-02-08 19:14:35'), 8: Timestamp('2019-02-08 19:15:05'), 9: Timestamp('2019-02-08 19:15:35'), 10: Timestamp('2019-02-08 19:16:03'), 11: Timestamp('2019-02-08 19:16:34'), 12: Timestamp('2019-02-08 19:17:07'), 13: Timestamp('2019-02-08 19:17:42'), 14: Timestamp('2019-02-08 19:18:04'), 15: Timestamp('2019-02-08 19:18:34'), 16: Timestamp('2019-02-08 19:19:04'), 17: Timestamp('2019-02-08 19:19:34'), 18: Timestamp('2019-02-08 19:20:04'), 19: Timestamp('2019-02-08 19:20:34')}}
df=pd.DataFrame(dict_)
Я сдвигаю дату окончания назад на две точки, чтобы у нас было начало и конец для каждых 3 шагов. Я делаю это в группах, чтобы сохранить преемственность:
df['end_time'] = df.groupby('id')['created_at'].shift(-2)
Чтобы найти места, где у нас есть последовательный ['foo', 'bar', 'baz']
, я застегиваю df['class_name']
вместе с shift(-1)
и shift(-2)
из class_name
[[x,y,z] for x,y,z in zip(df['class_name'], df['class_name'].shift(-1), df['class_name'].shift(-2))]
[['foo', 'bar', 'foo'],
['bar', 'foo', 'baz'],
['foo', 'baz', 'bar'],
['baz', 'bar', 'foo'],
['bar', 'foo', 'bar'],
['foo', 'bar', 'baz'],
['bar', 'baz', 'foo'],
['baz', 'foo', 'bar'],
['foo', 'bar', 'baz'],
['bar', 'baz', 'foo'],
['baz', 'foo', 'bar'],
['foo', 'bar', 'foo'],
['bar', 'foo', 'bar'],
['foo', 'bar', 'baz'],
['bar', 'baz', 'baz'],
['baz', 'baz', 'bar'],
['baz', 'bar', 'bar'],
['bar', 'bar', 'foo'],
['bar', 'foo', nan],
['foo', nan, nan]]
Затем я преобразую это в массив numpy и сравню это с тем, что мы ищем.
matches = np.array([[x,y,z] for x,y,z in zip(df['class_name'], df['class_name'].shift(-1), df['class_name'].shift(-2))]) == ['foo', 'bar', 'baz']
array([[ True, True, False],
[False, False, True],
[ True, False, False],
[False, True, False],
[False, False, False],
[ True, True, True],
[False, False, False],
[False, False, False],
[ True, True, True],
[False, False, False],
[False, False, False],
[ True, True, False],
[False, False, False],
[ True, True, True],
[False, False, True],
[False, False, False],
[False, True, False],
[False, True, False],
[False, False, False],
[ True, False, False]])
Затем, чтобы получить вектор поднабора, я просто .all()
сравниваю массив. Это даст нам отправную точку
vec = [x.all() == True for x in x]
[False,
False,
False,
False,
False,
True,
False,
False,
True,
False,
False,
False,
False,
True,
False,
False,
False,
False,
False,
False]
Теперь мы создаем и проверяем
subset = df.loc[vec]
id class_name created_at end_time
5 1 foo 2019-02-08 19:13:35 2019-02-08 19:14:35
8 1 foo 2019-02-08 19:15:05 2019-02-08 19:16:03
13 2 foo 2019-02-08 19:17:42 2019-02-08 19:18:34
Поскольку нам нужны сгруппированные версии, мы можем просто groupby
и agg
получить окончательный результат.
subset.groupby('id').agg({'class_name':'count', 'created_at':'min', 'end_time':'max'})
class_name created_at end_time
id
1 2 2019-02-08 19:13:35 2019-02-08 19:16:03
2 1 2019-02-08 19:17:42 2019-02-08 19:18:34