2016-05-07 1 views
3

У меня есть dataframe, который выглядит следующим образом:GroupBy - Datetime дифференциал() комбинирование дополнительных критериев

In [265]: df_2 
Out[265]: 
     A   ID   DATETIME ORDER_FAILED 
0 B-028 b76cd912ff 2014-10-08 13:43:27   True 
1 B-054 4a57ed0b02 2014-10-08 14:26:19  False 
2 B-076 1a682034f8 2014-10-08 14:29:01  False 
3 B-023 b76cd912ff 2014-10-08 18:39:34   True 
4 B-024 f88g8d7sds 2014-10-08 18:40:18   True 
5 B-025 b76cd912ff 2014-10-08 18:42:02   True 
6 B-026 b76cd912ff 2014-10-08 18:42:41  False 
7 B-033 b76cd912ff 2014-10-08 18:44:30   True 
8 B-032 b76cd912ff 2014-10-08 18:46:00   True 
9 B-037 b76cd912ff 2014-10-08 18:52:15   True 
10 B-046 db959faf02 2014-10-08 18:59:59  False 
11 B-053 b76cd912ff 2014-10-08 19:17:48   True 
12 B-065 b76cd912ff 2014-10-08 19:21:38  False 

мне нужно сбросить все повторить «неудачные заказы» - за исключением последнего - в сбойных последовательностях заказов ,

«последовательность» A представляет собой серию неудачных заказов, которые отвечают следующим критериям:

  1. Размещено тем же пользователем - идентифицируются 'ID'
  2. Имеет 'ORDER_FAILED' == True
  3. Нет последовательных заказов не более чем в 5 минутах друг от друга.

Я надеялся, что это можно сделать так:

In [298]: df_2[df_2.ORDER_FAILED == True].sort_values(by='DATETIME').groupby('ID')['DATETIME'].diff().dt.total_seconds() 
Out[298]: 
0   NaN 
3  17767.0 
4   NaN 
5  148.0 
7  148.0 
8  90.0 
9  375.0 
11  1533.0 
Name: DATETIME, dtype: float64 

, а затем использовать pd.join, чтобы достичь этого:

In [302]: df_2 = df_2.join(df_tmp); df_2 
Out[302]: 
     A   ID   DATETIME ORDER_FAILED  diff 
0 B-028 b76cd912ff 2014-10-08 13:43:27   True  NaN 
1 B-054 4a57ed0b02 2014-10-08 14:26:19  False  NaN 
2 B-076 1a682034f8 2014-10-08 14:29:01  False  NaN 
3 B-023 b76cd912ff 2014-10-08 18:39:34   True 17767.0 
4 B-024 f88g8d7sds 2014-10-08 18:40:18   True  NaN 
5 B-025 b76cd912ff 2014-10-08 18:42:02   True 148.0 
6 B-026 b76cd912ff 2014-10-08 18:42:41  False  NaN 
7 B-033 b76cd912ff 2014-10-08 18:44:30   True 148.0 
8 B-032 b76cd912ff 2014-10-08 18:46:00   True  90.0 
9 B-037 b76cd912ff 2014-10-08 18:52:15   True 375.0 
10 B-046 db959faf02 2014-10-08 18:59:59  False  NaN 
11 B-053 b76cd912ff 2014-10-08 19:17:48   True 1533.0 
12 B-065 b76cd912ff 2014-10-08 19:21:38  False  NaN 

Однако, к сожалению, это не правильно. Заказ 7 должен иметь diff == NaN, так как это первый заказ в серии неудачных заказов, следующих за успешным заказом этого пользователя (то есть заказ 6).

Я понимаю, как я вычисляю diff выше, неисправен, мне не удалось найти способ «сбросить» счетчик после каждого успешного заказа.

Нужный правильный результат был бы:

In [303]: df_2 
Out[303]: 
     A   ID   DATETIME ORDER_FAILED  diff 
