【问题标题】:How would you get number of days for each year in betwen dates in dataframe?您将如何获得数据框中日期之间每年的天数?
【发布时间】:2021-04-21 21:26:45
【问题描述】:

所以我有一个数据框,其中包含一个项目列和两个日期列:sale_date 和 entry_date 我想创建 4 个额外的列 2017、2018、2019 和 2020。每列必须包含每个项目的日期数销售前花费的示例是 dataframe1:

  item     Entry_date   Sale_date
dress      12/31/2017   10/31/2020
shoes       1/21/2018   9/10/2020
umbrella    6/14/2019   9/7/2020
car         12/31/2006  6/3/2020

数据框2:

item        Entry_date  Sale_date   2017    2018    2019    2020
dress       12/31/2017  10/31/2020  nan    365.00   365     305
shoes        1/21/2018  9/10/2020   nan    344.00   365     254
umbrella     6/14/2019  9/7/2020    nan     nan     200     251
car         12/31/2006  6/3/2020    4018   365.00   365     155

简单数据框的代码是

df = pd.DataFrame([['dress','31/12/2017','31/10/2020'],['shoes','21/1/2018','10/9/2020'],['umbrella','14/6/2019','7/9/2020'],['car','31/12/2006','3/6/2020'],], columns=['item', 'entry_date', 'sale_date'])

2017 年以下的所有数据都应假定为 2017 年,即不会创建 2017 年以下的列。我正在尝试使用 30,000 长的项目数据框来做到这一点,所以任何帮助都会很棒。

