熊猫:减去两个日期列,结果是一个整数

Posted

技术标签:

【中文标题】熊猫:减去两个日期列,结果是一个整数【英文标题】:Pandas: Subtracting two date columns and the result being an integer 【发布时间】:2016-10-16 21:15:49 【问题描述】:

我在 Pandas 数据框中有两列是日期。

我希望从另一列中减去一列,结果是天数的差异作为整数

查看数据:

df_test.head(10)
Out[20]: 
  First_Date Second Date
0 2016-02-09  2015-11-19
1 2016-01-06  2015-11-30
2        NaT  2015-12-04
3 2016-01-06  2015-12-08
4        NaT  2015-12-09
5 2016-01-07  2015-12-11
6        NaT  2015-12-12
7        NaT  2015-12-14
8 2016-01-06  2015-12-14
9        NaT  2015-12-15

我已经成功创建了一个新列,不同之处:

df_test['Difference'] = df_test['First_Date'].sub(df_test['Second Date'], axis=0)
df_test.head()         
Out[22]: 
  First_Date Second Date  Difference
0 2016-02-09  2015-11-19     82 days
1 2016-01-06  2015-11-30     37 days
2        NaT  2015-12-04         NaT
3 2016-01-06  2015-12-08     29 days
4        NaT  2015-12-09         NaT

但是我无法获得结果的数字版本:

df_test['Difference'] = df_test[['Difference']].apply(pd.to_numeric)     

df_test.head()
Out[25]: 
  First_Date Second Date    Difference
0 2016-02-09  2015-11-19  7.084800e+15
1 2016-01-06  2015-11-30  3.196800e+15
2        NaT  2015-12-04           NaN
3 2016-01-06  2015-12-08  2.505600e+15
4        NaT  2015-12-09           NaN

【问题讨论】:

【参考方案1】:

你可以将dtypetimedelta的列除以np.timedelta64(1, 'D'),但输出不是int,而是float,因为NaN values:

df_test['Difference'] = df_test['Difference'] / np.timedelta64(1, 'D')
print (df_test)
  First_Date Second Date  Difference
0 2016-02-09  2015-11-19        82.0
1 2016-01-06  2015-11-30        37.0
2        NaT  2015-12-04         NaN
3 2016-01-06  2015-12-08        29.0
4        NaT  2015-12-09         NaN
5 2016-01-07  2015-12-11        27.0
6        NaT  2015-12-12         NaN
7        NaT  2015-12-14         NaN
8 2016-01-06  2015-12-14        23.0
9        NaT  2015-12-15         NaN

Frequency conversion.

【讨论】:

【参考方案2】:

您可以在这里使用 datetime 模块提供帮助。另外,附带说明一下,简单的日期减法应该如下所示:

import datetime as dt
import numpy as np
import pandas as pd

#Assume we have df_test:
In [222]: df_test
Out[222]: 
   first_date second_date
0  2016-01-31  2015-11-19
1  2016-02-29  2015-11-20
2  2016-03-31  2015-11-21
3  2016-04-30  2015-11-22
4  2016-05-31  2015-11-23
5  2016-06-30  2015-11-24
6         NaT  2015-11-25
7         NaT  2015-11-26
8  2016-01-31  2015-11-27
9         NaT  2015-11-28
10        NaT  2015-11-29
11        NaT  2015-11-30
12 2016-04-30  2015-12-01
13        NaT  2015-12-02
14        NaT  2015-12-03
15 2016-04-30  2015-12-04
16        NaT  2015-12-05
17        NaT  2015-12-06

In [223]: df_test['Difference'] = df_test['first_date'] - df_test['second_date'] 

In [224]: df_test
Out[224]: 
   first_date second_date  Difference
0  2016-01-31  2015-11-19     73 days
1  2016-02-29  2015-11-20    101 days
2  2016-03-31  2015-11-21    131 days
3  2016-04-30  2015-11-22    160 days
4  2016-05-31  2015-11-23    190 days
5  2016-06-30  2015-11-24    219 days
6         NaT  2015-11-25         NaT
7         NaT  2015-11-26         NaT
8  2016-01-31  2015-11-27     65 days
9         NaT  2015-11-28         NaT
10        NaT  2015-11-29         NaT
11        NaT  2015-11-30         NaT
12 2016-04-30  2015-12-01    151 days
13        NaT  2015-12-02         NaT
14        NaT  2015-12-03         NaT
15 2016-04-30  2015-12-04    148 days
16        NaT  2015-12-05         NaT
17        NaT  2015-12-06         NaT

现在,将类型更改为 datetime.timedelta,然后对有效的 timedelta 对象使用 .days 方法。

In [226]: df_test['Diffference'] = df_test['Difference'].astype(dt.timedelta).map(lambda x: np.nan if pd.isnull(x) else x.days)

