2016-11-21 6 views
2

Я пытаюсь векторизовать цикл for в pandas для повышения производительности. У меня есть набор данных, включающий пользователей, продукты, дату каждой службы, а также количество предоставленных дней. Учитывая следующее подмножество данных:Pandas: векторизация условной совокупной суммы

testdf = pd.DataFrame(data={"USERID": ["A"] * 6, 
          "PRODUCTID": [1] * 6, 
          "SERVICEDATE": [datetime(2016, 1, 1), datetime(
           2016, 2, 5), 
          datetime(2016, 2, 28), datetime(2016, 3, 25), 
          datetime(2016, 4, 30), datetime(2016, 5, 30)], 
          "DAYSSUPPLY": [30] * 6}) 

testdf=testdf.set_index(["USERID", "PRODUCTID"]) 
testdf["datediff"] = testdf["SERVICEDATE"].diff() 
testdf.loc[testdf["datediff"].notnull(), "datediff"] = testdf.loc[ 
    testdf["datediff"].notnull(), "datediff"].apply(lambda x: x.days) 
testdf["datediff"] = testdf["datediff"].fillna(0) 
testdf["datediff"] = pd.to_numeric(testdf["datediff"]) 
testdf["over_under"] = testdf["DAYSSUPPLY"].shift() - testdf["datediff"] 

Я хотел бы получить следующий результат:

    DAYSSUPPLY SERVICEDATE datediff over_under desired 
USERID PRODUCTID              
A  1     30 2016-01-01   0   NaN  0 
     1     30 2016-02-05  35  -5.0  0 
     1     30 2016-02-28  23   7.0  7 
     1     30 2016-03-25  26   4.0  11 
     1     30 2016-04-30  36  -6.0  5 
     1     30 2016-05-30  30   0.0  5 

По существу, я хочу, чтобы мой нужный столбец будет бегущая сумма over_under, но только суммировать отрицательные значения, если значение желаемых на предыдущей линии> 0. желательно никогда не должен получить ниже 0. быстрые и грязные перебирает [пользователь, продукт] группа выглядит примерно так:

running_total = 0 
desired_loop = [] 
for row in testdf.itertuples(): 
    over_under=row[4] 
    # skip first row 
    if pd.isnull(over_under): 
     desired_loop.append(0) 
     continue 
    running_total += over_under 
    running_total = max(running_total, 0) 
    desired_loop.append(running_total) 
testdf["desired_loop"] = desired_loop 

        desired_loop 
USERID PRODUCTID    
A  1     0.0 
     1     0.0 
     1     7.0 
     1     11.0 
     1     5.0 
     1     5.0 

Я улица плохо нового для векторизации, панд и вообще. Я смог векторизовать каждый другой расчет в этом df, но этот частный случай кумулятивной суммы я просто не могу понять, как это сделать.

Спасибо!

ответ

0

У меня была аналогичная проблема и она была решена с использованием несколько нетрадиционной итерации.

testdf["desired"] = testdf["over_under"].cumsum() 
current = np.argmax(testdf["desired"] < 0) 
while current != 0: 
    testdf.loc[current:,"desired"] += testdf["desired"][current] # adjust the cumsum going forward 
    # the previous statement also implicitly sets 
    # testdf.loc[current, "desired"] = 0 
    current = np.argmax(testdf["desired"][current:] < 0) 

По сути, вы находите все «события» и корректируете работу cumsum с течением времени. Все операции манипуляции и тестирования векторизованы, поэтому, если ваш столбец desired не слишком часто пересекает негатив, вы должны быть довольно быстрыми.

Это определенно взломать, но это заработало для меня.