【问题讨论】:

    标签: python pandas dataframe date


    【解决方案1】:

    我不确定您需要这个特定程序有多强大。我已尽力处理我能想到的可能情况,但由于样本量有限,很难了解您可能使用的数据类型。

    我的方法是为每一行生成一个系列,其中包含当年经过的年数到天数。 (有下界的特殊条件)

    import calendar
    from datetime import datetime, date
    
    import pandas as pd
    
    
    def get_days_as_series(start_date, end_date, lower_bound_year):
        if end_date < date(lower_bound_year, 1, 1):
            return None
        elif start_date.year == end_date.year:
            return pd.Series({start_date.year: (end_date - start_date).days})
        else:
            # If Lower Bound Year Defined
            last_day_lower_bound_year = date(lower_bound_year, 12, 31)
            if start_date < last_day_lower_bound_year:
                # Set Days in First Year to Years Before through last day of lower bound
                days_in_first_year = (last_day_lower_bound_year - start_date).days
            else:
                # Otherwise, calculate days to end of current year
                days_in_first_year = (date(start_date.year, 12, 31) - start_date).days
            # Calculate Days in last year
            first_year = max(lower_bound_year, start_date.year)
            n_days = {first_year: days_in_first_year}
            # Handle Years in Range
            # Start at max of lower bound year and current year
            for year in range(first_year + 1, end_date.year):
                # Handle Leap Years
                n_days[year] = 365 + (1 * calendar.isleap(year))
    
            if end_date.year >= lower_bound_year:
                # Days in Last Year
                n_days[end_date.year] = (end_date - date(end_date.year, 1, 1)).days + 1
            return pd.Series(n_days)
    
    
    date_format = "%d/%m/%Y"
    
    df = pd.DataFrame({'item': {0: 'dress', 1: 'shoes', 2: 'umbrella',
                                3: 'car', 4: 'ends before start'},
                       'entry_date': {0: '31/12/2017', 1: '21/1/2018', 2: '14/6/2019',
                                      3: '31/12/2006', 4: '15/4/2007'},
                       'sale_date': {0: '31/10/2020', 1: '10/9/2020', 2: '7/9/2020',
                                     3: '3/6/2020', 4: '28/5/2010'}})
    
    df['entry_date'] = pd.to_datetime(df['entry_date'], format=date_format)
    df['sale_date'] = pd.to_datetime(df['sale_date'], format=date_format)
    
    df = df.merge(
        df.apply(
            lambda s: get_days_as_series(s['entry_date'].date(),
                                         s['sale_date'].date(),
                                         lower_bound_year=2017),
            axis=1
        ), left_index=True, right_index=True)
    print(df.to_string())
    

    输出:

                    item  entry_date   sale_date    2017   2018   2019   2020
    0              dress  31/12/2017  31/10/2020     0.0  365.0  365.0  305.0
    1              shoes   21/1/2018   10/9/2020     NaN  344.0  365.0  254.0
    2           umbrella   14/6/2019    7/9/2020     NaN    NaN  200.0  251.0
    3                car  31/12/2006    3/6/2020  4018.0  365.0  365.0  155.0
    4  ends before start   15/4/2007   28/5/2010     NaN    NaN    NaN    NaN
    

    一些注意事项和假设:

    注意事项:

    1. 目前仅实施了下限,这意味着如果您有 2021 年的日期,您最终会得到一个 2021 年的列。您可以编辑以在上限中包含一个最小值。

    2. 您没有指定如何处理结束日期发生在 2017 年之前的情况。我添加了这个带有索引 4 的示例,目前我只返回 None,这会导致 NaN 跨越。如果您想在 2017 列中显示 2017 年之前发生的日期,您可以省略以下行:

    if end_date < date(lower_bound_year, 1, 1):
        return None
    

    假设:

    1. sale_date 总是在entry_date 之后。如果这不正确,您将不得不更新get_days_as_series 中的条件以进行适当处理。

    为了完整起见,我提供了一个版本,该版本将返回每年的所有值而没有下限,以防有人稍后发现此问题并正在寻找这种功能:

    import calendar
    from datetime import datetime, date
    
    import pandas as pd
    
    
    def get_days_as_series(start_date, end_date):
        if start_date.year == end_date.year:
            return pd.Series({start_date.year: (end_date - start_date).days})
        else:
            days_in_first_year = (date(start_date.year, 12, 31) - start_date).days
            n_days = {start_date.year: days_in_first_year}
            for year in range(start_date.year + 1, end_date.year):
                n_days[year] = 365 + (1 * calendar.isleap(year))
    
            n_days[end_date.year] = (end_date - date(end_date.year, 1, 1)).days + 1
            return pd.Series(n_days)
    
    
    date_format = "%d/%m/%Y"
    
    df = pd.DataFrame({'item': {0: 'dress', 1: 'shoes', 2: 'umbrella',
                                3: 'car', 4: 'ends before start'},
                       'entry_date': {0: '31/12/2017', 1: '21/1/2018', 2: '14/6/2019',
                                      3: '31/12/2006', 4: '15/4/2007'},
                       'sale_date': {0: '31/10/2020', 1: '10/9/2020', 2: '7/9/2020',
                                     3: '3/6/2020', 4: '28/5/2010'}})
    
    df['entry_date'] = pd.to_datetime(df['entry_date'], format=date_format)
    df['sale_date'] = pd.to_datetime(df['sale_date'], format=date_format)
    
    df = df.merge(
        df.apply(
            lambda s: get_days_as_series(s['entry_date'].date(),
                                         s['sale_date'].date()),
            axis=1
        ), left_index=True, right_index=True)
    print(df.to_string())
    

    输出:

                    item  entry_date   sale_date  2006   2007   2008   2009   2010   2011   2012   2013   2014   2015   2016   2017   2018   2019   2020
    0              dress  31/12/2017  31/10/2020   NaN    NaN    NaN    NaN    NaN    NaN    NaN    NaN    NaN    NaN    NaN    0.0  365.0  365.0  305.0
    1              shoes   21/1/2018   10/9/2020   NaN    NaN    NaN    NaN    NaN    NaN    NaN    NaN    NaN    NaN    NaN    NaN  344.0  365.0  254.0
    2           umbrella   14/6/2019    7/9/2020   NaN    NaN    NaN    NaN    NaN    NaN    NaN    NaN    NaN    NaN    NaN    NaN    NaN  200.0  251.0
    3                car  31/12/2006    3/6/2020   0.0  365.0  366.0  365.0  365.0  365.0  366.0  365.0  365.0  365.0  366.0  365.0  365.0  365.0  155.0
    4  ends before start   15/4/2007   28/5/2010   NaN  260.0  366.0  365.0  148.0    NaN    NaN    NaN    NaN    NaN    NaN    NaN    NaN    NaN    NaN
    

    【讨论】:

      猜你喜欢
      • 2021-07-08
      • 2023-01-31
      • 2013-07-23
      • 1970-01-01
      • 2022-12-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多