Зацикливание диапазона дат + выделение дней на соответствующие месяцы

Был бы очень признателен за помощь в этом

Для следующего набора данных:


StartDate             EndDate               Days
0              2018-03-20 00:36:00       2018-05-01 00:42:00       42.004167
1              2018-05-01 00:42:00       2018-06-04 17:15:38       34.690023
2              2018-04-07 15:06:00       2018-05-09 17:01:00       32.079861
3              2018-03-21 04:36:00       2018-05-14 04:00:00       53.975000
4              2018-03-15 15:30:00       2018-05-08 08:30:00       53.708333
5              2018-05-08 08:30:00       2018-06-09 10:40:09       32.090382
6              2018-03-21 09:00:00       2018-05-16 13:40:00       56.194444
7              2018-03-31 06:00:00       2018-05-26 16:30:00       56.437500
8              2018-03-14 18:18:00       2018-04-27 01:00:00       43.279167
9              2018-04-07 15:00:00       2018-06-01 09:25:50       54.767940
10           2018-03-22 07:30:00       2018-05-20 19:00:00       59.479167

Мне удалось найти разницу между датами начала и окончания и создать новый столбец «Дни» следующим образом:

df['StartDate'] = pd.to_datetime(df['StartDate'])
df['EndDate'] = pd.to_datetime(df['EndDate'])
df['Days'] = df['EndDate'].sub(df['StartDate'], axis=0)
df['Days'] = df['Days'] / np.timedelta64(1, 'D')

Однако мне нужно это выяснить:

Для каждой строки сколько дней было в каждом месяце и в каком именно месяце они были.

Я думаю, что лучше всего было бы создать столбцы для всех возможных месяцев и таким образом привязать дни к соответствующим столбцам. Но у меня опыт работы с Excel, и, вероятно, есть более разумный способ сделать это.

Причина в том, что; правильно платить клиентам в соответствии с количеством отработанных дней между датой начала и датой окончания, а также распределять их расходы на этой основе. Полный набор данных довольно велик, поэтому скрипту придется эффективно обрабатывать широкий диапазон отклонений дат.

Спасибо

Черная мамба

Можете ли вы предоставить ожидаемый результат?

harvpan 14.05.2018 23:47
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
1
56
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Давай попробуем это.

Сначала создайте серию дней, используя pd.date_range с StartDate и EndDate для каждой записи. Затем используйте средство доступа dt datetime, чтобы получить месяц. Используйте value_counts для подсчета общего количества месяцев для каждой записи. Наконец, присоедините результаты к исходному фрейму данных. Кроме того, import calendar для получения названия месяца по сравнению с номером, используя rename со словарем, созданным из calendar.month_names.

import calendar
df.join(df.apply(lambda x: 
                 pd.Series(pd.date_range(x.StartDate,
                                         x.EndDate,
                                         freq='D')).dt.month,1)
          .apply(lambda x: 
                 x.value_counts(),1)
          .rename(columns=dict(enumerate(calendar.month_name))))

Выход:

             StartDate             EndDate       Days  March  April   May  June
0  2018-03-20 00:36:00 2018-05-01 00:42:00  42.004167   12.0   30.0   1.0   NaN
1  2018-05-01 00:42:00 2018-06-04 17:15:38  34.690023    NaN    NaN  31.0   4.0
2  2018-04-07 15:06:00 2018-05-09 17:01:00  32.079861    NaN   24.0   9.0   NaN
3  2018-03-21 04:36:00 2018-05-14 04:00:00  53.975000   11.0   30.0  13.0   NaN
4  2018-03-15 15:30:00 2018-05-08 08:30:00  53.708333   17.0   30.0   7.0   NaN
5  2018-05-08 08:30:00 2018-06-09 10:40:09  32.090382    NaN    NaN  24.0   9.0
6  2018-03-21 09:00:00 2018-05-16 13:40:00  56.194444   11.0   30.0  16.0   NaN
7  2018-03-31 06:00:00 2018-05-26 16:30:00  56.437500    1.0   30.0  26.0   NaN
8  2018-03-14 18:18:00 2018-04-27 01:00:00  43.279167   18.0   26.0   NaN   NaN
9  2018-04-07 15:00:00 2018-06-01 09:25:50  54.767940    NaN   24.0  31.0   NaN
10 2018-03-22 07:30:00 2018-05-20 19:00:00  59.479167   10.0   30.0  20.0   NaN

Я смог отфильтровать это до моего требования показывать месяц и год, изменив dt.month на dt.strftime

df.join(df.apply(lambda x: 
             pd.Series(pd.date_range(x.StartDate,
                                     x.EndDate,
                                     freq='D')).dt.strftime('%b-%y'),1)

      .apply(lambda x: 
             x.value_counts(),1)
      .rename(columns=dict(enumerate(calendar.month_abbr)))) 

Однако теперь в выходных данных столбцы перечислены в алфавитном порядке, а не с января по декабрь по году.

Любые идеи о том, как этого добиться, приветствуются.

Ответ Скотта Бостона

Во-первых, ваше указанное выше утверждение, вам не нужна часть переименования, потому что вы уже выполняете переименование при преобразовании в% m из ряда дат.

Теперь проблема возникает с этим методом, когда вы извлекаете .dt.month, который возвращает «номер месяца», который используется для сортировки. Когда вы меняете это название месяца, вы выполняете лексикографическую сортировку. Итак, давайте вернемся к номеру месяца (для сортировки) и выполним небольшую лямбда-функцию, чтобы преобразовать столбцы из номера месяца в имя.

df.join(df.apply(lambda x: 
             pd.Series(pd.date_range(x.StartDate,
                                     x.EndDate,
                                     freq='D')).dt.strftime('%m-%y'),1)
     .apply(lambda x: 
               x.value_counts(),1)
     .rename(columns=lambda x: 
                     pd.to_datetime(x, format='%m-%y').strftime('%b-%y')))

Выход:

             StartDate             EndDate       Days  Mar-18  Apr-18  May-18  Jun-18
0  2018-03-20 00:36:00 2018-05-01 00:42:00  42.004167    12.0    30.0     1.0     NaN
1  2018-05-01 00:42:00 2018-06-04 17:15:38  34.690023     NaN     NaN    31.0     4.0
2  2018-04-07 15:06:00 2018-05-09 17:01:00  32.079861     NaN    24.0     9.0     NaN
3  2018-03-21 04:36:00 2018-05-14 04:00:00  53.975000    11.0    30.0    13.0     NaN
4  2018-03-15 15:30:00 2018-05-08 08:30:00  53.708333    17.0    30.0     7.0     NaN
5  2018-05-08 08:30:00 2018-06-09 10:40:09  32.090382     NaN     NaN    24.0     9.0
6  2018-03-21 09:00:00 2018-05-16 13:40:00  56.194444    11.0    30.0    16.0     NaN
7  2018-03-31 06:00:00 2018-05-26 16:30:00  56.437500     1.0    30.0    26.0     NaN
8  2018-03-14 18:18:00 2018-04-27 01:00:00  43.279167    18.0    26.0     NaN     NaN
9  2018-04-07 15:00:00 2018-06-01 09:25:50  54.767940     NaN    24.0    31.0     NaN
10 2018-03-22 07:30:00 2018-05-20 19:00:00  59.479167    10.0    30.0    20.0     NaN

Другие вопросы по теме