0 B-028 b76cd912ff 2014-10-08 13:43:27   True  NaN 
1 B-054 4a57ed0b02 2014-10-08 14:26:19  False  NaN 
2 B-076 1a682034f8 2014-10-08 14:29:01  False  NaN 
3 B-023 b76cd912ff 2014-10-08 18:39:34   True 17767.0 
4 B-024 f88g8d7sds 2014-10-08 18:40:18   True  NaN 
5 B-025 b76cd912ff 2014-10-08 18:42:02   True 148.0 
6 B-026 b76cd912ff 2014-10-08 18:42:41  False  NaN ## <- successful order 
7 B-033 b76cd912ff 2014-10-08 18:44:30   True  NaN ## <- since this is the first failed order in this sequence of failed orders 
8 B-032 b76cd912ff 2014-10-08 18:46:00   True  90.0 
9 B-037 b76cd912ff 2014-10-08 18:52:15   True 375.0 
10 B-046 db959faf02 2014-10-08 18:59:59  False  NaN 
11 B-053 b76cd912ff 2014-10-08 19:17:48   True 1533.0 
12 B-065 b76cd912ff 2014-10-08 19:21:38  False  NaN 

После этого момента, я бы просто отметить заказы, где diff > 300 с чем-то вроде этого:

>> df_2.ix[df_2['diff'] > 300, 'remove_flag'] = 1 
>> df_2.groupby('ID')['remove_flag'].shift(-1) ## <- adjust flag to mark the previous order in the sequence 
>> df_2 = df_2[df_2.remove_flag != 1] 

, что означает, что, в конечном счете, заказы, которые следует сохранить или отбросить, приведены ниже:

>> df_2 
     A   ID   DATETIME ORDER_FAILED  diff 
0 B-028 b76cd912ff 2014-10-08 13:43:27   True  NaN ## STAYS - Failed, but gap to next failed by same user is greater than 5 minutes 
1 B-054 4a57ed0b02 2014-10-08 14:26:19  False  NaN ## STAYS - successful order 
2 B-076 1a682034f8 2014-10-08 14:29:01  False  NaN ## STAYS - successful order 
3 B-023 b76cd912ff 2014-10-08 18:39:34   True 17767.0 ## DISCARD - The next failed order by the same user is only 148 seconds away (less than 5 minutes) 
4 B-024 f88g8d7sds 2014-10-08 18:40:18   True  NaN ## STAYS - successful order 
5 B-025 b76cd912ff 2014-10-08 18:42:02   True 148.0 ## STAYS - last in this sequence of failed orders by this user 
6 B-026 b76cd912ff 2014-10-08 18:42:41  False  NaN ## STAYS - successful order 
7 B-033 b76cd912ff 2014-10-08 18:44:30   True  NaN ## DISCARD - The next failed order by the same user is only 90 seconds away (less than 5 minutes) 
8 B-032 b76cd912ff 2014-10-08 18:46:00   True  90.0 ## STAYS - next failed order by the same user is more than 5 minutes away 
9 B-037 b76cd912ff 2014-10-08 18:52:15   True 375.0 ## STAYS - More than 5 minutes away from previous failed order by the same user 
10 B-046 db959faf02 2014-10-08 18:59:59  False  NaN ## STAYS - Successful order 
11 B-053 b76cd912ff 2014-10-08 19:17:48   True 1533.0 ## STAYS - too long since last failed order by this same user 
12 B-065 b76cd912ff 2014-10-08 19:21:38  False  NaN ## STAYS - Successful order 

Любая помощь была бы принята с благодарностью, спасибо!

+0

Почему заказ 3 не NaN, так как это также первый неудачный заказ после успешного заказа? – Evert

+0

«Мне нужно сбросить все повторяющиеся заказы» - за исключением последнего »: означает ли это, что из каждой последовательности неудачных заказов вы хотите сохранить последнюю строку, или это означает, что из всех последовательностей неудачные заказы, вы хотите сохранить (полную) последнюю последовательность? – Evert

+0

Заказ '3' не является' NaN', потому что предыдущий ** тем же пользователем ** (заказ '0') действительно был неудачным заказом. – Thanos

ответ

1

Начну с сортировкой по ID и DATETIME (по возрастанию):

df1 = df.sort_values(by = ['ID','DATETIME']) 

