条件覆盖的累积总和

问题描述 投票:0回答:1

我正在尝试计算一段时间内库存的余额(水平),并将进货和出货数量作为输入(以及每种库存类型的类别)。通常我会计算

incoming - outgoing
并结转到下一个期间(累计总和),但在这种情况下,一个额外的困难是余额可以在不同的时间点被覆盖,从而将余额“重置”为这些值(以及传入的余额) /支出需要从此时开始添加到这些覆盖中)。

我想出了一种计算方法,通过在存在超驰余额时抵消计算的余额(=累积和(传入-传出))(通过负计算的累积和;即,当存在超驰余额时将库存设置为 0),但是当不同时间有多个覆盖时,这不起作用。

这是我当前的方法,对于给定的数据帧效果很好(=每个类别(

bal
)只有一个覆盖(
cat
))。

>>> df = pd.DataFrame({
...   'cat': ['a', 'a', 'b', 'b', 'a', 'a', 'a', 'a', 'a', 'b'],
...   'time': [1, 2, 1, 2, 4, 5, 6, 7, 8, 9],
...   'in': [None, 10, None, None, None, 20, 11, 9, 10, None],
...   'out': [10, None, None, 20, 10, 5, None, 30, None, None],
...   'bal': [None, None, None, None, 50, None, None, None, None, None]
                                       ^ at this time, the balance should be set to 50, irrespective of prior `in` and `out`.
... })
>>> 
>>> # cumsum goes by row, so order matters
>>> df = df.sort_values(by=['time'])
>>> df
  cat  time    in   out   bal
0   a     1   NaN  10.0   NaN
2   b     1   NaN   NaN   NaN
1   a     2  10.0   NaN   NaN
3   b     2   NaN  20.0   NaN
4   a     4   NaN  10.0  50.0
5   a     5  20.0   5.0   NaN
6   a     6  11.0   NaN   NaN
7   a     7   9.0  30.0   NaN
8   a     8  10.0   NaN   NaN
9   b     9   NaN   NaN   NaN
>>> 
>>> 
>>> # Calculate the balance as if 'bal' (the override) wasn't there (cumsum(in - out))
>>> df['inout'] = df['in'].fillna(0) - df['out'].fillna(0)
>>> df['cumsum'] = df[['cat', 'inout']].groupby(['cat']).cumsum()
>>> df
  cat  time    in   out   bal  inout  cumsum
0   a     1   NaN  10.0   NaN  -10.0   -10.0
2   b     1   NaN   NaN   NaN    0.0     0.0
1   a     2  10.0   NaN   NaN   10.0     0.0
3   b     2   NaN  20.0   NaN  -20.0   -20.0
4   a     4   NaN  10.0  50.0  -10.0   -10.0   <-- we want to override this with the value from 'bal' (50) and continue the calculation
5   a     5  20.0   5.0   NaN   15.0     5.0
6   a     6  11.0   NaN   NaN   11.0    16.0
7   a     7   9.0  30.0   NaN  -21.0    -5.0
8   a     8  10.0   NaN   NaN   10.0     5.0
9   b     9   NaN   NaN   NaN    0.0   -20.0
>>> 
>>> # Find the positions where a balance would override the calculated balance
>>> df['correction'] = -df.loc[pd.notnull(df['bal']), 'cumsum']
>>> df
  cat  time    in   out   bal  inout  cumsum  correction
0   a     1   NaN  10.0   NaN  -10.0   -10.0         NaN
2   b     1   NaN   NaN   NaN    0.0     0.0         NaN
1   a     2  10.0   NaN   NaN   10.0     0.0         NaN
3   b     2   NaN  20.0   NaN  -20.0   -20.0         NaN
4   a     4   NaN  10.0  50.0  -10.0   -10.0        10.0
5   a     5  20.0   5.0   NaN   15.0     5.0         NaN
6   a     6  11.0   NaN   NaN   11.0    16.0         NaN
7   a     7   9.0  30.0   NaN  -21.0    -5.0         NaN
8   a     8  10.0   NaN   NaN   10.0     5.0         NaN
9   b     9   NaN   NaN   NaN    0.0   -20.0         NaN
>>> 
>>> 
>>> # Calculate with the corrected balance
>>> df['inout2'] = df['in'].fillna(0) - df['out'].fillna(0) + df['bal'].fillna(0) + df['correction'].fillna(0)
>>> df['cumsum2'] = df[['cat', 'inout2']].groupby(['cat']).cumsum()
>>> df
  cat  time    in   out   bal  inout  cumsum  correction  inout2  cumsum2
