Подсчитывать максимальное количество значений в пределах временной выборки в dataframe

#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() )