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

#python #r #pandas #dplyr #data.table

#python #pandas #дата-время #Календарь

Вопрос:

Я имею дело с финансовыми данными, которые мне нужно экстраполировать на разные месяцы. Вот мой фрейм данных:

 invoice_id,date_from,date_to
30492,2019-02-04,2019-09-18
  

Я хочу разбить это на разные месяцы между date_from и date_to. Следовательно, мне нужно добавлять строки для каждого месяца с датой начала месяца до даты окончания. Конечный результат должен выглядеть следующим образом:

 invoice_id,date_from,date_to
30492,2019-02-04,2019-02-28
30492,2019-03-01,2019-03-31
30492,2019-04-01,2019-04-30
30492,2019-05-01,2019-05-31
30492,2019-06-01,2019-06-30
30492,2019-07-01,2019-07-31
30492,2019-08-01,2019-08-30
30492,2019-09-01,2019-09-18
  

Нужно также позаботиться о сценарии високосного года. Есть ли какой- либо собственный метод, уже доступный в пакете pandas datetime, который я могу использовать для достижения желаемого результата?

Ответ №1:

Используйте:

 print (df)
   invoice_id  date_from    date_to
0       30492 2019-02-04 2019-09-18
1       30493 2019-01-20 2019-03-10

#added months between date_from and date_to
df1 = pd.concat([pd.Series(r.invoice_id,pd.date_range(r.date_from, r.date_to, freq='MS')) 
                 for r in df.itertuples()]).reset_index()
df1.columns = ['date_from','invoice_id']

#added starts of months - sorting for correct positions
df2 = (pd.concat([df[['invoice_id','date_from']], df1], sort=False, ignore_index=True)
         .sort_values(['invoice_id','date_from'])
         .reset_index(drop=True))

#added MonthEnd and date_to  to last rows
mask = df2['invoice_id'].duplicated(keep='last')
s = df2['invoice_id'].map(df.set_index('invoice_id')['date_to'])
df2['date_to'] = np.where(mask, df2['date_from']   pd.offsets.MonthEnd(), s)

print (df2)
    invoice_id  date_from    date_to
0        30492 2019-02-04 2019-02-28
1        30492 2019-03-01 2019-03-31
2        30492 2019-04-01 2019-04-30
3        30492 2019-05-01 2019-05-31
4        30492 2019-06-01 2019-06-30
5        30492 2019-07-01 2019-07-31
6        30492 2019-08-01 2019-08-31
7        30492 2019-09-01 2019-09-18
8        30493 2019-01-20 2019-01-31
9        30493 2019-02-01 2019-02-28
10       30493 2019-03-01 2019-03-10
  

Ответ №2:

Вы можете использовать pandas.date_range с начальной и конечной датой в сочетании с freq='MS' , которая является началом месяца, и freq='M' которая является концом месяца:

 x = pd.date_range(start=df.iloc[0]['date_from'], end=df.iloc[0]['date_to'], freq='MS')
y = pd.date_range(start=df.iloc[0]['date_from'], end=df.iloc[0]['date_to'], freq='M')

df_new = pd.DataFrame({'date_from':x,
                       'date_to':y})

df_new['invoice_id'] = df.iloc[0]['invoice_id']

print(df_new)
   date_from    date_to  invoice_id
0 2019-03-01 2019-02-28       30492
1 2019-04-01 2019-03-31       30492
2 2019-05-01 2019-04-30       30492
3 2019-06-01 2019-05-31       30492
4 2019-07-01 2019-06-30       30492
5 2019-08-01 2019-07-31       30492
6 2019-09-01 2019-08-31       30492
  

Ответ №3:

Другой способ, используя resample метод индекса datetime:

 # melt, so we have start and end dates in 1 column
df = pd.melt(df, id_vars='invoice_id')
# now set the date column as index
df.set_index(inplace=True, keys='value')
# resample to daily level
df = df.resample('D').ffill().reset_index()
# get the yr-month value of each daily row
df['yr_month'] = df['value'].dt.strftime("%Y-%m")

# Now group by month and take min/max day values
output = (df.groupby(['invoice_id', 'yr_month'])['value']
          .agg({'date_from': 'min', 'date_to': 'max'})
          .reset_index()
          .drop(labels='yr_month', axis=1))

print(output)

   invoice_id  date_from    date_to
0       30492 2019-02-04 2019-02-28
1       30492 2019-03-01 2019-03-31
2       30492 2019-04-01 2019-04-30
3       30492 2019-05-01 2019-05-31
4       30492 2019-06-01 2019-06-30
5       30492 2019-07-01 2019-07-31
6       30492 2019-08-01 2019-08-31
7       30492 2019-09-01 2019-09-18