在groupby数据帧中查找给定日期的最近日期(Python)

时间:2015-12-14 02:14:09

标签: python datetime group-by

我正在尝试在我的pandas数据框中生成Last_Payment_Date字段,并且需要在每个客户(即groupby)的给定Payment_Date之前找到最接近的Order_Date。< / p>

Payment_Date将始终发生在Order_Date之后,但可能需要不同的时间段,这很难使用排序和转移来查找最近的日期。

Masking似乎是一种可能的方式,但我无法想办法如何使用它。

感谢我能得到的所有帮助!

Cust_No  Order_Date  Payment_Date  Last_Payment_Date
      A    5/8/2014      6/8/2014                Nat
      B    6/8/2014      1/5/2015                Nat
      B    7/8/2014      7/8/2014                Nat
      A    8/8/2014      1/5/2015           6/8/2014
      A    9/8/2014     10/8/2014           6/8/2014
      A  10/11/2014    12/11/2014          10/8/2014
      B  11/12/2014      1/1/2015           7/8/2014
      B    1/2/2015      2/2/2015           1/1/2015
      A    2/5/2015      5/5/2015           1/5/2015
      B    3/5/2015      4/5/2015           2/2/2015

1 个答案:

答案 0 :(得分:3)

Series.searchsorted很大程度上做你想要的 - 它 可用于查找Order_DatePayment_Date内的适合位置。在 特别是,它返回与每个索引对应的序数索引 需要插入Order_Date才能保留Payment_Dates 排序。例如,假设

In [266]: df['Payment_Date']
Out[266]: 
0   2014-06-08
2   2014-07-08
4   2014-10-08
5   2014-12-11
6   2015-01-01
1   2015-01-05
3   2015-01-05
7   2015-02-02
9   2015-04-05
8   2015-05-05
Name: Payment_Date, dtype: datetime64[ns]

In [267]: df['Order_Date']
Out[267]: 
0   2014-05-08
2   2014-07-08
4   2014-09-08
5   2014-10-11
6   2014-11-12
1   2014-06-08
3   2014-08-08
7   2015-01-02
9   2015-03-05
8   2015-02-05
Name: Order_Date, dtype: datetime64[ns]

然后searchsorted返回

In [268]: df['Payment_Date'].searchsorted(df['Order_Date'])
Out[268]: array([0, 1, 2, 3, 3, 0, 2, 5, 8, 8])

例如,第一个值0表示Order_Date2014-05-08, 必须插入序数索引0(Payment_Date之前 2014-06-08)保持Payment_Date按排序顺序排列。第二个值,1, 表示必须插入Order_Date2014-07-08 序数索引1(在Payment_Date 2014-06-08之后和2014-07-08之前) 保持Payment_Date按排序顺序。等等其他指数。

现在,当然,有一些并发症:

  1. Payment_Dates需要按照searchsorted的排序顺序返回a 有意义的结果:

    df = df.sort_values(by=['Payment_Date'])    
    
  2. 我们需要按Cust_No

    进行分组
    grouped = df.groupby('Cust_No')
    
  3. 我们希望之前的<{>}的Payment_Date索引 Order_Date。因此,我们确实需要将索引减少一个:

    idx = grp['Payment_Date'].searchsorted(grp['Order_Date']) 
    result = grp['Payment_Date'].iloc[idx-1]
    
  4. 因此grp['Payment_Date'].iloc[idx-1]会抓住之前 Payment_Date

    1. searchsorted返回0时,Order_Date小于全部 Payment_Date秒。在这种情况下我们想要一个NaT。

      result[idx == 0] = pd.NaT
      
    2. 所以把它全部放在一起,

      import pandas as pd
      NaT = pd.NaT
      T = pd.Timestamp
      df = pd.DataFrame({
          'Cust_No': ['A', 'B', 'B', 'A', 'A', 'A', 'B', 'B', 'A', 'B'],
          'expected': [
              NaT,  NaT,  NaT, T('2014-06-08'), T('2014-06-08'), T('2014-10-08'), 
              T('2014-07-08'), T('2015-01-01'), T('2015-01-05'), T('2015-02-02')], 
          'Order_Date': [
              T('2014-05-08'), T('2014-06-08'), T('2014-07-08'), T('2014-08-08'), 
              T('2014-09-08'), T('2014-10-11'), T('2014-11-12'), T('2015-01-02'), 
              T('2015-02-05'), T('2015-03-05')], 
          'Payment_Date': [
              T('2014-06-08'), T('2015-01-05'), T('2014-07-08'), T('2015-01-05'), 
              T('2014-10-08'), T('2014-12-11'), T('2015-01-01'), T('2015-02-02'), 
              T('2015-05-05'), T('2015-04-05')]})
      
      def last_payment_date(s, df):
          grp = df.loc[s.index]
          idx = grp['Payment_Date'].searchsorted(grp['Order_Date']) 
          result = grp['Payment_Date'].iloc[idx-1]
          result[idx == 0] = pd.NaT
          return result
      
      df = df.sort_values(by=['Payment_Date'])    
      grouped = df.groupby('Cust_No')
      df['Last_Payment_Date'] = grouped['Payment_Date'].transform(last_payment_date, df)
      
      print(df)
      

      产量

        Cust_No Order_Date Payment_Date   expected Last_Payment_Date
      0       A 2014-05-08   2014-06-08        NaT               NaT
      2       B 2014-07-08   2014-07-08        NaT               NaT
      4       A 2014-09-08   2014-10-08 2014-06-08        2014-06-08
      5       A 2014-10-11   2014-12-11 2014-10-08        2014-10-08
      6       B 2014-11-12   2015-01-01 2014-07-08        2014-07-08
      1       B 2014-06-08   2015-01-05        NaT               NaT
      3       A 2014-08-08   2015-01-05 2014-06-08        2014-06-08
      7       B 2015-01-02   2015-02-02 2015-01-01        2015-01-01
      9       B 2015-03-05   2015-04-05 2015-02-02        2015-02-02
      8       A 2015-02-05   2015-05-05 2015-01-05        2015-01-05