Теперь, если я правильно понимаю, нам нужно удалить все заказы, которые удовлетворяют конъюнкцию следующих условий (по «рядом "Я понимаю "в следующей строке"):

  • заказ не удалось

  • следующий заказ не удалось

  • разница во времени между порядком и следующей составляет не более 300 с

  • (и дополнительно) идентификатор такой же, как на следующий ID (в противном случае он был самый последний заказ)

Моя идея проста: добавить соответствующие столбцы, чтобы каждая строка содержала все данные, необходимые для оценки этих условий.

Это один добавляет «следующий идентификатор» и поля «следующего порядка»:

df1[['Next_ID','Next_ORDER_FAILED']] = df1[['ID','ORDER_FAILED']].shift(-1) 

и это один отвечает за разницы во времени в следующем порядке:

df1['diff'] = -df1['DATETIME'].diff(-1).dt.total_seconds() 

(соответствующие различия с периодом = -1 будут отрицательными, поэтому знак минус).

Я считаю, что остальное уже довольно просто.

Update: Кстати, мы можем создать Ьоо маску, даже без добавления новых столбцов в кадре данных:

mask = (df1['ORDER_FAILED'] == True) and (df1['ORDER_FAILED'].shift(-1) == True) and ... 

UPDATE

Там нет реальной необходимости порядок по ID и общее решение на самом деле были бы более чистыми, если бы правильно использовалось groupby(). Вот как это было сделано в конце, после предложений выше.

In [478]: df_3 
Out[478]: 
     A   ID   DATETIME ORDER_FAILED 
0 B-028 b76cd912ff 2014-10-08 13:43:27   True 
1 B-054 4a57ed0b02 2014-10-08 14:26:19  False 
2 B-076 1a682034f8 2014-10-08 14:29:01  False 
3 B-023 b76cd912ff 2014-10-08 18:39:34   True 
4 B-024 f88g8d7sds 2014-10-08 18:40:18   True 
5 B-025 b76cd912ff 2014-10-08 18:42:02   True 
6 B-026 b76cd912ff 2014-10-08 18:42:41  False 
7 B-033 b76cd912ff 2014-10-08 18:44:30   True 
8 B-032 b76cd912ff 2014-10-08 18:46:00   True 
9 B-037 b76cd912ff 2014-10-08 18:52:15   True 
10 B-046 db959faf02 2014-10-08 18:59:59  False 
11 B-053 b76cd912ff 2014-10-08 19:17:48   True 
12 B-065 b76cd912ff 2014-10-08 19:21:38  False 

In [479]: df_3['NEXT_FAILED'] = df_3.sort_values(by='DATETIME').groupby('ID')['ORDER_FAILED'].shift(-1) 

In [480]: df_3['SECONDS_TO_NEXT_ORDER'] = -df_3.sort_values(by='DATETIME').groupby('ID')['DATETIME'].diff(-1).dt.total_seconds() 

In [481]: condition = (df_3.NEXT_FAILED == True) & (df_3.ORDER_FAILED == True) & (df_3.SECONDS_TO_NEXT_ORDER <= 300) 

In [482]: df_3[~condition].drop(['NEXT_FAILED','SECONDS_TO_NEXT_ORDER'], axis=1) 
Out[482]: 
     A   ID   DATETIME ORDER_FAILED 
0 B-028 b76cd912ff 2014-10-08 13:43:27   True 
1 B-054 4a57ed0b02 2014-10-08 14:26:19  False 
2 B-076 1a682034f8 2014-10-08 14:29:01  False 
4 B-024 f88g8d7sds 2014-10-08 18:40:18   True 
5 B-025 b76cd912ff 2014-10-08 18:42:02   True 
6 B-026 b76cd912ff 2014-10-08 18:42:41  False 
8 B-032 b76cd912ff 2014-10-08 18:46:00   True 
9 B-037 b76cd912ff 2014-10-08 18:52:15   True 
10 B-046 db959faf02 2014-10-08 18:59:59  False 
11 B-053 b76cd912ff 2014-10-08 19:17:48   True 
12 B-065 b76cd912ff 2014-10-08 19:21:38  False 

Правильные заказы - согласно описанию ОП - действительно отброшены!

+0

Это был набор хороших предложений, спасибо за время и силы. Я делаю предложение для редактирования для окончательного решения. Еще раз спасибо! – Thanos

+0

@Thanos Добро пожаловать. Рад, что смог помочь. – ptrj

Смежные вопросы