根据递减值计算新值

Calculate new value based on decreasing value

问题:

我想做的是逐步将 Series 中的值按连续递减的基数递减。

我不确定这个的术语 - 我确实认为我可以用 cumsumdiff 做一些事情,但我想我正在带领自己去追逐白鹅。 ..

起始码:

import pandas as pd

ALLOWANCE = 100
values = pd.Series([85, 10, 25, 30])

期望输出:

desired = pd.Series([0, 0, 20, 30])

理由:

ALLOWANCE 的基数开始 - Series 中的每个值都减去剩余的数量,津贴本身也是如此,因此发生以下步骤:

它应该与 while 循环一起工作:

ii = 0
while (ALLOWANCE > 0 and ii < len(values)):
    if (ALLOWANCE > values[ii]):
        ALLOWANCE -= values[ii]
        values[ii] = 0
    else:
        values[ii] -= ALLOWANCE
        ALLOWANCE = 0
    ii += 1 

这可能不是那么高效,但目前这是使用 rolling_apply:

的 Pandas 方法
In [53]:

ALLOWANCE = 100
def reduce(x):
    global ALLOWANCE
    # short circuit if we've already reached 0
    if ALLOWANCE == 0:
        return x
    val = max(0, x - ALLOWANCE)
    ALLOWANCE = max(0, ALLOWANCE - x)
    return val

pd.rolling_apply(values, window=1, func=reduce)
Out[53]:
0     0
1     0
2    20
3    30
dtype: float64

或者更简单地说:

In [58]:

values.apply(reduce)
Out[58]:
0     0
1     0
2    20
3    30
dtype: int64

您对 cumsumdiff 的想法可行。看起来并不太复杂;不确定是否有更短的解决方案。首先,我们计算累加和,对其进行运算,然后返回(diff 有点像 cumsum 的反函数)。

import math

c = values.cumsum() - ALLOWANCE
# now we've got [-15, -5, 20, 50]
c[c < 0] = 0 # negative values don't make sense here

# (c - c.shift(1)) # <-- what I had first: diff by accident

# it is important that we don't fill with 0, in case that the first
# value is greater than ALLOWANCE
c.diff().fillna(math.max(0, values[0] - ALLOWANCE))

按照您对 cumsumdiff 的最初想法,您可以写成:

>>> (values.cumsum() - ALLOWANCE).clip_lower(0).diff().fillna(0)
0     0
1     0
2    20
3    30
dtype: float64

这是values减去津贴的累计总和。负值被截断为零(因为我们不关心数字,直到我们透支了我们的津贴)。从那里,您可以计算出差异。

但是,如果第一个值可能大于允许值,则首选以下两行变化:

s = (values.cumsum() - ALLOWANCE).clip_lower(0)
desired = s.diff().fillna(s)

这会用 "first value - allowance" 值填充第一个 NaN 值。所以在 ALLOWANCE 降低到 75 的情况下,它 returns desiredSeries([10, 10, 25, 30]).