【问题标题】:Create new rows in a dataframe by range of dates按日期范围在数据框中创建新行
【发布时间】:2021-08-31 04:45:39
【问题描述】:

我需要在数据框中按天生成日期列表,并且每一天都是新数据框中的一行,同时考虑到每条记录的开始日期和结束日期。

输入数据框:

A B Start End
A1 B1 2021-05-15 00:00:00 2021-05-17 00:00:00
A1 B2 2021-05-30 00:00:00 2021-06-02 00:00:00
A2 B3 2021-05-10 00:00:00 2021-05-12 00:00:00
A2 B4 2021-06-02 00:00:00 2021-06-04 00:00:00

预期输出:

A B Start End
A1 B1 2021-05-15 00:00:00 2021-05-16 00:00:00
A1 B1 2021-05-16 00:00:00 2021-05-17 00:00:00
A1 B2 2021-05-30 00:00:00 2021-05-31 00:00:00
A1 B2 2021-05-31 00:00:00 2021-06-01 00:00:00
A1 B2 2021-06-01 00:00:00 2021-06-02 00:00:00
A2 B3 2021-05-10 00:00:00 2021-05-11 00:00:00
A2 B3 2021-05-11 00:00:00 2021-05-12 00:00:00
A2 B4 2021-06-02 00:00:00 2021-06-03 00:00:00
A2 B4 2021-06-03 00:00:00 2021-06-04 00:00:00

【问题讨论】:

    标签: python pandas dataframe datetime


    【解决方案1】:

    用途:

    #convert columns to datetimes
    df["Start"] = pd.to_datetime(df["Start"])
    df["End"] = pd.to_datetime(df["End"])
    
    #subtract values and convert to days
    s = df["End"].sub(df["Start"]).dt.days
    
    #repeat index
    df = df.loc[df.index.repeat(s)].copy()
    
    #add days by timedeltas, add 1 day for End column
    add = pd.to_timedelta(df.groupby(level=0).cumcount(), unit='d')
    df['Start'] = df["Start"].add(add)
    df['End'] = df["Start"] + pd.Timedelta(1, 'd')
    
    #default index
    df = df.reset_index(drop=True)
    print (df)
        A   B      Start        End
    0  A1  B1 2021-05-15 2021-05-16
    1  A1  B1 2021-05-16 2021-05-17
    2  A1  B2 2021-05-30 2021-05-31
    3  A1  B2 2021-05-31 2021-06-01
    4  A1  B2 2021-06-01 2021-06-02
    5  A2  B3 2021-05-10 2021-05-11
    6  A2  B3 2021-05-11 2021-05-12
    7  A2  B4 2021-06-02 2021-06-03
    8  A2  B4 2021-06-03 2021-06-04
    

    性能

    #4k rows
    df = pd.concat([df] * 1000, ignore_index=True)
    
    In [136]: %timeit jez(df)
    16.9 ms ± 3.94 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
    
    In [137]: %timeit andreas(df)
    888 ms ± 136 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    

    #800 rows
    df = pd.concat([df] * 200, ignore_index=True)
    
    
    In [139]: %timeit jez(df)
    6.25 ms ± 46.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    
    In [140]: %timeit andreas(df)
    170 ms ± 28.1 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
    

    def andreas(df):
        df['d_range'] = df.apply(lambda row: list(pd.date_range(start=row['Start'], end=row['End'])), axis=1)
        return df.explode('d_range')
    
    def jez(df):
        df["Start"] = pd.to_datetime(df["Start"])
        df["End"] = pd.to_datetime(df["End"])
        
        #subtract values and convert to days
        s = df["End"].sub(df["Start"]).dt.days
        
        #repeat index
        df = df.loc[df.index.repeat(s)].copy()
        
        #add days by timedeltas, add 1 day for End column
        add = pd.to_timedelta(df.groupby(level=0).cumcount(), unit='d')
        df['Start'] = df["Start"].add(add)
        df['End'] = df["Start"] + pd.Timedelta(1, 'd')
        
        #default index
        return df.reset_index(drop=True)
    

    【讨论】:

      【解决方案2】:

      您可以创建日期列表并将其展开:

      df['d_range'] = df.apply(lambda row: list(pd.date_range(start=row['Start'], end=row['End'])), axis=1)
      df = df.explode('d_range')
      

      【讨论】:

        猜你喜欢
        • 2021-10-26
        • 2021-07-22
        • 2020-08-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-15
        • 1970-01-01
        相关资源
        最近更新 更多