#python-3.x #pandas #dataframe
#python-3.x #pandas #фрейм данных
Вопрос:
У меня есть фрейм данных:
ID time
A1 2019-04-04 08:04:56
A11 2019-04-04 08:14:22
BB 2019-04-04 08:44:53
C5 2019-04-04 09:01:12
C1 2019-04-04 09:03:51
DD 2019-04-04 10:02:42
Итак, я хочу получить максимальное количество идентификаторов за выборку «времени» в 20 минут. Итак, здесь мы получаем 2 уникальных идентификатора (2019-04-04 08:04:56 и 2019-04-04 08:14:22), 3 уникальных идентификатора (2019-04-04 08:44:53, 2019-04-04 09:01:12, 2019-04-04 09:03:51) и 1 уникальный идентификатор (2019-04-04 10:02:42). max(2,3,1)=3. Итак, ответ равен 3. Как я мог это получить? Желаемый результат — получить его как dataframe:
time ID_num
2019-04-04 08:04:56 2
2019-04-04 08:44:53 3
2019-04-04 10:02:42 1
Я могу определить свой массив выборки следующим образом:
numpy.arange(8, 20, 0.3)
то есть переход с 8 утра до 8 вечера с шагом 0,3 часа
Комментарии:
1. Привет, вы действительно хотите получить 3 строки в качестве выходных данных или вас просто интересует максимум, который равен 3? Если вас интересуют 3 строки, не могли бы вы описать, как вы получаете эти три строки? вы хотите получить максимальное количество выборок за произвольный период в 20 минут или периоды фиксированы, как 8:00-8:20, 8:20-8:40, …?
2. Вы могли бы улучшить ясность вопроса, указав, являются ли ваши «приращения» 20-минутными разделами времени (не перекрывающимися), так что определяет начало; фиксированный момент времени или первая выборка. если вместо этого это скользящее окно, что представляет собой наименьшее заданное изменение шага во времени (второе) или следующий заданный образец… это игра в угадайку, пока вы не получите конкретные
Ответ №1:
#Lets define some groups based on time differences
s=((df.time.diff(1) / np.timedelta64(1, 'm') >=20)|(df.time.diff(1).isna())).cumsum()
#Now let us groupby as we pick the first occurrence of time in a group and find how many ids are in each group using .groupby() and agg()
df.groupby(s).agg(ftime=('time','first'), idcount=('ID','count'))
ftime idcount
time
1 2019-04-04 08:04:56 2
2 2019-04-04 08:44:53 3
3 2019-04-04 10:02:42 1
Ответ №2:
Вы можете перебирать «времена», смещать каждое на 20 минут и нарезать фрейм данных, чтобы проверить длину подмножества. Затем получите максимальное значение каждого из этих подмножеств.
df= df.set_index("time")
offset = pd.Timedelta("20min")
lengths = []
for start_time in df.index:
stop_time = start_time offset
chunk_length = df.loc[start_time:stop_time].shape[0]
record = (start_time, chunk_length)
lengths.append(record)
max(lengths, key=lambda item: item[1])
(Timestamp('2019-04-04 08:44:53'), 3)
Вывод указывает, что временная метка 2019-04-04 08:44:53 содержала 3 записи в dataframe, которые существовали между этой временной меткой и 20 минутами после этой временной метки. Таким образом, вы получаете максимальную длину последовательности, а также временную метку, с которой начался последовательный запуск.
Ответ №3:
Если вас интересует 20-минутный период с наибольшим количеством выборок в нем, где 20-минутный период можно свободно определить, вы можете сделать это следующим образом:
from datetime import timedelta
df= pd.DataFrame(
dict(
ID=['A1', 'A11', 'BB', 'C5', 'C1', 'DD'],
time=pd.to_datetime(['2019-04-04 08:04:56', '2019-04-04 08:14:22', '2019-04-04 08:44:53', '2019-04-04 09:01:12', '2019-04-04 09:03:51', '2019-04-04 10:02:42'])
)
)
df.dtypes
previous_time= df['time'].shift(1)
previous_time= df['time'].where(previous_time.isnull(), previous_time)
df['fake']= (df['time'] > previous_time timedelta(minutes=20)).cumsum()
df2= df.merge(df, on='fake', suffixes=('', '_next'))
df2.dtypes
indexer= df2['time_next'].between(df2['time'], df2['time'] timedelta(minutes=20))
result= df2[indexer].groupby('time').agg(
count=('time', 'count'),
period_start=('time_next', 'min'),
last_sample_in_period=('time_next', 'max')
)
result.sort_values('count', ascending=False).iloc[0]
Последняя строка выводит:
count 3
period_start 2019-04-04 08:44:53
last_sample_in_period 2019-04-04 09:03:51
Name: 2019-04-04 08:44:53, dtype: object
То же самое также может быть достигнуто с помощью этого кода:
from datetime import timedelta
df= pd.DataFrame(
dict(
ID=['A1', 'A11', 'BB', 'C5', 'C1', 'DD'],
time=pd.to_datetime(['2019-04-04 08:04:56', '2019-04-04 08:14:22', '2019-04-04 08:44:53', '2019-04-04 09:01:12', '2019-04-04 09:03:51', '2019-04-04 10:02:42'])
)
)
df['period_end']= df['time'] timedelta(minutes=20)
df['count']= 1
time_series=df['time']
continue_iteration= True
period_end_series= df['period_end']
while continue_iteration:
time_series= time_series.shift(-1)
in_period= (~time_series.isnull()) amp; (time_series <= period_end_series)
df['count'] = in_period
continue_iteration= in_period.any()
df.sort_values(by='count', ascending=False, inplace=True)
df.iloc[0]
Разница между двумя способами заключается в том, что первый создает подмножество перекрестного произведения путем объединения фрейма данных с самим собой. Он подходит для небольших наборов данных и наборов данных с большим количеством пробелов продолжительностью более 20 минут и не таким большим количеством максимальных выборок в течение 20 минут.
Второму не нужно присоединять dataframe к самому себе. Он просто сдвигает столбец времени до тех пор, пока есть хотя бы одно совпадение с периодом. Таким образом, if m
обозначает максимальное количество выборок за произвольный 20-минутный период в dataframe, тогда цикл завершается точно после m
операций сдвига (из-за критерия остановки in_period.any()
)