Pandas Dataframe - 具有 Condition/Row Iteration/Prev 行计算的最小函数
Pandas Dataframe - Min Function with Condition/Row Iteration/Prev Row Calculation
我有一个数据框,其中包含一些记录的开始和结束日期:
import pandas as pd
df = pd.DataFrame({'Key': ['A', 'A', 'A', 'A', 'B', 'B', 'B', 'B' ],
'StartDate': ['01/01/2015', '01/01/2016', '06/01/2016','10/01/2017',
'01/01/2015', '01/01/2016', '07/15/2016','10/01/2017'],
'EndDate': ['12/30/2015', '05/31/2016', '09/30/2017', '12/31/2018',
'12/30/2015', '05/31/2016', '09/30/2017', '12/31/2018']})
df = df[['Key', 'StartDate', 'EndDate']]
print(df)
我的输出如下所示:
Key StartDate EndDate
0 A 01/01/2015 12/30/2015
1 A 01/01/2016 05/31/2016
2 A 06/01/2016 09/30/2017
3 A 10/01/2017 12/31/2018
4 B 01/01/2015 12/30/2015
5 B 01/01/2016 05/31/2016
6 B 07/15/2016 09/30/2017
7 B 10/01/2017 12/31/2018
我需要知道每个密钥的最早开始日期和最晚结束日期。
我这样做了(如果有更好的方法,请告诉我):
df_start = df.groupby('Key')['StartDate'].min().reset_index(name = 'StartDate')
df_end = df.groupby('Key')['EndDate'].max().reset_index(name = 'EndDate')
final = pd.merge(df_start, df_end, on = 'Key', how = 'left')
print(final)
这给了我这个输出:
Key StartDate EndDate
0 A 01/01/2015 12/31/2018
1 B 01/01/2015 12/31/2018
现在,如果您查看原始数据框中的键 "B",您会看到第 5 行的结束日期是 05/31/2016,第 6 行的开始日期是 07/15 /2016,所以这些记录不是连续的。有 1.5 个月的日期中断。如果日期中断超过 3 天,我只需要 return 连续记录的最早开始日期,因此在这种情况下,所需的输出将是:
Key StartDate EndDate
0 A 01/01/2015 12/31/2018
1 B 07/15/2016 12/31/2018
我一直在尝试使用 'shift' 方法来计算每一行的开始日期和前一行的结束日期之间的天数,但不确定我是否要进入完全正确的方向..或者我应该遍历行?我的数据框中有数十万条记录。
完成此任务的最有效方法是什么?谢谢。
好的,您需要为定义的连续记录创建一个标记,然后分组并删除重复项:
df['StartDate'] = pd.to_datetime(df['StartDate'])
df['EndDate'] = pd.to_datetime(df['EndDate'])
consec = (df.groupby('Key').apply(lambda x: x.StartDate - x.EndDate.shift(1) >= pd.Timedelta('3 day'))
.cumsum().reset_index(drop=True))
(df.groupby(['Key',consec])
.agg({'StartDate':'min','EndDate':'max'})
.reset_index()
.drop_duplicates('Key', keep='last')
.drop('level_1', axis=1))
输出:
Key StartDate EndDate
0 A 2015-01-01 2018-12-31
2 B 2016-07-15 2018-12-31
我绝不是 Pandas 专家,但我想我有一些东西可以满足您的需求。首先,我将日期转换为日期时间:
df['StartDate'] = pd.to_datetime(df['StartDate'], infer_datetime_format=True)
df['EndDate'] = pd.to_datetime(df['EndDate'], infer_datetime_format=True)
print(df)
结果:
Key StartDate EndDate
0 A 2015-01-01 2015-12-30
1 A 2016-01-01 2016-05-31
2 A 2016-06-01 2017-09-30
3 A 2017-10-01 2018-12-31
4 B 2015-01-01 2015-12-30
5 B 2016-01-01 2016-05-31
6 B 2016-07-15 2017-09-30
7 B 2017-10-01 2018-12-31
然后确定每个组中结束日期和开始日期之间的时间量:
df['Break'] = (df.groupby('Key')
.apply(lambda d: d['StartDate'] - d['EndDate'].shift(1))
.reset_index(level=0, name='Break')['Break']
)
print(df)
结果:
Key StartDate EndDate Break
0 A 2015-01-01 2015-12-30 NaT
1 A 2016-01-01 2016-05-31 2 days
2 A 2016-06-01 2017-09-30 1 days
3 A 2017-10-01 2018-12-31 1 days
4 B 2015-01-01 2015-12-30 NaT
5 B 2016-01-01 2016-05-31 2 days
6 B 2016-07-15 2017-09-30 45 days
7 B 2017-10-01 2018-12-31 1 days
找到突破点高于我们要求的截止点的位置:
cutoff = pd.Timedelta('3 days')
df['Break_above_cutoff'] = df['Break'] > cutoff
print(df)
结果:
Key StartDate EndDate Break Break_above_cutoff
0 A 2015-01-01 2015-12-30 NaT False
1 A 2016-01-01 2016-05-31 2 days False
2 A 2016-06-01 2017-09-30 1 days False
3 A 2017-10-01 2018-12-31 1 days False
4 B 2015-01-01 2015-12-30 NaT False
5 B 2016-01-01 2016-05-31 2 days False
6 B 2016-07-15 2017-09-30 45 days True
7 B 2017-10-01 2018-12-31 1 days False
然后我定义这个函数来查找从
列中包含 true 的最后一行:
def get_after_last_true(df, colname):
"""Gets the portion of the dataframe starting from the last occurance of
True in colname"""
idx = np.where(df[colname])[0]
if len(idx) > 0:
return df.iloc[idx[-1]:]
else:
return df
将其应用于组:
trimmed = (df.groupby('Key')
.apply(lambda d: get_after_last_true(d, 'Break_above_cutoff'))
.reset_index(drop=True)
)
print(trimmed)
结果:
Key StartDate EndDate Break Break_above_cutoff
0 A 2015-01-01 2015-12-30 NaT False
1 A 2016-01-01 2016-05-31 2 days False
2 A 2016-06-01 2017-09-30 1 days False
3 A 2017-10-01 2018-12-31 1 days False
4 B 2016-07-15 2017-09-30 45 days True
5 B 2017-10-01 2018-12-31 1 days False
然后只需使用 groupby-apply 获取 EndDate 的最大值和 StartDate 的最小值的元组
result = trimmed.groupby('Key').apply(
lambda df: (df['StartDate'].min(), df['EndDate'].max())
)
print(result)
结果:
Key
A (2015-01-01 00:00:00, 2018-12-31 00:00:00)
B (2016-07-15 00:00:00, 2018-12-31 00:00:00)
dtype: object
我有一个数据框,其中包含一些记录的开始和结束日期:
import pandas as pd
df = pd.DataFrame({'Key': ['A', 'A', 'A', 'A', 'B', 'B', 'B', 'B' ],
'StartDate': ['01/01/2015', '01/01/2016', '06/01/2016','10/01/2017',
'01/01/2015', '01/01/2016', '07/15/2016','10/01/2017'],
'EndDate': ['12/30/2015', '05/31/2016', '09/30/2017', '12/31/2018',
'12/30/2015', '05/31/2016', '09/30/2017', '12/31/2018']})
df = df[['Key', 'StartDate', 'EndDate']]
print(df)
我的输出如下所示:
Key StartDate EndDate
0 A 01/01/2015 12/30/2015
1 A 01/01/2016 05/31/2016
2 A 06/01/2016 09/30/2017
3 A 10/01/2017 12/31/2018
4 B 01/01/2015 12/30/2015
5 B 01/01/2016 05/31/2016
6 B 07/15/2016 09/30/2017
7 B 10/01/2017 12/31/2018
我需要知道每个密钥的最早开始日期和最晚结束日期。 我这样做了(如果有更好的方法,请告诉我):
df_start = df.groupby('Key')['StartDate'].min().reset_index(name = 'StartDate')
df_end = df.groupby('Key')['EndDate'].max().reset_index(name = 'EndDate')
final = pd.merge(df_start, df_end, on = 'Key', how = 'left')
print(final)
这给了我这个输出:
Key StartDate EndDate
0 A 01/01/2015 12/31/2018
1 B 01/01/2015 12/31/2018
现在,如果您查看原始数据框中的键 "B",您会看到第 5 行的结束日期是 05/31/2016,第 6 行的开始日期是 07/15 /2016,所以这些记录不是连续的。有 1.5 个月的日期中断。如果日期中断超过 3 天,我只需要 return 连续记录的最早开始日期,因此在这种情况下,所需的输出将是:
Key StartDate EndDate
0 A 01/01/2015 12/31/2018
1 B 07/15/2016 12/31/2018
我一直在尝试使用 'shift' 方法来计算每一行的开始日期和前一行的结束日期之间的天数,但不确定我是否要进入完全正确的方向..或者我应该遍历行?我的数据框中有数十万条记录。
完成此任务的最有效方法是什么?谢谢。
好的,您需要为定义的连续记录创建一个标记,然后分组并删除重复项:
df['StartDate'] = pd.to_datetime(df['StartDate'])
df['EndDate'] = pd.to_datetime(df['EndDate'])
consec = (df.groupby('Key').apply(lambda x: x.StartDate - x.EndDate.shift(1) >= pd.Timedelta('3 day'))
.cumsum().reset_index(drop=True))
(df.groupby(['Key',consec])
.agg({'StartDate':'min','EndDate':'max'})
.reset_index()
.drop_duplicates('Key', keep='last')
.drop('level_1', axis=1))
输出:
Key StartDate EndDate
0 A 2015-01-01 2018-12-31
2 B 2016-07-15 2018-12-31
我绝不是 Pandas 专家,但我想我有一些东西可以满足您的需求。首先,我将日期转换为日期时间:
df['StartDate'] = pd.to_datetime(df['StartDate'], infer_datetime_format=True)
df['EndDate'] = pd.to_datetime(df['EndDate'], infer_datetime_format=True)
print(df)
结果:
Key StartDate EndDate
0 A 2015-01-01 2015-12-30
1 A 2016-01-01 2016-05-31
2 A 2016-06-01 2017-09-30
3 A 2017-10-01 2018-12-31
4 B 2015-01-01 2015-12-30
5 B 2016-01-01 2016-05-31
6 B 2016-07-15 2017-09-30
7 B 2017-10-01 2018-12-31
然后确定每个组中结束日期和开始日期之间的时间量:
df['Break'] = (df.groupby('Key')
.apply(lambda d: d['StartDate'] - d['EndDate'].shift(1))
.reset_index(level=0, name='Break')['Break']
)
print(df)
结果:
Key StartDate EndDate Break
0 A 2015-01-01 2015-12-30 NaT
1 A 2016-01-01 2016-05-31 2 days
2 A 2016-06-01 2017-09-30 1 days
3 A 2017-10-01 2018-12-31 1 days
4 B 2015-01-01 2015-12-30 NaT
5 B 2016-01-01 2016-05-31 2 days
6 B 2016-07-15 2017-09-30 45 days
7 B 2017-10-01 2018-12-31 1 days
找到突破点高于我们要求的截止点的位置:
cutoff = pd.Timedelta('3 days')
df['Break_above_cutoff'] = df['Break'] > cutoff
print(df)
结果:
Key StartDate EndDate Break Break_above_cutoff
0 A 2015-01-01 2015-12-30 NaT False
1 A 2016-01-01 2016-05-31 2 days False
2 A 2016-06-01 2017-09-30 1 days False
3 A 2017-10-01 2018-12-31 1 days False
4 B 2015-01-01 2015-12-30 NaT False
5 B 2016-01-01 2016-05-31 2 days False
6 B 2016-07-15 2017-09-30 45 days True
7 B 2017-10-01 2018-12-31 1 days False
然后我定义这个函数来查找从 列中包含 true 的最后一行:
def get_after_last_true(df, colname):
"""Gets the portion of the dataframe starting from the last occurance of
True in colname"""
idx = np.where(df[colname])[0]
if len(idx) > 0:
return df.iloc[idx[-1]:]
else:
return df
将其应用于组:
trimmed = (df.groupby('Key')
.apply(lambda d: get_after_last_true(d, 'Break_above_cutoff'))
.reset_index(drop=True)
)
print(trimmed)
结果:
Key StartDate EndDate Break Break_above_cutoff
0 A 2015-01-01 2015-12-30 NaT False
1 A 2016-01-01 2016-05-31 2 days False
2 A 2016-06-01 2017-09-30 1 days False
3 A 2017-10-01 2018-12-31 1 days False
4 B 2016-07-15 2017-09-30 45 days True
5 B 2017-10-01 2018-12-31 1 days False
然后只需使用 groupby-apply 获取 EndDate 的最大值和 StartDate 的最小值的元组
result = trimmed.groupby('Key').apply(
lambda df: (df['StartDate'].min(), df['EndDate'].max())
)
print(result)
结果:
Key
A (2015-01-01 00:00:00, 2018-12-31 00:00:00)
B (2016-07-15 00:00:00, 2018-12-31 00:00:00)
dtype: object