【问题标题】:Calculate difference between two time columns in pandas as a new column excluding weekends, when the columns may contain NaT当列可能包含 NaT 时,将 pandas 中两个时间列之间的差异计算为不包括周末的新列
【发布时间】:2021-02-02 09:18:45
【问题描述】:

我需要计算两个包含日期的 pandas df 列之间的差异。我需要以天为单位的差异,而不是包含周末。

我知道我可以使用

 np.busday_count()

实现这一目标。我也知道,为了让这个函数正常工作,我可能需要将数据类型切换为 datetime[64]。

我的问题是,由于数据来源的原因,我的数据可能在任一输入列中包含 NaT 的某些值。 np.nusday_count() 不喜欢这样。

有没有一种方法可以计算包含日期的两列之间的差异,以天为单位,同时不包括周末,对于第 1 列或第 2 列将是 NaT 的任何地方不运行计算/返回空白并将数据输入到数据框中的新列。我已经尝试了好几个小时都没有成功。

令人沮丧的是,如果我不必忽略周末,这一切都可以非常简单地完成

df['Difference in Days'] = df['Column 2] - df['Column 1']

对于我想要实现的初始 df 将是这样的

Column 1       Column 2
07/10/20       09/10/20
07/10/20       12/10/20
NaT            09/10/20
07/10/20       NaT

像这样的最终df

Column 1       Column 2    Difference in Days
07/10/20       09/10/20    2
07/10/20       12/10/20    3
NaT            09/10/20    Blank
07/10/20       NaT         Blank

【问题讨论】:

    标签: python pandas numpy


    【解决方案1】:

    您可以创建一个自定义函数来捕获由于 NaT 值而导致的 ValueError:

    首先将值转换为日期时间格式:

    for i in df:
      df[i] = pd.to_datetime(df[i],infer_datetime_format=True,errors='coerce',dayfirst=True)
    

    带有 Try/Except 子句的自定义函数来捕获错误并返回“Blank”。

    def diff_days(row):
      try:
        return np.busday_count(row['Column 1'].date(), row['Column 2'].date())
      except ValueError:
        return "Blank"
    

    使用apply

    df['Diff'] = df.apply(lambda x: diff_days(x),axis=1)
    

    返回:

        Column 1   Column 2   Diff
    0 2020-07-10 2020-10-09     2
    1 2020-07-10 2020-10-12     3 
    2        NaT 2020-09-10  Blank
    3 2020-07-10        NaT  Blank
    

    【讨论】:

    • 添加了dayfirst=True,因为 Scott Boston 建议正确解析日期。谢谢斯科特!
    • 很好的解决方案。我的实际数据框的范围比我在问题中解释的要大得多,我实际上正在创建多个这些计算列。我能够使用这种定义子句的方法来对不同的列重复此操作。我已经在使用 pd.to_datetime 但实际上已将其设置为 errors='ignore' 因为我正在读取的源数据的格式存在问题。代码仍然可以使用它。
    • 很高兴知道并随时在您面临的进一步问题中发表评论!
    【解决方案2】:

    我假设两列都已转换为日期时间

    第一步是创建 wrk - 一个带有行的 DataFrame 在周末和 wrkV 之外 - 转换的相同日期 到 Numpy 数组,也是 datetime 但具有 D 分辨率:

    wrk = df[df['Column 2'].dt.dayofweek.lt(5) & df['Column 1'].dt.dayofweek.lt(5)]
    wrkV = wrk.values.astype('<M8[D]')
    

    要生成新列,请运行:

    df['Difference in Days'] = pd.Series(np.busday_count(wrkV[:, 0], wrkV[:, 1]), index=wrk.index)
    df['Difference in Days'].fillna('Blank', inplace=True)
    

    结果是:

        Column 1   Column 2 Difference in Days
    0 2020-10-07 2020-10-09                  2
    1 2020-10-07 2020-10-12                  3
    2        NaT 2020-10-09              Blank
    3 2020-10-07        NaT              Blank
    

    【讨论】:

      【解决方案3】:

      这是一个矢量化解决方案:您可以在将 NaT 发送到 np.busday_count 之前过滤掉它们:

      # If you haven't done so already, convert the columns to Timestamp
      cols = ['Column 1', 'Column 2']
      df[cols] = df[cols].apply(lambda col: pd.to_datetime(col, dayfirst=True, errors='coerce'))
      
      # Look for the NaTs and remove them
      mask = df[cols].notnull().all(axis=1)
      
      # numpy does not like datetime64[ns] so convert to datetime64[D]
      c1, c2 = df.loc[mask, cols].T.to_numpy('datetime64[D]')
      
      # And the result
      df['Diff'] = pd.Series(np.busday_count(c1, c2), index=mask[mask].index)
      

      【讨论】:

        猜你喜欢
        • 2020-05-03
        • 2019-12-03
        • 1970-01-01
        • 2019-12-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-06-20
        相关资源
        最近更新 更多