There is a whole sub-genre of questions on SO involving cumulative operations with limits (e.g.: "reset to zero when the cumulative sum becomes negative"). This is not the same as cumulative operation with known reset points (e.g. from another column, or where there are NaNs, etc.) because the condition involves the cumulative value itself.
There is no clean way in the current pandas or numpy to do that in a vectorized way.
The best (fastest) way I am aware of for this is this SO answer involving numba
. Slightly modified and adapted to your problem:
from numba import njit
@njit
def poscumsum(x):
total = 0
result = np.empty(x.shape)
for i, y in enumerate(x):
total += y
if total < 0:
total = 0
result[i] = total
return result
With this, you can do:
a = df1.set_index(['Code', 'Date'])
b = df2.set_index(['Code', 'Date'])
idx = a.index.union(b.index).sort_values()
df3 = (a.reindex(idx, fill_value=0) - b.reindex(idx, fill_value=0))
# optional: resample Date to daily within each group:
df3 = df3.groupby('Code').resample('D', level='Date').sum()
df3['Quantity'] = df3.groupby('Code')['Quantity'].transform(
lambda g: poscumsum(g.values))
On the data provided in the question:
>>> df3
Quantity
Code Date
A 2019-01-10 20
2019-01-11 17
2019-01-12 0
2019-01-13 10
2019-01-14 10
2019-01-15 9
2019-01-16 7
If you prefer, you can also use merge. Here is an example where all the intermediary results are kept (for forensic analysis):
df3 = df1.merge(df2, on=['Code', 'Date'], how='outer', sort=True).fillna(0)
# optional: resample Date to daily within each group:
df3 = df3.set_index(['Code', 'Date']).groupby('Code').resample('D', level='Date').sum()
df3['diff'] = df3['Quantity_x'] - df3['Quantity_y']
df3['cumdiff'] = df3.groupby('Code')['diff'].transform(
lambda g: poscumsum(g.values))
df3
# out:
Quantity_x Quantity_y diff cumdiff
Code Date
A 2019-01-10 20.0 0.0 20.0 20.0
2019-01-11 2.0 5.0 -3.0 17.0
2019-01-12 4.0 100.0 -96.0 0.0
2019-01-13 10.0 0.0 10.0 10.0
2019-01-14 0.0 0.0 0.0 10.0
2019-01-15 0.0 1.0 -1.0 9.0
2019-01-16 0.0 2.0 -2.0 7.0
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…