In [227]: df_test
Out[227]: 
   first_date second_date  Difference  Diffference
0  2016-01-31  2015-11-19     73 days           73
1  2016-02-29  2015-11-20    101 days          101
2  2016-03-31  2015-11-21    131 days          131
3  2016-04-30  2015-11-22    160 days          160
4  2016-05-31  2015-11-23    190 days          190
5  2016-06-30  2015-11-24    219 days          219
6         NaT  2015-11-25         NaT          NaN
7         NaT  2015-11-26         NaT          NaN
8  2016-01-31  2015-11-27     65 days           65
9         NaT  2015-11-28         NaT          NaN
10        NaT  2015-11-29         NaT          NaN
11        NaT  2015-11-30         NaT          NaN
12 2016-04-30  2015-12-01    151 days          151
13        NaT  2015-12-02         NaT          NaN
14        NaT  2015-12-03         NaT          NaN
15 2016-04-30  2015-12-04    148 days          148
16        NaT  2015-12-05         NaT          NaN
17        NaT  2015-12-06         NaT          NaN

希望对您有所帮助。

【讨论】:

是的,这是一种可能的解决方案,但我认为这是不推荐的方法,因为列 Diffference 的输出是 object 并且下一个处理(加法、减法...)是不可能的。 @jesrael,还有其他方法,例如您的解决方案。但是,对于 NaN 与列中的 int 类型混合,加/减不是问题。它们将根据需要自动转换为浮动操作。【参考方案3】:

怎么样:

df_test['Difference'] = (df_test['First_Date'] - df_test['Second Date']).dt.days

如果没有缺失值(NaT)和float(如果有),这将返回差异为int

Pandas 在 Time series / date functionality 和 Time deltas 上有丰富的文档

【讨论】:

同意@AllenWang。这是最好的答案。 @至少有 3 个建议这是公认的答案 这在最近的版本中可能有所改变。现在使用.days 对我有用,而.dt.days 会引发错误 看来它取决于结果值。如果它们是日期时间序列,则需要 .dt。你能检查表达式的结果吗?它是 DataFrame 还是 Series?我仍在试图弄清楚何时需要 dt 这似乎只能工作几天,而不是几周或几年。【参考方案4】:

我觉得如果日期“环绕”一年左右,则总体答案无法处理。这将有助于了解接近日期的日期是否准确。为了进行这些行操作,我做了以下操作。 (我在商业环境中使用它来更新客户订阅)。

def get_date_difference(row, x, y):
    try:
        # Calcuating the smallest date difference between the start and the close date
        # There's some tricky logic in here to calculate for determining date difference
        # the other way around (Dec -> Jan is 1 month rather than 11)

        sub_start_date = int(row[x].strftime('%j')) # day of year (1-366)
        close_date = int(row[y].strftime('%j')) # day of year (1-366)

        later_date_of_year = max(sub_start_date, close_date) 
        earlier_date_of_year = min(sub_start_date, close_date)
        days_diff = later_date_of_year - earlier_date_of_year

# Calculates the difference going across the next year (December -> Jan)
        days_diff_reversed = (365 - later_date_of_year) + earlier_date_of_year
        return min(days_diff, days_diff_reversed)

    except ValueError:
        return None

那么函数可以是:

dfAC_Renew['date_difference'] = dfAC_Renew.apply(get_date_difference, x = 'customer_since_date', y = 'renewal_date', axis = 1)

【讨论】:

【参考方案5】:

创建矢量化方法

def calc_xb_minus_xa(df):
    time_dict = 
        '<Minute>': 'm',
        '<Hour>': 'h',
        '<Day>': 'D',
        '<Week>': 'W',
        '<Month>': 'M',
        '<Year>': 'Y'
    

    time_delta = df.at[df.index[0], 'end_time'] - df.at[df.index[0], 'open_time']
    offset_base_name = str(to_offset(time_delta).base)
    time_term = time_dict.get(offset_base_name)

    result = (df.end_time - df.open_time) / np.timedelta64(1, time_term)
    return result

然后在你的 df 中做:

df['x'] = calc_xb_minus_xa(df)

这将适用于分钟、小时、天、周、月和年。 open_time 和 end_time 需要根据你的 df 改变

【讨论】:

以上是关于熊猫:减去两个日期列,结果是一个整数的主要内容,如果未能解决你的问题,请参考以下文章

减去熊猫中的日期时间列时返回错误

熊猫将带有年份整数的列转换为日期时间

查找熊猫中两个日期之间差异的最简单方法

在 SQL 中加入表后,通过从当前日期中减去日期列来创建新列

如何在熊猫中将列转换为一个日期时间列?

在 PySpark Python 中减去两个日期列