0   a     1   NaN  10.0   NaN  -10.0   -10.0         NaN   -10.0    -10.0
2   b     1   NaN   NaN   NaN    0.0     0.0         NaN     0.0      0.0
1   a     2  10.0   NaN   NaN   10.0     0.0         NaN    10.0      0.0
3   b     2   NaN  20.0   NaN  -20.0   -20.0         NaN   -20.0    -20.0
4   a     4   NaN  10.0  50.0  -10.0   -10.0        10.0    50.0     50.0 (override from 'bal')
5   a     5  20.0   5.0   NaN   15.0     5.0         NaN    15.0     65.0 <--- 50 (override) +15 (in-out)
6   a     6  11.0   NaN   NaN   11.0    16.0         NaN    11.0     76.0
7   a     7   9.0  30.0   NaN  -21.0    -5.0         NaN   -21.0     55.0
8   a     8  10.0   NaN   NaN   10.0     5.0         NaN    10.0     65.0
9   b     9   NaN   NaN   NaN    0.0   -20.0         NaN     0.0    -20.0
>>> 
>>> 
>>> df[df['cat'] == 'a']
  cat  time    in   out   bal  inout  cumsum  correction  inout2  cumsum2
0   a     1   NaN  10.0   NaN  -10.0   -10.0         NaN   -10.0    -10.0
1   a     2  10.0   NaN   NaN   10.0     0.0         NaN    10.0      0.0
4   a     4   NaN  10.0  50.0  -10.0   -10.0        10.0    50.0     50.0
5   a     5  20.0   5.0   NaN   15.0     5.0         NaN    15.0     65.0
6   a     6  11.0   NaN   NaN   11.0    16.0         NaN    11.0     76.0
7   a     7   9.0  30.0   NaN  -21.0    -5.0         NaN   -21.0     55.0
8   a     8  10.0   NaN   NaN   10.0     5.0         NaN    10.0     65.0
 

看起来不错。在指数 4 处,简单的余额计算被覆盖(之前为 -10,现在为 50,如预期),并按预期添加后续期间的流入流出流量。

但是,当我引入另一个覆盖时,上述算法就会中断。

df = pd.DataFrame({
  'cat': ['a', 'a', 'b', 'b', 'a', 'a', 'a', 'a', 'a', 'b'],
  'time': [1, 2, 1, 2, 4, 5, 6, 7, 8, 9],
  'in': [None, 10, None, None, None, 20, 11, 9, 10, None],
  'out': [10, None, None, 20, 10, 5, None, 30, None, None],
  'bal': [None, None, None, None, 50, None, None, 30, None, None]
  #                                                ^
})

... same pipeline as before

>>> df
  cat  time    in   out   bal  inout  cumsum  correction  inout2  cumsum2
0   a     1   NaN  10.0   NaN  -10.0   -10.0         NaN   -10.0    -10.0
2   b     1   NaN   NaN   NaN    0.0     0.0         NaN     0.0      0.0
1   a     2  10.0   NaN   NaN   10.0     0.0         NaN    10.0      0.0
3   b     2   NaN  20.0   NaN  -20.0   -20.0         NaN   -20.0    -20.0
4   a     4   NaN  10.0  50.0  -10.0   -10.0        10.0    50.0     50.0  # still ok
5   a     5  20.0   5.0   NaN   15.0     5.0         NaN    15.0     65.0
6   a     6  11.0   NaN   NaN   11.0    16.0         NaN    11.0     76.0
7   a     7   9.0  30.0  30.0  -21.0    -5.0         5.0    14.0     90.0  # expect 30
8   a     8  10.0   NaN   NaN   10.0     5.0         NaN    10.0    100.0  # expect 30 + 10 = 40
9   b     9   NaN   NaN   NaN    0.0   -20.0         NaN     0.0    -20.0

我想修改算法以保持使用

cumsum
(函数式)的简单性,但无法弄清楚如何继续。这几乎就像我需要一个条件累积和,当满足条件时(在本例中为
bal
中的值),它会替换中间值。然而,我宁愿计算另一个校正列(或修复现有的一个)并添加它(但我碰壁了,因为我可能看它太久了)。非常感谢任何帮助。

python pandas dataframe cumulative-sum
1个回答
0
投票

代码

cond = df['bal'].notna()
df['cumsum2'] = (
    df['in'].fillna(0).sub(df['out'].fillna(0)).mask(cond, df['bal'])
    .groupby([df['cat'], cond.groupby(df['cat']).cumsum()]).cumsum()
)

df
(你的第二个例子)

  cat  time    in   out   bal  cumsum2
0   a     1   NaN  10.0   NaN    -10.0
1   a     2  10.0   NaN   NaN      0.0
2   b     1   NaN   NaN   NaN      0.0
3   b     2   NaN  20.0   NaN    -20.0
4   a     4   NaN  10.0  50.0     50.0
5   a     5  20.0   5.0   NaN     65.0
6   a     6  11.0   NaN   NaN     76.0
7   a     7   9.0  30.0  30.0     30.0
8   a     8  10.0   NaN   NaN     40.0
9   b     9   NaN   NaN   NaN    -20.0
© www.soinside.com 2019 - 2024. All rights reserved.