Pandas Dataframe 使用 Groupby 从另外两列的唯一值创建下一个未来日期的列
Pandas Dataframe Create Column of Next Future Date from Unique values of two other columns, with Groupby
我有一个可以用这个创建的数据框:
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 列中获取所有按日期值分组的唯一值,并给出我是 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 来获取日期?但是,您如何 select 和 'NEXT' 分配呢?只是难倒...
其他几点。不要假设数据帧以任何方式排序,效率在这里很重要,因为实际数据帧非常大。另请注意,newdate 中存在 'None' 值,因为我们没有表示 'NEXT' 未来日期,因为子集中的最大日期与 date2 相同。我们可以用None,nan,whatever来表示这些...
编辑:
根据 Wen 的回答,如果喜欢日期,他的回答会失败。如果您使用此数据集:
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,'next' id==1 的 date1&date2 超集中的可用日期。
我相信融化会让我们更亲近……
可能不是最快的,具体取决于您的实际数据框("very large" 可能意味着任何事情)。基本上有两个步骤 - 首先为下一个日期的每个日期创建一个查找 table。然后将该查找与原始 table.
合并
#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
我有一个可以用这个创建的数据框:
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 列中获取所有按日期值分组的唯一值,并给出我是 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 来获取日期?但是,您如何 select 和 'NEXT' 分配呢?只是难倒...
其他几点。不要假设数据帧以任何方式排序,效率在这里很重要,因为实际数据帧非常大。另请注意,newdate 中存在 'None' 值,因为我们没有表示 'NEXT' 未来日期,因为子集中的最大日期与 date2 相同。我们可以用None,nan,whatever来表示这些...
编辑: 根据 Wen 的回答,如果喜欢日期,他的回答会失败。如果您使用此数据集:
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,'next' id==1 的 date1&date2 超集中的可用日期。
我相信融化会让我们更亲近……
可能不是最快的,具体取决于您的实际数据框("very large" 可能意味着任何事情)。基本上有两个步骤 - 首先为下一个日期的每个日期创建一个查找 table。然后将该查找与原始 table.
合并#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