Python 一段时间内的Groupby
我有一个表,其中包含ID、日期、目标(可能是多类的,但现在是二进制的,其中1是失败的)和基于日期列的yearmonth列。以下是此表的前8行: 一行 身份证件 日期 目标 年月 0 A. 2015-03-16 0 2015-03 1. A. 2015-05-29 1. 2015-05 2. A. 2015-08-02 1. 2015-08 3. A. 2015-09-05 1. 2015-09 4. A. 2015-09-22 0 2015-09 5. A. 2015-10-15 1. 2015-10 6. A. 2015-11-09 1. 2015-11 7. B 2015-04-17 0 2015-04Python 一段时间内的Groupby,python,pandas,pandas-groupby,Python,Pandas,Pandas Groupby,我有一个表,其中包含ID、日期、目标(可能是多类的,但现在是二进制的,其中1是失败的)和基于日期列的yearmonth列。以下是此表的前8行: 一行 身份证件 日期 目标 年月 0 A. 2015-03-16 0 2015-03 1. A. 2015-05-29 1. 2015-05 2. A. 2015-08-02 1. 2015-08 3. A. 2015-09-05 1. 2015-09 4. A. 2015-09-22 0 2015-09 5. A. 2015-10-15 1. 2015
您可以通过在
'id'
上将数据帧与其自身合并来实现这一点
首先,我们将创建一个月的第一个“fom”
列,因为您的日期逻辑希望基于前几个月而不是具体的日期进行回顾。然后,我们将数据帧与自身合并,带上索引,以便我们最终可以分配回结果
使用月偏移量,我们可以将其过滤为仅在该行观察的3个月内保留观察值,然后我们groupby
原始索引,并取'target'
的平均值以获得失败百分比,我们可以将其分配回(索引对齐)
如果输出中有NaN
,那是因为该行在前3个月内没有观察到,因此无法计算
#df['date'] = pd.to_datetime(df['date'], dayfirst = True)
df['fom'] = df['date'].astype('datetime64[M]') # Credit @anky
df1 = df.reset_index()
df1 = (df1.drop(columns='target').merge(df1, on='id', suffixes=['', '_past']))
df1 = df1[df1.fom_past.between(df1.fom-pd.offsets.DateOffset(months=3),
df1.fom-pd.offsets.DateOffset(months=1))]
df['Pct_fail'] = df1.groupby('index').target.mean()*100
如果您的内存有问题,我们可以采用非常慢的循环方法,为每一行划分子集,然后计算该子集的平均值
def get_prev_avg(row, df):
df = df[df['id'].eq(row['id'])
& df['fom'].between(row['fom']-pd.offsets.DateOffset(months=3),
row['fom']-pd.offsets.DateOffset(months=1))]
if not df.empty:
return df['target'].mean()*100
else:
return np.NaN
#df['date'] = pd.to_datetime(df['date'], dayfirst = True)
df['fom'] = df['date'].astype('datetime64[M]')
df['Pct_fail'] = df.apply(lambda row: get_prev_avg(row, df), axis=1)
我修改了@ALollz代码,以便它更好地应用于我的原始数据集,其中我有一个多类目标,我希望获得类1和2的PctFails,加上事务的nr,我需要在不同的时间段按不同的列进行分组。此外,决定使用日期之前的最后x个月比使用日历月更简单、更好。所以我的解决方案是:
df = pd.DataFrame({'Id':['A','A','A','A','A','A','A','B'],'Type':['T1','T3','T1','T2','T2','T1','T1','T3'],'date' :['2015-03-16','2015-05-29','2015-08-10','2015-09-05','2015-09-22','2015-11-08','2015-11-09','2015-04-17'],'target':[2,1,2,1,0,1,2,0]} )
df['date'] = pd.to_datetime(df['date'], dayfirst = True)
def get_prev_avg(row, df, columnname, lastxmonths):
df = df[df[columnname].eq(row[columnname])
& df['date'].between(row['date']-pd.offsets.DateOffset(months=lastxmonths),
row['date']-pd.offsets.DateOffset(days=1))]
if not df.empty:
NrTransactions= len(df['target'])
PctMinorFails= (df['target'].where(df['target'] == 1).count())/len(df['target'])*100
PctMajorFails= (df['target'].where(df['target'] == 2).count())/len(df['target'])*100
return pd.Series([NrTransactions, PctMinorFails, PctMajorFails])
else:
return pd.Series([np.NaN, np.NaN, np.NaN])
for lastxmonths in [3, 4]:
for columnname in ['Id','Type']:
df[['NrTransactionsBy' + str(columnname) + 'Last' + str(lastxmonths) +'Months',
'PctMinorFailsBy' + str(columnname) + 'Last' + str(lastxmonths) +'Months',
'PctMajorFailsBy' + str(columnname) + 'Last' + str(lastxmonths) +'Months'
]]= df.apply(lambda row: get_prev_avg(row, df, columnname, lastxmonths), axis=1)
我的原始数据集每次迭代都需要几个小时,虽然不是很好,但不确定如何进一步优化它。Perfect,这正是我想要的,谢谢@Alolz。关闭,使其成为基于不同回望期的函数!谢谢again@Sam是的,这应该不会太糟糕,你基本上可以传入3个参数,一个用于df,然后另外两个用于lookback窗口(即在本例中为3和1,或者如果你总是希望回溯在前一个月结束,那么就只传入一个用于3的参数)。然后返回
df1.groupby('index').target.mean()*100
,这样当我返回到我的原始df(顺便说一句,它有大约120万行)时,您可以将其分配回,不幸的是,由于合并本身,我得到了一个内存错误:“无法为数组分配852.GiB(114287601070,)和数据类型int64@Sam哇,那么你的ID中肯定有很多重复。因为合并在ID
中,你可以尝试通过每个ID分别进行上述操作,这样你就可以处理更小的部分。但是如果这仍然是一个问题(比如说10k行合并10k行),可能需要重新考虑行上的循环(非常慢)。我认为主要的问题是行可以属于多个组,因此groupby
并不理想,滚动也不会喜欢非单调的日期列,因此非常棘手。谢谢,你是这样一个数据向导。你的get\u prev\u avg函数最终花费了将近3小时(160分钟)对于我的数据集来说,这并不理想,但也不是一个交易破坏者。然而,我将在一个新的答案中发布一些缺失的内容。
df = pd.DataFrame({'Id':['A','A','A','A','A','A','A','B'],'Type':['T1','T3','T1','T2','T2','T1','T1','T3'],'date' :['2015-03-16','2015-05-29','2015-08-10','2015-09-05','2015-09-22','2015-11-08','2015-11-09','2015-04-17'],'target':[2,1,2,1,0,1,2,0]} )
df['date'] = pd.to_datetime(df['date'], dayfirst = True)
def get_prev_avg(row, df, columnname, lastxmonths):
df = df[df[columnname].eq(row[columnname])
& df['date'].between(row['date']-pd.offsets.DateOffset(months=lastxmonths),
row['date']-pd.offsets.DateOffset(days=1))]
if not df.empty:
NrTransactions= len(df['target'])
PctMinorFails= (df['target'].where(df['target'] == 1).count())/len(df['target'])*100
PctMajorFails= (df['target'].where(df['target'] == 2).count())/len(df['target'])*100
return pd.Series([NrTransactions, PctMinorFails, PctMajorFails])
else:
return pd.Series([np.NaN, np.NaN, np.NaN])
for lastxmonths in [3, 4]:
for columnname in ['Id','Type']:
df[['NrTransactionsBy' + str(columnname) + 'Last' + str(lastxmonths) +'Months',
'PctMinorFailsBy' + str(columnname) + 'Last' + str(lastxmonths) +'Months',
'PctMajorFailsBy' + str(columnname) + 'Last' + str(lastxmonths) +'Months'
]]= df.apply(lambda row: get_prev_avg(row, df, columnname, lastxmonths), axis=1)