2015-02-23 54 views
14

問題計算新值:基於遞減值

什麼了我喜歡做的是一步一步連續遞減的基數減少在Series的值。

我不知道的術語這個 - 我認爲我可以做一些與cumsumdiff,但我覺得我自己領先於白費力氣那裏......

啓動代碼:

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 availa ble,所以這又變成0,我們還有5離開。
  • 下一個值是25 - 我們只有5左邊,所以這變成20,現在我們沒有進一步的津貼。
  • 下一個值是30,並且由於沒有餘量,所以該值仍然爲30
+0

我會將'values'變量重命名爲'expenses'和'desired'變量''debts',它與'allowance'結合使讀者明白你想要完成什麼,甚至沒有看文本,海事組織。 – mucaho 2015-02-23 22:44:45

回答

10

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) 

這與填充第一NaN值「第一值 - 津貼」值。因此,在ALLOWANCE降至75的情況下,返回desiredSeries([10, 10, 25, 30])

+0

這似乎並沒有處理'系列'>'ALLOWANCE'的第一個元素:( – 2015-02-23 15:43:43

+0

@JonClements你只需要追加'.fillna(0)' – EdChum 2015-02-23 15:45:35

+0

@EdChum不能這樣做 - 想想我需要使用類似Carsten的答案,如果系列中的第一個值保持爲「85」,並且「允許」爲70,結果爲「0」 - 這是不正確的 - 應該是'15' – 2015-02-23 15:47:46

1

應該有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 
+2

謝謝。雖然這將起作用,但我也計劃在'pandas'中執行其他操作 - 所以如果可能的話,我真的會在一個基於pandas的解決方案之後。 – 2015-02-23 15:25:50

5

這可能不是那麼高性能,但目前這是做這個用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 
+0

有可能有更好的方法來重寫我的功能,我不是一個Python專家,我認爲這可以重寫使用一個生成器,但由於某種原因它不工作。理想情況下,如果補貼已經爲0,並且返回傳入的行值,我會將此短路。 – EdChum 2015-02-23 15:32:40

+0

當然,它指出我看起來像正確的方向,並給了我一些想法......非常感謝 - 閱讀'rolling_apply '現在 – 2015-02-23 15:33:58

8

您的想法與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))