Search code examples
pythonpandasnumpydataframepython-datetime

Elegant way to shift multiple date columns - Pandas


I have a dataframe like as shown below

df = pd.DataFrame({'person_id': [11,11,11,21,21],
                   'offset' :['-131 days','29 days','142 days','20 days','-200 days'],
                   'date_1': ['05/29/2017', '01/21/1997', '7/27/1989','01/01/2013','12/31/2016'],
                   'dis_date': ['05/29/2017', '01/24/1999', '7/22/1999','01/01/2015','12/31/1991'],
                   'vis_date':['05/29/2018', '01/27/1994', '7/29/2011','01/01/2018','12/31/2014']})
df['date_1'] = pd.to_datetime(df['date_1'])
df['dis_date'] = pd.to_datetime(df['dis_date'])
df['vis_date'] = pd.to_datetime(df['vis_date'])

I would like to shift all the dates of each subject based on his offset

Though my code works (credit - SO), I am looking for an elegant approach. You can see am kind of repeating almost the same line thrice.

df['offset_to_shift'] = pd.to_timedelta(df['offset'],unit='d') 
#am trying to make the below lines elegant/efficient
df['shifted_date_1'] = df['date_1'] + df['offset_to_shift']    
df['shifted_dis_date'] = df['dis_date'] + df['offset_to_shift']
df['shifted_vis_date'] = df['vis_date'] + df['offset_to_shift']

I expect my output to be like as shown below

enter image description here


Solution

  • Use, DataFrame.add along with DataFrame.add_prefix and DataFrame.join:

    cols = ['date_1', 'dis_date', 'vis_date']
    df = df.join(df[cols].add(df['offset_to_shift'], 0).add_prefix('shifted_'))
    

    OR, it is also possible to use pd.concat:

    df = pd.concat([df, df[cols].add(df['offset_to_shift'], 0).add_prefix('shifted_')], axis=1)
    

    OR, we can also directly assign the new shifted columns to the dataframe:

    df[['shifted_' + col for col in cols]] = df[cols].add(df['offset_to_shift'], 0)
    

    Result:

    # print(df)
    
       person_id     offset     date_1   dis_date   vis_date offset_to_shift shifted_date_1 shifted_dis_date shifted_vis_date
    0         11  -131 days 2017-05-29 2017-05-29 2018-05-29       -131 days     2017-01-18       2017-01-18       2018-01-18
    1         11    29 days 1997-01-21 1999-01-24 1994-01-27         29 days     1997-02-19       1999-02-22       1994-02-25
    2         11   142 days 1989-07-27 1999-07-22 2011-07-29        142 days     1989-12-16       1999-12-11       2011-12-18
    3         21    20 days 2013-01-01 2015-01-01 2018-01-01         20 days     2013-01-21       2015-01-21       2018-01-21
    4         21  -200 days 2016-12-31 1991-12-31 2014-12-31       -200 days     2016-06-14       1991-06-14       2014-06-14