【问题标题】:Calculate the anniversary date for each row and for each index计算每一行和每个索引的周年日期
【发布时间】:2021-05-23 07:18:52
【问题描述】:

我的合同第一行 1442008-02-11 开始,到 2011-03-28 结束。我想计算 20082011 之间每一年的周年纪念日(对于 144 和其他合同)

主要目标是检查每一行合同的周年日期是否正常,如果不计算并使用正确的值更新开始和结束

*这是我的 pandas 数据框,涉及 "144""150" 两个合同:

index NUM_contrat start end anniversary quantity
0 144 2008-02-11 2011-03-28 2009-02-11 550
1 144 2011-03-28 2011-09-19 2012-02-11 550
2 150 2011-09-19 2012-02-10 2012-09-19 900
3 150 2012-02-10 2013-02-10 2013-09-19 900

*这是我想要得到的数据框:

index NUM_contrat start end anniversary quantity
0 144 2008-02-11 2009-02-11 2009-02-11 550
0 144 2009-02-11 2010-02-11 2010-02-11 550
0 144 2010-02-11 2011-02-11 2011-02-11 550
0 144 2011-02-11 2011-03-28 2012-02-11 550
1 144 2011-03-28 2011-09-19 2012-02-11 550
2 150 2011-09-19 2012-02-10 2012-09-19 900
3 150 2012-03-28 2012-07-11 2013-09-19 900

这是我的代码,仅当我只有一个 Num_contract 但不适用于 2 个或更多 NUM_Contrat 时才有效


    
for NUM_contrat in df['NUM_contrat'].unique():

    for i in df['index'].unique():

       for index,row in df.iterrows():

           if df.iloc[index]['end'] > df.iloc[index]['anniversary']:

               df1=pd.DataFrame(df.iloc[index]).transpose() 

               df.loc[index, 'end'] = df.loc[index, 'anniversary']

               df= pd.concat([df,df1],ignore_index=True).sort_values(['start','end']).reset_index(drop=True)

               df.loc[index+1,'start'] = df.loc[index,'anniversary']
               df.loc[index+1,'anniversary'] = df.loc[index,'anniversary'] + relativedelta(years=1)
                    
    return df

【问题讨论】:

    标签: python pandas loops for-loop


    【解决方案1】:
    • 开始日期生成日期范围
    • explode()它来生成所需的行
    • 计算结束周年纪念
    df = pd.read_csv(io.StringIO("""index   NUM_contrat start   end anniversary quantity
    0   144 2008-02-11  2011-03-28  2009-02-11  550
    1   144 2011-03-28  2011-09-19  2012-02-11  550
    2   150 2011-09-19  2012-02-10  2012-09-19  900
    3   150 2012-02-10  2013-02-10  2013-09-19  900"""), sep="\t", index_col=0)
    
    # cleanup - make sure dates are dates
    df.start = pd.to_datetime(df.start)
    df.end = pd.to_datetime(df.end)
    df.anniversary = pd.to_datetime(df.anniversary)
    df
    # # generate a date range for start, based on end date
    df2 = (df.assign(start=df.apply(lambda r: pd.date_range(r.start, 
                                                     periods=((r.end.year+1)-r.start.year), 
                                                     freq=pd.DateOffset(years=1)), axis=1))
    # explode the start dates
     .explode("start")
    # calc end and anivversary dates
     .assign(end=lambda dfa: np.where(dfa.start.dt.year==dfa.end.dt.year,dfa.end, dfa.start+pd.DateOffset(years=1)),
            anniversary=lambda dfa: dfa.start+pd.DateOffset(years=1))
    # anniversary is always the one from the first instance of the contract
     .assign(anniversary=lambda dfa: dfa.groupby(["NUM_contrat",dfa.start.dt.year])["anniversary"].transform("first"))
    )
    
    df2
    
    

    输出

    index NUM_contrat start end anniversary quantity
    0 144 2008-02-11 00:00:00 2009-02-11 00:00:00 2009-02-11 00:00:00 550
    0 144 2009-02-11 00:00:00 2010-02-11 00:00:00 2010-02-11 00:00:00 550
    0 144 2010-02-11 00:00:00 2011-02-11 00:00:00 2011-02-11 00:00:00 550
    0 144 2011-02-11 00:00:00 2011-03-28 00:00:00 2012-02-11 00:00:00 550
    1 144 2011-03-28 00:00:00 2011-09-19 00:00:00 2012-02-11 00:00:00 550
    2 150 2011-09-19 00:00:00 2012-09-19 00:00:00 2012-09-19 00:00:00 900
    2 150 2012-09-19 00:00:00 2012-02-10 00:00:00 2013-09-19 00:00:00 900
    3 150 2012-02-10 00:00:00 2013-02-10 00:00:00 2013-09-19 00:00:00 900
    3 150 2013-02-10 00:00:00 2013-02-10 00:00:00 2014-02-10 00:00:00 900

    【讨论】:

    • 非常感谢,这部分有效。我的数据可能包含例如从 28-03-2011 到 19-09-2011 的合同 144 的另一行,我想要这一行的周年纪念日2011 年 2 月 11 日(每份合同的周年纪念日应为同一天)。如果你能提供帮助,我更新了数据框:)
    • 这是对逻辑的简单补充,添加了一行代码以进一步指定周年纪念日
    【解决方案2】:

    更新:根据您的评论,每份合同不止一行。

    下面的 sn-p 可以回答你的问题。

    >>> data
       NUM_contrat      start        end anniversary  quantity
    0          144 2008-02-11 2011-03-28  2009-02-11       550
    1          144 2011-03-28 2011-09-19  2012-02-11       550
    2          150 2011-09-19 2012-02-10  2012-09-19       900
    3          150 2012-02-10 2013-02-10  2013-09-19       900
    
    for _, sr in data.loc[data["anniversary"] < data["end"]].iterrows():
        df = sr.to_frame().transpose()
        periods = sr["end"].year - sr["start"].year
        idx = pd.date_range(sr["anniversary"], periods=periods, freq="Y")
        idx += pd.DateOffset(days=sr["anniversary"].day, months=sr["anniversary"].month - 1)
        data = pd.concat([data, df.loc[df.index.repeat(len(idx))].assign(anniversary=idx)])
    
      NUM_contrat      start        end anniversary quantity
    0         144 2008-02-11 2011-03-28  2009-02-11      550
    0         144 2008-02-11 2011-03-28  2010-02-11      550
    0         144 2008-02-11 2011-03-28  2011-02-11      550
    0         144 2008-02-11 2011-03-28  2012-02-11      550
    1         144 2011-03-28 2011-09-19  2012-02-11      550
    2         150 2011-09-19 2012-02-10  2012-09-19      900
    3         150 2012-02-10 2013-02-10  2013-09-19      900
    

    【讨论】:

    • 非常感谢您的帮助 :) 但如果我的每份合同不止一行,这将不起作用。平均的想法是检查每份合同,每一行:周年纪念日是否可以,如果它的票据分裂和计算,什么也不做!如果你能看到,我更新了数据框。谢谢 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-31
    • 2022-12-06
    • 1970-01-01
    相关资源
    最近更新 更多