Pandas Python: вычисление номера финансовой недели

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

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

Вопрос:

У меня есть данные за пять лет, связанные с финансами. Финансовый год начинается 1 июля и заканчивается 30 июня. Я хочу рассчитать финансовую неделю каждого финансового года. Я хочу применить некоторую операцию к столбцу даты фрейма данных, чтобы, когда я пишу что-то вроде df['date].dt.week , оно должно возвращать номер финансовой недели вместо номера календарной недели. Поэтому я использовал следующий код для выполнения желаемого результата:

 df['date'] = df['date'].apply(pd.Period,freq='W')
df['date'].dt.week
  

Но это не дало желаемого результата. Может ли кто-нибудь указать мне, где я совершаю ошибку?

Ответ №1:

Я не думаю, что вы можете это сделать pandas . Тем не менее, вы можете использовать векторизованную функцию, которую я создал ниже business_week (пока я был на ней, я также создал ее для business day ). Эти функции учитывают високосный год. Эта функция начинает отсчет с первого дня месяца / дня, который вы передаете, а не с определенного дня недели. Пожалуйста, обратите внимание, что в году 52 полных недели и 1 или 2 дополнительных дня в зависимости от високосного года, поэтому 30 июня будет отображаться как неделя 53 , а 29 июня — как високосный год. Вы можете просто заменить 53 на 52, если хотите, чтобы было 52. Вы должны передать следующие параметры:

  1. Столбец, который вы хотите вывести из рабочей недели в формате datetime
  2. Начальный месяц
  3. День начала

Например: df['week'] = business_week(df['date'], 7, 1) и минимальный воспроизводимый пример ниже:

 df = pd.DataFrame({'date': 
{0: pd.Timestamp('2019-01-01 00:00:00'),
1: pd.Timestamp('2019-06-28 00:00:00'),
2: pd.Timestamp('2019-06-29 00:00:00'),
3: pd.Timestamp('2019-06-30 00:00:00'),
4: pd.Timestamp('2019-07-01 00:00:00'),
5: pd.Timestamp('2019-07-07 00:00:00'),
6: pd.Timestamp('2019-07-08 00:00:00'),
7: pd.Timestamp('2020-01-01 00:00:00'),
8: pd.Timestamp('2020-06-28 00:00:00'),
9: pd.Timestamp('2020-06-29 00:00:00'),
10: pd.Timestamp('2020-06-30 00:00:00'),
11: pd.Timestamp('2020-07-01 00:00:00'),
12: pd.Timestamp('2020-07-07 00:00:00'),
13: pd.Timestamp('2020-07-08 00:00:00')}})

def business_week(d, start_month, start_day):
    from datetime import datetime, timedelta
    y_int = d.dt.year
    y_str = y_int.astype(str)
    start_md = (datetime(2020, start_month, start_day) - timedelta(days=1)).strftime('%m-%d')
    start_ymd = pd.to_datetime(y_str   '-'   start_md)
    s = d.dt.dayofyear - start_ymd.dt.dayofyear
    m1 = s.mask(s < 1, 365 - abs(s))
    m2 = m1.mask((y_int % 4 == 0) amp; (d > start_ymd), m1 - 1)
    return np.where(y_int % 4 != 0, (m2   6) / 7, (m2   7) / 7).astype(int)


df['week'] = business_week(df['date'], 7, 1)
df
Out[1]: 
         date  week
0  2019-01-01    27
1  2019-06-28    52
2  2019-06-29    52
3  2019-06-30    53
4  2019-07-01     1
5  2019-07-07     1
6  2019-07-08     2
7  2020-01-01    27
8  2020-06-28    52
9  2020-06-29    53
10 2020-06-30    53
11 2020-07-01     1
12 2020-07-07     1
13 2020-07-08     2
  

Кроме того, если вы хотите, вы могли бы использовать аналогичный метод для возврата business_day :

 def business_day(d, start_month, start_day):
    from datetime import datetime, timedelta
    y_int = d.dt.year
    y_str = y_int.astype(str)
    start_md = (datetime(2020, start_month, start_day) - timedelta(days=1)).strftime('%m-%d')
    start_ymd = pd.to_datetime(y_str   '-'   start_md)
    s = d.dt.dayofyear - start_ymd.dt.dayofyear
    m1 = s.mask(s < 1, 365 - abs(s))
    m2 = m1.mask((y_int % 4 == 0) amp; (d <= start_ymd), m1   1)
    return m2


df['day'] = business_day(df['date'], 7, 1)
df
Out[1]: 
         date  day
0  2019-01-01  185
1  2019-06-28  363
2  2019-06-29  364
3  2019-06-30  365
4  2019-07-01    1
5  2019-07-07    7
6  2019-07-08    8
7  2020-01-01  185
8  2020-06-28  364
9  2020-06-29  365
10 2020-06-30  366
11 2020-07-01    1
12 2020-07-07    7
13 2020-07-08    8
  

Ответ №2:

Series.dt.week идентификатор устарел. Я не сталкивался с неделей финансового года. Возможно, установить начало номера недели, чтобы оно начиналось с определенной даты.

Обычная неделя года номер я бы попробовал следующее после приведения даты к дате-времени

 df.date.apply(lambda x: pd.Period(x,freq='D').week)
  

или

 df['date'].dt.strftime("%W").astype(int)
  

Комментарии:

1. Я не знал dt.week , что он устарел. Похоже, dt.weekofyear это также устарело: pandas.pydata.org/pandas-docs/stable/reference/api /… . Похоже, сейчас лучший способ dt.isocalendar().week

2. @Дэвид Эриксон, отредактирует мой ответ, чтобы удалить его. хороший друг!