【问题标题】:python / pandas find number of years between two datespython / pandas查找两个日期之间的年数
【发布时间】:2017-07-20 03:40:54
【问题描述】:

我有一个 pandas 数据框,其中有两列包含日期。我想知道两个日期之间的年数,同时考虑闰年。

理想的解决方案不会假设一年总是有一定天数。因为年份并不总是有 365 天。

样本数据:

date_end    date_start
2010-02-09  1933-03-03
2010-03-19  1924-04-08
2010-04-19  1924-04-08
2010-09-06  1924-04-08
2010-09-24  1924-04-08
2010-01-09  1933-04-29
2010-02-26  1933-04-29
2010-01-31  1953-06-10
2010-07-07  1928-11-14
2010-12-01  1974-11-17

date_startdate_end 属于“日期时间”dtype。我想要一个新列,即两个日期之间的年数。获取两个日期之间的天数(df['diff'] = df.date_end - df.date_start)很简单,但后来我遇到了麻烦,因为给定天数的年数取决于天数发生的“时间”,因为闰年。

这类似于一个人的年龄。我已经尝试为类似问题调整一些解决方案,但许多问题是关于两个日期之间的天数或周数。我已经有办法在不考虑闰年的情况下获得年数,但我想要更正确。

【问题讨论】:

  • 所以从 2016 年 1 月 1 日到 2017 年 1 月 1 日是一年。从 2016 年 2 月 28 日到 2017 年 2 月 28 日呢?从 2016 年 2 月 29 日到 2017 年 2 月 28 日?从 2016 年 2 月 29 日到 2017 年 3 月 1 日?从 2015 年 3 月 1 日到 2016 年 2 月 29 日?

标签: python date pandas datetime


【解决方案1】:

假设您想将一年定义为 365 天,那么您可以这样做:

>> df
    date_end date_start  is_leapyear
0 2016-02-28 2015-02-28            0
1 2017-02-28 2016-02-28            1
2 2018-02-28 2017-02-28            0

>> df['diff_in_days'] = df['date_end'] - df['date_start']
>> df['diff_in_years'] = df["diff_in_days"] / timedelta(days=365)
>> print df[["date_end", "date_start", "diff_in_years"]]

>> df
    date_end date_start  is_leapyear  diff_in_years
0 2016-02-28 2015-02-28            0        1.00000
1 2017-02-28 2016-02-28            1        1.00274
2 2018-02-28 2017-02-28            0        1.00000

如您所见,在有额外天数的年份(2 月 29 日),日期之间的间隔时间更长。在您的情况下,这将是:

    date_end date_start  diff_in_years
0 2010-02-09 1933-03-03      76.991781
1 2010-03-19 1924-04-08      86.002740
2 2010-04-19 1924-04-08      86.087671
3 2010-09-06 1924-04-08      86.471233
4 2010-09-24 1924-04-08      86.520548
5 2010-01-09 1933-04-29      76.750685
6 2010-02-26 1933-04-29      76.882192
7 2010-01-31 1953-06-10      56.682192
8 2010-07-07 1928-11-14      81.698630
9 2010-12-01 1974-11-17      36.063014

另一方面,如果您只想要年份的差异。即减去日期发生的年份(无论该日期发生的年份何时)。然后你可以这样做:

df['date_end_year'] = df.date_end.apply(lambda x: x.year)
df['date_start_year'] = df.date_start.apply(lambda x: x.year)
df['diff_in_years'] = df['date_end_year'] - df['date_start_year']
print df[["date_end", "date_start", "diff_in_years"]]

    date_end date_start  diff_in_years
0 2016-02-28 2015-02-28              1
1 2017-02-28 2016-02-28              1
2 2018-02-28 2017-02-28              1

在你的情况下,这将是:

    date_end date_start  diff_in_years
0 2010-02-09 1933-03-03             77
1 2010-03-19 1924-04-08             86
2 2010-04-19 1924-04-08             86
3 2010-09-06 1924-04-08             86
4 2010-09-24 1924-04-08             86
5 2010-01-09 1933-04-29             77
6 2010-02-26 1933-04-29             77
7 2010-01-31 1953-06-10             57
8 2010-07-07 1928-11-14             82
9 2010-12-01 1974-11-17             36

【讨论】:

  • 还有timedelta在哪个模块?
  • from datetime import timedelta,是的,is_leapyear 仅用于演示目的
  • 好吧,从技术上讲,有 aprox。 365.242... 太阳年中的天数(地球绕太阳公转一周的时间)。这就是为什么我们每 4 年增加一天,这将使我们现在得到 365.25 到 365.242 我们没有每隔一段时间(大约每 400 年)有一个闰年。所以这取决于你想要什么:) Tropical_year
  • 这不是您要找的答案吗?有问题吗?
【解决方案2】:

您可以将列转换为年份并获得年份差异,如下所示:

df.date_end.apply(lambda x: x.year) - df.date_start.apply(lambda x: x.year)

【讨论】:

  • 这是错误的,2020 年 12 月 20 日到 2021 年 1 月 1 日之间没有 1 年
