根据递减值计算新值
Calculate new value based on decreasing value
问题:
我想做的是逐步将 Series
中的值按连续递减的基数递减。
我不确定这个的术语 - 我确实认为我可以用 cumsum
和 diff
做一些事情,但我想我正在带领自己去追逐白鹅。 ..
起始码:
import pandas as pd
ALLOWANCE = 100
values = pd.Series([85, 10, 25, 30])
期望输出:
desired = pd.Series([0, 0, 20, 30])
理由:
从 ALLOWANCE
的基数开始 - Series
中的每个值都减去剩余的数量,津贴本身也是如此,因此发生以下步骤:
- 从 100 开始,我们可以完全删除
85
所以它变成 0
,我们现在 15
剩下 ALLOWANCE
- 下一个值是
10
,我们还有 15
可用,所以这又变成了 0
,我们还剩下 5
。
- 下一个值是
25
- 我们只剩下 5
,所以这变成了 20
,现在我们没有更多余量了。
- 下一个值为
30
,由于没有余量,该值仍为 30
。
它应该与 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
您对 cumsum
和 diff
的想法可行。看起来并不太复杂;不确定是否有更短的解决方案。首先,我们计算累加和,对其进行运算,然后返回(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))
按照您对 cumsum
和 diff
的最初想法,您可以写成:
>>> (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 desired
为 Series([10, 10, 25, 30])
.
问题:
我想做的是逐步将 Series
中的值按连续递减的基数递减。
我不确定这个的术语 - 我确实认为我可以用 cumsum
和 diff
做一些事情,但我想我正在带领自己去追逐白鹅。 ..
起始码:
import pandas as pd
ALLOWANCE = 100
values = pd.Series([85, 10, 25, 30])
期望输出:
desired = pd.Series([0, 0, 20, 30])
理由:
从 ALLOWANCE
的基数开始 - Series
中的每个值都减去剩余的数量,津贴本身也是如此,因此发生以下步骤:
- 从 100 开始,我们可以完全删除
85
所以它变成0
,我们现在15
剩下ALLOWANCE
- 下一个值是
10
,我们还有15
可用,所以这又变成了0
,我们还剩下5
。 - 下一个值是
25
- 我们只剩下5
,所以这变成了20
,现在我们没有更多余量了。 - 下一个值为
30
,由于没有余量,该值仍为30
。
它应该与 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
:
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
您对 cumsum
和 diff
的想法可行。看起来并不太复杂;不确定是否有更短的解决方案。首先,我们计算累加和,对其进行运算,然后返回(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))
按照您对 cumsum
和 diff
的最初想法,您可以写成:
>>> (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 desired
为 Series([10, 10, 25, 30])
.