#python #pandas #datetime #time-series #google-calendar-api
#python #pandas #дата и время #временные ряды #google-calendar-api
Вопрос:
Я создаю программу на Python, используя библиотеки pandas и datetime, которые будут рассчитывать оплату за мою случайную работу каждую неделю, поэтому я могу перекрестно ссылаться на свою банковскую выписку вместо просмотра платежных ведомостей. Данные, которые я анализирую, взяты из API календаря Google, который синхронизируется с моим рабочим графиком. Он печатает события в этом конкретном календаре в файл csv в этом формате:
Начать | Конец | Название | Часы | |
---|---|---|---|---|
0 | 02.12.2020 07:00 | 02.12.2020 16:00 | Сдвиг | 9.0 |
1 | 04.12.2020 18:00 | 04.12.2020 21:00 | Сдвиг | 3.0 |
2 | 05.12.2020 07:00 | 05.12.2020 12:00 | Сдвиг | 5.0 |
3 | 06.12.2020 09:00 | 06.12.2020 18:00 | Сдвиг | 9.0 |
4 | 07.12.2020 19:00 | 07.12.2020 23:00 | Сдвиг | 4.0 |
5 | 08.12.2020 19:00 | 08.12.2020 23:00 | Сдвиг | 4.0 |
6 | 09.12.2020 10:00 | 09.12.2020 15:00 | Сдвиг | 5.0 |
Поскольку я работаю на этой работе, я должен учитывать несколько вещей, таких как ставки штрафов (базовый уровень, после 6 вечера с понедельника по пятницу, субботу и воскресенье у всех разные ставки). Мне интересно, могу ли я проанализировать этот csv с помощью datetime и подсчитать, сколько часов до 6 вечера и сколько после 6 вечера. Итак, используя это в качестве примера, результат будет выглядеть так:
Начать | Конец | Название | Часы | |
---|---|---|---|---|
1 | 04.12.2020 15:00 | 04.12.2020 21:00 | Сдвиг | 6.0 |
Начать | Конец | Название | Общее количество часов | За несколько часов до 3 часов дня | Часов после 3 часов дня | |
---|---|---|---|---|---|---|
1 | 04.12.2020 15:00 | 04.12.2020 21:00 | Сдвиг | 6.0 | 3.0 | 3.0 |
Я могу использовать это, чтобы получить день недели, но я просто не уверен, как анализировать определенные промежутки времени на предмет штрафных ставок:
df['day_of_week'] = df['Start'].dt.day_name()
Я ценю любую помощь в Python или даже в других языках / методах кодирования, к которым это может быть применено 🙂
Редактировать: вот как выглядит мой dataframe на данный момент
Начать | Конец | Название | Часы | day_of_week | Платить | неделя_о_году | |
---|---|---|---|---|---|---|---|
0 | 2020-12-02 07:00:00 | 2020-12-02 16:00:00 | Сдвиг | 9.0 | Среда | 337.30 | 49 |
ОТРЕДАКТИРУЙТЕ в ответ на комментарий Дэвида Эриксона.
значение | переменная | bool | |
---|---|---|---|
0 | 2020-12-02 07:00:00 | Начать | False |
1 | 2020-12-02 08:00:00 | Начать | False |
2 | 2020-12-02 09:00:00 | Начать | False |
3 | 2020-12-02 10:00:00 | Начать | False |
4 | 2020-12-02 11:00:00 | Начать | False |
5 | 2020-12-02 12:00:00 | Начать | False |
6 | 2020-12-02 13:00:00 | Начать | False |
7 | 2020-12-02 14:00:00 | Начать | False |
8 | 2020-12-02 15:00:00 | Начать | False |
9 | 2020-12-02 16:00:00 | Конец | False |
10 | 2020-12-04 18:00:00 | Start | False |
11 | 2020-12-04 19:00:00 | Start | True |
12 | 2020-12-04 20:00:00 | Start | True |
13 | 2020-12-04 21:00:00 | Конец | Верно |
14 | 2020-12-05 07:00:00 | Начать | False |
15 | 2020-12-05 08:00:00 | Начать | False |
16 | 2020-12-05 09:00:00 | Начать | False |
17 | 2020-12-05 10:00:00 | Начать | False |
18 | 2020-12-05 11:00:00 | Начать | False |
19 | 2020-12-05 12:00:00 | Конец | False |
20 | 2020-12-06 09:00:00 | Начать | False |
21 | 2020-12-06 10:00:00 | Начать | False |
22 | 2020-12-06 11:00:00 | Начать | False |
23 | 2020-12-06 12:00:00 | Начать | False |
24 | 2020-12-06 13:00:00 | Начать | False |
25 | 2020-12-06 14:00:00 | Начать | False |
26 | 2020-12-06 15:00:00 | Начать | False |
27 | 2020-12-06 6:00:00 | Начать | False |
28 | 2020-12-06 17:00:00 | Начать | False |
29 | 2020-12-06 18:00:00 | Конец | False |
30 | 2020-12-07 19:00:00 | Начать | False |
31 | 2020-12-07 20:00:00 | Начать | Верно |
32 | 2020-12-07 21:00:00 | Начать | Верно |
33 | 2020-12-07 22:00:00 | Начать | Верно |
34 | 2020-12-07 23:00:00 | Конец | Верно |
35 | 2020-12-08 19:00:00 | Начать | False |
36 | 2020-12-08 20:00:00 | Начать | Верно |
37 | 2020-12-08 21:00:00 | Начать | Верно |
38 | 2020-12-08 22:00:00 | Начать | Верно |
39 | 2020-12-08 23:00:00 | Конец | Верно |
40 | 2020-12-09 10:00:00 | Начать | False |
41 | 2020-12-09 11:00:00 | Начать | False |
42 | 2020-12-09 12:00:00 | Начать | False |
43 | 2020-12-09 13:00:00 | Начать | False |
44 | 2020-12-09 14:00:00 | Начать | False |
45 | 2020-12-09 15:00:00 | Конец | False |
46 | 2020-12-11 19:00:00 | Начать | False |
47 | 2020-12-11 20:00:00 | Начать | Верно |
48 | 2020-12-11 21:00:00 | Начать | Верно |
49 | 2020-12-11 22:00:00 | Начать | Верно |
Ответ №1:
ОБНОВЛЕНИЕ: (2020-12-19)
Я просто отфильтровал Start
строки, поскольку вы были правы, что вычисляется дополнительная строка. Кроме того, я перешел dayfirst=True
pd.to_datetime()
к правильному преобразованию даты. Я также очистил вывод с помощью некоторых дополнительных столбцов.
higher_pay = 40
lower_pay = 30
df['Start'], df['End'] = pd.to_datetime(df['Start'], dayfirst=True), pd.to_datetime(df['End'], dayfirst=True)
start = df['Start']
df1 = df[['Start', 'End']].melt(value_name='Date').set_index('Date')
s = df1.groupby('variable').cumcount()
df1 = df1.groupby(s, group_keys=False).resample('1H').asfreq().join(s.rename('Shift').to_frame()).ffill().reset_index()
df1 = df1[~df1['Date'].isin(start)]
df1['Day'] = df1['Date'].dt.day_name()
df1['Week'] = df1['Date'].dt.isocalendar().week
m = (df1['Date'].dt.hour > 18) | (df1['Day'].isin(['Saturday', 'Sunday']))
df1['Higher Pay Hours'] = np.where(m, 1, 0)
df1['Lower Pay Hours'] = np.where(m, 0, 1)
df1['Pay'] = np.where(m, higher_pay, lower_pay)
df1 = df1.groupby(['Shift', 'Day', 'Week']).sum().reset_index()
df2 = df.merge(df1, how='left', left_index=True, right_on='Shift').drop('Shift', axis=1)
df2
Out[1]:
Start End Title Hours Day Week
0 2020-12-02 07:00:00 2020-12-02 16:00:00 Shift 9.0 Wednesday 49
1 2020-12-04 18:00:00 2020-12-04 21:00:00 Shift 3.0 Friday 49
2 2020-12-05 07:00:00 2020-12-05 12:00:00 Shift 5.0 Saturday 49
3 2020-12-06 09:00:00 2020-12-06 18:00:00 Shift 9.0 Sunday 49
4 2020-12-07 19:00:00 2020-12-07 23:00:00 Shift 4.0 Monday 50
5 2020-12-08 19:00:00 2020-12-08 23:00:00 Shift 4.0 Tuesday 50
6 2020-12-09 10:00:00 2020-12-09 15:00:00 Shift 5.0 Wednesday 50
Higher Pay Hours Lower Pay Hours Pay
0 0 9 270
1 3 0 120
2 5 0 200
3 9 0 360
4 4 0 160
5 4 0 160
6 0 5 150
Вероятно, есть более краткие способы сделать это, но я подумал, что повторная выборка фрейма данных и затем подсчет часов были бы чистым подходом. Вы можете melt
использовать фрейм данных, чтобы иметь Start
и End
в том же столбце, и заполнить промежутки времени, resample
убедившись groupby
, что это значения ‘Start’ и ‘End’, которые изначально были в одной строке. Самый простой способ выяснить, какие строки изначально были вместе, — это получить совокупный подсчет cumcount
значений в новом фрейме данных, сгруппированных по ‘Start’ и ‘End’. Я покажу вам, как это работает позже в ответе.
Полный код:
df['Start'], df['End'] = pd.to_datetime(df['Start']), pd.to_datetime(df['End'])
df = df[['Start', 'End']].melt().set_index('value')
df = df.groupby(df.groupby('variable').cumcount(), group_keys=False).resample('1H').asfreq().ffill().reset_index()
m = (df['value'].dt.hour > 18) | (df['value'].dt.day_name().isin(['Saturday', 'Sunday']))
print('Normal Rate No. of Hours', df[m].shape[0])
print('Higher Rate No. of Hours', df[~m].shape[0])
Normal Rate No. of Hours 20
Higher Rate No. of Hours 26
Добавим еще несколько деталей…
Шаг 1: Расплавьте dataframe: вам нужны только два столбца ‘Start’ и ‘End’, чтобы получить желаемый результат
df = df[['Start', 'End']].melt().set_index('value')
df
Out[1]:
variable
value
2020-02-12 07:00:00 Start
2020-04-12 18:00:00 Start
2020-05-12 07:00:00 Start
2020-06-12 09:00:00 Start
2020-07-12 19:00:00 Start
2020-08-12 19:00:00 Start
2020-09-12 10:00:00 Start
2020-02-12 16:00:00 End
2020-04-12 21:00:00 End
2020-05-12 12:00:00 End
2020-06-12 18:00:00 End
2020-07-12 23:00:00 End
2020-08-12 23:00:00 End
2020-09-12 15:00:00 End
Шаг 2: Создайте группу в рамках подготовки к повторной выборке: * Как вы можете видеть, группы 0-6 выстраиваются друг с другом, представляя ‘
Начало» и «Конец», как они были вместе ранее
df.groupby('variable').cumcount()
Out[2]:
value
2020-02-12 07:00:00 0
2020-04-12 18:00:00 1
2020-05-12 07:00:00 2
2020-06-12 09:00:00 3
2020-07-12 19:00:00 4
2020-08-12 19:00:00 5
2020-09-12 10:00:00 6
2020-02-12 16:00:00 0
2020-04-12 21:00:00 1
2020-05-12 12:00:00 2
2020-06-12 18:00:00 3
2020-07-12 23:00:00 4
2020-08-12 23:00:00 5
2020-09-12 15:00:00 6
Шаг 3: Повторная выборка данных для каждой группы по часам, чтобы заполнить пробелы для каждой группы:
df.groupby(df.groupby('variable').cumcount(), group_keys=False).resample('1H').asfreq().ffill().reset_index()
Out[3]:
value variable
0 2020-02-12 07:00:00 Start
1 2020-02-12 08:00:00 Start
2 2020-02-12 09:00:00 Start
3 2020-02-12 10:00:00 Start
4 2020-02-12 11:00:00 Start
5 2020-02-12 12:00:00 Start
6 2020-02-12 13:00:00 Start
7 2020-02-12 14:00:00 Start
8 2020-02-12 15:00:00 Start
9 2020-02-12 16:00:00 End
10 2020-04-12 18:00:00 Start
11 2020-04-12 19:00:00 Start
12 2020-04-12 20:00:00 Start
13 2020-04-12 21:00:00 End
14 2020-05-12 07:00:00 Start
15 2020-05-12 08:00:00 Start
16 2020-05-12 09:00:00 Start
17 2020-05-12 10:00:00 Start
18 2020-05-12 11:00:00 Start
19 2020-05-12 12:00:00 End
20 2020-06-12 09:00:00 Start
21 2020-06-12 10:00:00 Start
22 2020-06-12 11:00:00 Start
23 2020-06-12 12:00:00 Start
24 2020-06-12 13:00:00 Start
25 2020-06-12 14:00:00 Start
26 2020-06-12 15:00:00 Start
27 2020-06-12 16:00:00 Start
28 2020-06-12 17:00:00 Start
29 2020-06-12 18:00:00 End
30 2020-07-12 19:00:00 Start
31 2020-07-12 20:00:00 Start
32 2020-07-12 21:00:00 Start
33 2020-07-12 22:00:00 Start
34 2020-07-12 23:00:00 End
35 2020-08-12 19:00:00 Start
36 2020-08-12 20:00:00 Start
37 2020-08-12 21:00:00 Start
38 2020-08-12 22:00:00 Start
39 2020-08-12 23:00:00 End
40 2020-09-12 10:00:00 Start
41 2020-09-12 11:00:00 Start
42 2020-09-12 12:00:00 Start
43 2020-09-12 13:00:00 Start
44 2020-09-12 14:00:00 Start
45 2020-09-12 15:00:00 End
Шаг 4 — Оттуда вы можете вычислить логический ряд, который я вызвал m
: * Истинные значения представляют условия, выполненные для «Более высокой скорости».
m = (df['value'].dt.hour > 18) | (df['value'].dt.day_name().isin(['Saturday', 'Sunday']))
m
Out[4]:
0 False
1 False
2 False
3 False
4 False
5 False
6 False
7 False
8 False
9 False
10 True
11 True
12 True
13 True
14 False
15 False
16 False
17 False
18 False
19 False
20 False
21 False
22 False
23 False
24 False
25 False
26 False
27 False
28 False
29 False
30 True
31 True
32 True
33 True
34 True
35 True
36 True
37 True
38 True
39 True
40 True
41 True
42 True
43 True
44 True
45 True
Шаг 5: Отфильтруйте фрейм данных по True
или False
, чтобы подсчитать общее количество часов для значений нормальной скорости и более высокой скорости и печати.
print('Normal Rate No. of Hours', df[m].shape[0])
print('Higher Rate No. of Hours', df[~m].shape[0])
Normal Rate No. of Hours 20
Higher Rate No. of Hours 26
Комментарии:
1. Большое спасибо за публикацию этого! Определенно поставьте меня на правильный путь с помощью функции melt. Единственное, что я пытаюсь выяснить сейчас, это то, что если начало смены прошло 6 вечера (например, 7 вечера), то это будет засчитываться как час (вместо того, чтобы работать до 8 вечера, засчитываемого как час). Итак, я пытаюсь выполнить итерацию по столбцу ‘variable’, и если строка перед i ‘End’, измените на False, потому что это будет означать, что это начало сдвига. Дайте мне знать, если у вас есть какие-либо идеи в то же время. Ценю помощь Дэвида.
2. @tyleroki В вашем вопросе, можете ли вы поделиться ожидаемым результатом в виде
True
False
логического ряда / ‘, который я показываю на шаге 4? По сути, можете ли вы скопировать вывод шага в свой вопрос и изменить True на False или False на True для всего, что не ожидается?3. извините за мой поздний ответ. Я обновил свой вопрос, чтобы показать, что мне нужно. По сути, всякий раз, когда «Start» находится под «End» в столбце переменных, тогда bool должно быть False . Это связано с тем, что смена начинается, поэтому время не прошло (например, если смена начинается в 7 вечера, это не следует считать истинным)