Python — группировать по периодам времени с допуском

#python #python-3.x #pandas #datetime #pandas-groupby

#python #python-3.x #панды #дата и время #pandas-groupby

Вопрос:

В этом наборе данных существуют три столбца: ID (уникальная идентификация сотрудника), WorkComplete (указывает, когда вся работа была завершена) и DateDiff (количество дней с даты их начала). Я хочу сгруппировать столбец DaysDiff на основе определенных периодов времени с добавлением уровня терпимости или снисходительности. Для моих макетных данных я разделяю периоды времени на 30 дней.

 Group 0: 0-30 DateDiff (with a 30 day extra window if 'Y' is not found)
Group 1: 31-60 DateDiff (with a 30 day extra window if 'Y' is not found)
Group 2: 61-90 DateDiff (with a 30 day extra window if 'Y' is not found)
 

Я смог создать очень простой код и назначить группировки, но у меня возникли проблемы с дополнительным 30-дневным окном. Например, если сотрудник выполнил свою работу (Y) в течение указанных выше периодов времени, то он получает атрибутивную группировку. Для идентификатора 111 ниже вы можете видеть, что человек не завершил свою работу в течение первых 30 дней, поэтому я даю им дополнительные 30 дней для завершения их работы. Если они завершают свою работу, то в первом экземпляре мы видим «Y», он сгруппирован в предыдущей группировке.

 df = pd.DataFrame({'ID':[111, 111, 111, 111, 111, 111, 112, 112, 112],
                   'WorkComplete':['N', 'N', 'Y', 'N', 'N', 'N', 'N', 'Y', 'Y'],
                   'DaysDiff': [0, 29, 45, 46, 47, 88, 1, 12, 89]})
 

Ввод

 ID   WorkComplete      DaysDiff 
111  N                 0
111  N                 29
111  Y                 45
111  N                 46
111  N                 47
111  N                 88
123  N                 1 
123  Y                 12
123  Y                 89        
 

Вывод

 ID   WorkComplete      DaysDiff   Group
111  N                 0          0
111  N                 29         0
111  Y                 45         0   <---- note here the grouping is 0 to provide extra time
111  N                 46         1   <---- back to normal
111  N                 47         1   
111  N                 88         2
123  N                 1          0
123  Y                 12         0
123  Y                 89         2
 
 minQ1 = 0
highQ1 = 30
minQ2 = 31
highQ2 = 60
minQ2 = 61
highQ2 = 90

def Group_df(df):
    if (minQ1 <= df['DateDiff'] <= highQ1): return '0'
    elif (minQ1 <= df['DateDiff'] <= highQ1): return '1'
    elif (minQ2 <= df['DateDiff'] <= highQ2): return '2'

df['Group'] = df.apply(Group_df, axis = 1)
 

Проблема, с которой я сталкиваюсь, заключается в том, что я допускаю дополнительные 30 дней, если человек не завершил работу. Моя вышеупомянутая попытка является частичной попыткой решить проблему.

Ответ №1:

  1. Вы можете использовать np.select для основных условий.
  2. Затем используйте mask для конкретного условия, которое вы упомянули. s это первое расположение индекса для всех Y значений в группе. Затем я временно assign s создаю новый столбец, чтобы я мог проверять наличие строк по df.index (индексу), чтобы возвращать строки, соответствующие условию. Второе условие заключается в том, что номер группы 1 взят из предыдущей строки кода:

 df['Group'] = np.select([df['DaysDiff'].between(0,30), 
                         df['DaysDiff'].between(31,60), 
                         df['DaysDiff'].between(61,90)],
                         [0,1,2])
s = df[df['WorkComplete'] == 'Y'].groupby('ID')['DaysDiff'].transform('idxmin')
df['Group'] = df['Group'].mask((df.assign(s=s)['s'].eq(df.index)) amp; (df['Group'].eq(1)), 0)
df
Out[1]: 
    ID WorkComplete  DaysDiff  Group
0  111            N         0      0
1  111            N        29      0
2  111            Y        45      0
3  111            N        46      1
4  111            N        47      1
5  111            N        88      2
6  123            N         1      0
7  123            Y        12      0
8  123            Y        89      2