【问题标题】:Pandas Dataframe Create Column of Next Future Date from Unique values of two other columns, with GroupbyPandas Dataframe 从其他两列的唯一值创建下一个未来日期的列,使用 Groupby
【发布时间】:2017-10-26 22:20:01
【问题描述】:

我有一个可以用这个创建的数据框:

import pandas as pd
import datetime

#create df
data={'id':[1,1,1,1,2,2,2,2],
      'date1':[datetime.date(2016,1,1),datetime.date(2016,7,23),datetime.date(2017,2,26),datetime.date(2017,5,28),
               datetime.date(2015,11,1),datetime.date(2016,7,23),datetime.date(2017,6,28),datetime.date(2017,5,23)],
      'date2':[datetime.date(2017,5,12),datetime.date(2016,8,10),datetime.date(2017,10,26),datetime.date(2017,9,22),
               datetime.date(2015,11,9),datetime.date(2016,9,23),datetime.date(2017,8,3),datetime.date(2017,9,22)]}
df=pd.DataFrame.from_dict(data)
df=df[['id','date1','date2']]

看起来像这样:

df
Out[83]: 
   id       date1       date2
0   1  2016-01-01  2017-05-12
1   1  2016-07-23  2016-08-10
2   1  2017-02-26  2017-10-26
3   1  2017-05-28  2017-09-22
4   2  2015-11-01  2015-11-09
5   2  2016-07-23  2016-09-23
6   2  2017-06-28  2017-08-03
7   2  2017-05-23  2017-09-22

我需要做的是创建一个名为“newdate”的新列,该列在 groupby['id'] 级别将获取列 date1 和 date2 中按日期值分组的所有唯一值,并为我提供 NEXT FUTURE 日期date2 中日期之后的那些唯一值。

所以新的数据框看起来像:

df
Out[87]: 
   id       date1       date2     newdate
0   1  2016-01-01  2017-05-12  2017-05-28
1   1  2016-07-23  2016-08-10  2017-02-26
2   1  2017-02-26  2017-10-26        None
3   1  2017-05-28  2017-09-22  2017-10-26
4   2  2015-11-01  2015-11-09  2016-07-23
5   2  2016-07-23  2016-09-23  2017-05-23
6   2  2017-06-28  2017-08-03  2017-09-22
7   2  2017-05-23  2017-09-22        None

为了清楚起见,请查看 id=2 记录。请注意第 4 行,新日期为 2016-07-23。这是因为它是 date1 和 date2 列中 id=2 表示的所有日期中的第一个日期,它位于第 4 行 date2 之后。

我们肯定需要使用 groupby。我认为我们可以使用一些形式的 unique()、np.unique、pd.unique 来获取日期?但是,您如何选择“下一个”并分配?只是被难住了……

其他几点。不要假设数据框以任何方式排序,效率在这里很重要,因为实际的数据框非常大。另请注意,newdate 中的“None”值存在,因为我们没有表示“NEXT”未来日期,因为子集中的最大日期与 date2 相同。我们可以用None,nan,随便什么来代表这些……

编辑: 根据温的回答,如果日期相同,他的回答将失败。如果你使用这个数据集:

data={'id':[1,1,1,1,2,2,2,2],
      'date1':[datetime.date(2016,1,1),datetime.date(2016,7,23),datetime.date(2017,2,26),datetime.date(2017,5,28),
               datetime.date(2015,11,1),datetime.date(2016,7,23),datetime.date(2017,6,28),datetime.date(2017,5,23)],
      'date2':[datetime.date(2017,5,12),datetime.date(2017,5,12),datetime.date(2017,2,26),datetime.date(2017,9,22),
               datetime.date(2015,11,9),datetime.date(2016,9,23),datetime.date(2017,8,3),datetime.date(2017,9,22)]}
df=pd.DataFrame.from_dict(data)
df=df[['id','date1','date2']]

那么结果是:

df
Out[104]: 
   id       date1       date2     newdate
0   1  2016-01-01  2017-05-12  2017-05-12
1   1  2016-07-23  2017-05-12  2017-05-28
2   1  2017-02-26  2017-02-26  2017-05-12
3   1  2017-05-28  2017-09-22         NaN
4   2  2015-11-01  2015-11-09  2016-07-23
5   2  2016-07-23  2016-09-23  2017-05-23
6   2  2017-06-28  2017-08-03  2017-09-22
7   2  2017-05-23  2017-09-22         NaN

请注意,第 0 行“newdate”应为 2017-05-28,即 id==1 的 date1&date2 超集的“下一个”可用日期。

我相信融化会让我们更亲近……

【问题讨论】:

    标签: python pandas unique pandas-groupby


    【解决方案1】:

    可能不是最快的,具体取决于您的实际数据框(“非常大”可能意味着什么)。基本上两个步骤 - 首先为下一个日期的每个日期创建一个查找表。然后将该查找与原始表合并。

    #get the latest date for each row - just the max of date1 and date2
    df['latest_date'] = df.loc[:, ['date1','date2']].max(axis=1)
    
    #for each date, find the next date - basically create a lookup table
    new_date_lookup = (df
                       .melt(id_vars=['id'], value_vars=['date1', 'date2'])
                       .loc[:, ['id','value']]
                      )
    
    new_date_lookup = (new_date_lookup
                       .merge(new_date_lookup, on="id")
                       .query("value_y > value_x")
                       .groupby(["id", "value_x"])
                       .min()
                       .reset_index()
                       .rename(columns={'value_x': 'value', 'value_y':'new_date'})
                      )
    
    #merge the original and lookup table together to get the new_date for each row
    new_df = (pd
              .merge(df, new_date_lookup, how='left', left_on=['id', 'latest_date'], right_on=['id','value'])
              .drop(['latest_date', 'value'], axis=1)
             )
    
    print(new_df)
    

    它给出了输出:

       id       date1       date2    new_date
    0   1  2016-01-01  2017-05-12  2017-05-28
    1   1  2016-07-23  2016-08-10  2017-02-26
    2   1  2017-02-26  2017-10-26         NaN
    3   1  2017-05-28  2017-09-22  2017-10-26
    4   2  2015-11-01  2015-11-09  2016-07-23
    5   2  2016-07-23  2016-09-23  2017-05-23
    6   2  2017-06-28  2017-08-03  2017-09-22
    7   2  2017-05-23  2017-09-22         NaN
    

    对于第二个示例,在编辑中添加,给出输出:

       id       date1       date2    new_date
    0   1  2016-01-01  2017-05-12  2017-05-28
    1   1  2016-07-23  2017-05-12  2017-05-28
    2   1  2017-02-26  2017-02-26  2017-05-12
    3   1  2017-05-28  2017-09-22         NaN
    4   2  2015-11-01  2015-11-09  2016-07-23
    5   2  2016-07-23  2016-09-23  2017-05-23
    6   2  2017-06-28  2017-08-03  2017-09-22
    7   2  2017-05-23  2017-09-22         NaN
    

    【讨论】:

    • 这很好用!我想出了一个类似的解决方案,使用 apply(lambda x:...) 创建一个查找表。然后像你一样合并。我认为你使用融化可能更有效,所以我会尝试。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-10
    • 2020-10-04
    • 1970-01-01
    • 2018-08-25
    • 2019-06-05
    • 1970-01-01
    相关资源
    最近更新 更多