【解决方案3】:

需要解决完全相同的问题,并在下面创建了一个 num_years() 函数来计算它。

代码将 start_date 和 end_date 之间的时间段分解为三个时间段:

  1. 从 start_date 到那年年底,
  2. start_date 和 end_date 之间的整年,并且
  3. 从包含 end_date 的年初到 end_date。

它还处理 start_date 和 end_date 属于同一年、连续年份等的情况。

def num_days_in_year(date: pd.Timestamp):
    return 366 if date.is_leap_year else 365


def num_years(start_date: pd.Timestamp, end_date: pd.Timestamp) -> float:
    """
    Compute the number of years between two given dates, accounting
    for leap years.
    :param start_date: Start date as Pandas Timestamp
    :param end_date: End date as Pandas Timestamp
    :return: float representing the number of years
    """
    start_year = start_date.year
    end_year = end_date.year

    years = 0.0
    if start_year != end_year:
        send = start_date + pd.offsets.YearEnd()
        years += (send - start_date).days / num_days_in_year(start_date)
    if end_year > start_year + 1:
        years += end_year - start_year- 1

    if start_year == end_year:
        start = start_date
    else:
        start = end_date - pd.offsets.YearBegin()
    years += (end_date - start).days / num_days_in_year(end_date)
    return years

【讨论】:

    【解决方案4】:

    我想我对这个案例有更好的想法:

    1. 获取年份差异;
    2. 如果 (start_month end_month) 或 (start_month == end_monthstart_day end_day) 则减 1。

    第二个条件基本上检查最后一整年是否已经过去。

    可以使用 pandas 系列来完成。一种可能的实现方式:

    (
        df.end_date.dt.year - df.start_date.dt.year
        -
        (
            (df.end_date.dt.month < df.start_date.dt.month)
            |
            (
                (df.end_date.dt.month == df.start_date.dt.month)
                &
                (df.end_date.dt.day < df.start_date.dt.day)
            )
        ).astype(int)
    )
    

    假设使用pd.to_datetime 将列转换为日期时间。

    【讨论】:

      【解决方案5】:

      起初,我尝试了 praveen 的回答,但发现有一点不太直观: 如果开始日期属于闰年而结束日期不属于闰年,则结果将不是整数,尽管开始日期和结束日期的月份和日期相同,因为开始日期使用 366,结束日期使用 365。

      我的想法是计算从开始日期到结束日期之前的完整年数。 然后,计算“不”之间的天数。开始日期后的整年”并将其除以 365,如果不是,则除以 366。开始日期或结束日期之后的整年属于闰年。

      def num_years(stdt: pd.Timestamp, endt: pd.Timestamp):
          stYr = stdt.year
          stMon = stdt.month
          stDay = stdt.day
          enYr = endt.year
      
          rv = 0
      
          # num of full years 
          calcStdt = pd.Timestamp(year=enYr, month=stMon, day=stDay)
          if calcStdt > endt:
              calcStdt = pd.Timestamp(year=enYr - 1, month=stMon, day=stDay)
              rv = enYr - 1 - stYr
          else:
              rv = enYr - stYr
      
          # remaining period less than a year
          if (calcStdt.is_leap_year==True) | (endt.is_leap_year==True):
              rv = rv + (endt-calcStdt).days/366
          else:  
              rv = rv + (endt-calcStdt).days/365
          return rv
      
      # Test
      print(num_years(pd.Timestamp(year=2000, month=1, day=10), pd.Timestamp(year=2004, month=1, day=10)))
      print(num_years(pd.Timestamp(year=2000, month=5, day=10), pd.Timestamp(year=2004, month=5, day=10)))
      print(num_years(pd.Timestamp(year=2001, month=1, day=10), pd.Timestamp(year=2004, month=1, day=10)))
      print(num_years(pd.Timestamp(year=2001, month=5, day=10), pd.Timestamp(year=2004, month=5, day=10)))
      

      【讨论】:

        【解决方案6】:

        上述 Alexandr Sudakov 解决方案的一个变体是

        df['Age'] = (df['date_end'].dt.year - df['date_start'].dt.year).astype(int)
        df.loc[df['date_end'].dt.month < df['date_start'].dt.month, 'Age'] = df['Age'] - 1
        df.loc[(df['date_end'].dt.month == df['date_start'].dt.month) & (df['date_end'].dt.day < df['date_start'].dt.day), 'Age'] = df['Age'] - 1
        

        这给了

            date_end date_start  Age
        0 2010-02-09 1933-03-03   76
        1 2010-03-19 1924-04-08   85
        2 2010-04-19 1924-04-08   86
        3 2010-09-06 1924-04-08   86
        4 2010-09-24 1924-04-08   86
        5 2010-01-09 1933-04-29   76
        6 2010-02-26 1933-04-29   76
        7 2010-01-31 1953-06-10   56
        8 2010-07-07 1928-11-14   81
        9 2010-12-01 1974-11-17   36
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-07-19
          • 2020-02-07
          • 1970-01-01
          • 2022-01-14
          相关资源
          最近更新 更多