2016-05-07 6 views
1

Используя pandas, вы можете создать ежемесячную серию дат.Создание ежемесячных временных рядов с произвольными датами начала работы

import pandas as pd 

pd.date_range('2012-04-23', '2013-01-23', freq='BM') 

DatetimeIndex(['2012-04-30', '2012-05-31', '2012-06-29', '2012-07-31', 
       '2012-08-31', '2012-09-28', '2012-10-31', '2012-11-30', 
       '2012-12-31'], 
       dtype='datetime64[ns]', freq='BM') 

Обратите внимание, что даты в DatetimeIndex в месяц заканчивается. Я знаю, что следует подумать, что я выбрал freq='BM', но я не считаю, что у меня был выбор, который бы выполнил мою цель.

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

Я хотел бы видеть вместо этого:

DatetimeIndex(['2012-04-23', '2012-05-23', '2012-06-23', '2012-07-23', 
       '2012-08-23', '2012-09-23', '2012-10-23', '2012-11-23', 
       '2012-12-23'], 
       dtype='datetime64[ns]', freq=None) 

или другой более сложный пример, может быть, чтобы получить месяцев от «2012-01-30» до «2012-04-30». Я бы ожидал увидеть:

DatetimeIndex(['2012-01-30', '2012-02-29', '2012-03-30', '2012-04-30'], 
       dtype='datetime64[ns]', freq=None) 
+0

Я не понимаю. То, что вы только что попросили, похоже, не то, что вы хотели бы видеть, т. Е. Верхний DatetimeIndex * - * последний рабочий день месяца, возвращающийся во времени. – Alexander

+0

@Alexander Это результат того, что я не знаю, как поставить аргумент 'freq'. Я не хотел выпускать ежедневно, что, по моему мнению, является дефолтом. – piRSquared

+0

Итак, сегодня 6 мая (уже?!? ...). 6 марта - воскресенье. Что бы вы хотели посмотреть? 4 марта (пятница)? – Alexander

ответ

1

Вы можете искать что-то вроде этого:

from pandas.tseries.offsets import Day, BDay 
pd.date_range(start = '2012-01-01', periods = 6, freq = 'MS') + Day(22) + BDay(0) 
Out[12]: 
DatetimeIndex(['2012-01-23', '2012-02-23', '2012-03-23', '2012-04-23', 
       '2012-05-23', '2012-06-25'], 
       dtype='datetime64[ns]', freq=None) 

Day(22) добавляет смещение 22 дней и BDay отвечает за рабочий день смещения (BDay(0) занимает ближайший рабочий день).

Это немного сложнее с датами, начинающимися с 30-го. Поэтому мне пришлось написать функцию для этого. (Для ясности кода не позволяет обычай freq параметр.)

def my_business_date_range(day, **kwargs): 
    assert(isinstance(day, int) & (day > 0) & (day < 32)) 
    rng0 = pd.date_range(freq = 'MS', **kwargs) 
    rng1 = rng0 + pd.tseries.offsets.Day(day-1) + pd.tseries.offsets.BDay(0) 
    # Correcting overflows: 
    overflow_idx, = np.nonzero(rng0.month != rng1.month) 
    if overflow_idx.size > 0: 
     # rng1 is not mutable 
     tmp = rng1.tolist()   
     bme = pd.tseries.offsets.BusinessMonthEnd(-1) 
     for i in overflow_idx: 
      tmp[i] = bme(rng1[i]) 
     rng1 = pd.DatetimeIndex(tmp) 
    return rng1 

my_business_date_range(30, start= '2012-01-01', periods = 6) 
Out[13]: 
DatetimeIndex(['2012-01-30', '2012-02-29', '2012-03-30', '2012-04-30', 
       '2012-05-30', '2012-06-29'], 
       dtype='datetime64[ns]', freq=None) 

панды также имеет экспериментальную CustomBusinessMonth and the like, но я не мог заставить его работать.

+0

Это замечательно. Много хорошего понимания и ссылок на интересные функции. Спасибо за попытку. – piRSquared

+0

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

1

Я не понимаю вашего вопроса, но считаю, что это шаг в правильном направлении.

start = '2012-04-23' 
end = '2013-01-23' 

>>> pd.DatetimeIndex([pd.datetime(ts.year, ts.month, int(end.split("-")[-1])) 
         for ts in pd.date_range(start, end, freq='BM')]) 

DatetimeIndex(['2012-04-23', '2012-05-23', '2012-06-23', '2012-07-23', '2012-08-23', '2012-09-23', '2012-10-23', '2012-11-23', '2012-12-23'], dtype='datetime64[ns]', freq=None) 

Хотя я не оптимизирован для скорости, я считаю, что следующая функция вернет правильные значения в соответствии с вашими требованиями.

def foo(date, periods, forward=True): 
    if isinstance(date, str): 
     date = pd.Timestamp(date).date() 
    dates = [date + relativedelta(date, months=n * (1 if forward else -1)) for n in range(1, periods +1)] 
    result = [] 
    print dates 
    for date in dates: 
     month = date.month 
     iso_day = date.isoweekday() 
     if iso_day == 6: 
      date += dt.timedelta(days=2 if forward else -1) 
     elif iso_day == 7: 
      date += dt.timedelta(days=1 if forward else -2) 
     if date.month != month: 
      # Gone into next/preceding month. Roll back/forward. 
      date -= dt.timedelta(days=3 if forward else -3) 
     result.append(date) 
    return result 
+0

Это определенно будет работать для определенного набора дат, которые я использовал в этом примере. У меня уже есть надежное, хотя и запутанное решение. Я хотел запросить другие мнения, чтобы у меня был более чистый ответ. Я буду следить за дополнительными комментариями по моему вопросу. – piRSquared

+1

«BM» не представляется допустимым для периодов. Что, если сегодня было 2016-6-01 (ср.). 2016-05-01 - воскресенье, так что вы хотите увидеть 2016-04-28 (пятница)? http://pandas.pydata.org/pandas-docs/stable/timeseries.html#anchored-offsets – Alexander

+0

Это такой хороший вопрос. Я должен выбрать 2016-05-02. – piRSquared

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

  • Нет связанных вопросов^_^