我有下表:
id | invoice_date | payment_date
--------------------------------
1 | 2017-03-01 | 2017-03-03
1 | 2017-03-04 | 2017-03-06
1 | 2017-03-04 | 2017-03-11
1 | 2017-03-15 | 2017-03-16
1 | 2017-03-21 | 2017-03-31
2 | 2017-01-22 | 2017-01-22
2 | 2017-01-24 | 2017-01-25
我想知道:对于表中的任何给定索引,索引较少的付款日期少于索引的发票日期?也就是说,对于给定的发票日期,提前支付的付款日期是多少?理想情况下,我想为每个id(分组)执行此操作,因此我有类似的内容:
id | invoice_date | payment_date | num_pay_dates_less_than_inv_date
------------------------------------------------------------------
1 | 2017-03-01 | 2017-03-03 | 0
1 | 2017-03-04 | 2017-03-06 | 1
1 | 2017-03-04 | 2017-03-11 | 1
1 | 2017-03-15 | 2017-03-16 | 3
1 | 2017-03-21 | 2017-03-31 | 4
2 | 2017-01-22 | 2017-01-22 | 0
2 | 2017-01-24 | 2017-01-25 | 1
答案 0 :(得分:2)
Numpy解决方案。使用广播比较,然后沿第一轴进行cumsum
操作。最后,提取对角元素并分配给df
。
v = (df.invoice_date[:, None] > df.payment_date.values).cumsum(1)
df['num_pay_dates_less_than_inv_date'] = v[np.diag_indices_from(v)]
df
id invoice_date payment_date num_pay_dates_less_than_inv_date
0 1 2017-03-01 2017-03-03 0
1 1 2017-03-04 2017-03-06 1
2 1 2017-03-04 2017-03-11 1
3 1 2017-03-15 2017-03-16 3
4 1 2017-03-21 2017-03-31 4
5 2 2017-01-22 2017-01-22 0
6 2 2017-01-24 2017-01-25 1
这要求两列都采用日期时间格式(否则,比较是词典编纂,通常没问题,但这取决于您当时的日期格式)。
答案 1 :(得分:0)
我能够使用@COLDSPEEDs建议在他/她的回答中写一个循环。在大小为50K的数据帧上,循环实现比非循环实现快20倍。数据框的列是id,value1和value2。
def f(grp):
dxv = {}
for ix, val in zip(grp.index, grp.value1[:, None]):
if (val > grp.value2.values[:ix]).any():
dxv[ix] = (val > grp.value2.values[:ix]).cumsum()[-1]
else:
dxv[ix] = 0
return pd.Series(dxv)
这适用于我的案例:
df['num_pay_dates_less_than_inv_date'] = \
df.groupby(['id']).apply(f).reset_index(drop=True)
随意进一步优化。