Вы можете использовать
import numpy as np
import pandas as pd
df = pd.DataFrame({'Inbound': [500, 0, 100, 50, 0, 0, 20],
'Item': ['A', 'A', 'A', 'B', 'B', 'B', 'B'],
'Outbound': [200, 400, 0, 50, 80, 20, 20],
'Week': [1, 2, 3, 1, 2, 3, 4]})
df = df[['Item', 'Week', 'Inbound', 'Outbound']]
def custsum(x):
total = 0
for i, v in x.iterrows():
total += x.loc[i, 'Inbound'] - x.loc[i, 'Outbound']
if total < 0:
total = 0
x.loc[i, 'Post Available'] = total
x['Pre Available'] = x['Post Available'].shift(1).fillna(0) + x['Inbound']
return x
result = df.groupby('Item').apply(custsum)
result = result[['Item', 'Week', 'Inbound', 'Outbound', 'Pre Available', 'Post Available']]
print(result)
, что дает
Item Week Inbound Outbound Pre Available Post Available
0 A 1 500 200 500.0 300.0
1 A 2 0 400 300.0 0.0
2 A 3 100 0 100.0 100.0
3 B 1 50 50 50.0 0.0
4 B 2 0 80 0.0 0.0
5 B 3 0 20 0.0 0.0
6 B 4 20 20 20.0 0.0
Основное различие между этим кодом и кодом, который вы разместили:
total += x.loc[i, 'Inbound'] - x.loc[i, 'Outbound']
x.loc
используется для выбора числового значения в строке, индексированной i
и в
столбец Inbound
или Outbound
. Таким образом, разница числовая и total
остается числовым В отличие от
total += df['Inbound'] - df['Outbound']
добавляет всю серию к total
. Это приводит к ValueError
позже. (Подробнее о причинах этого см. Ниже).
Условный
if total < 0:
total = 0
было перемещено выше x.loc[i, 'Post Available'] = total
, чтобы Post
Available
всегда было неотрицательным.
Если вам не нужно это условие, тогда все for-loop
можно заменить на
x['Post Available'] = (df['Inbound'] - df.loc['Outbound']).cumsum()
А так как по столбцам арифметика и cumsum
являются векторизованными операциями, вычисление может быть выполнено намного быстрее.
К сожалению, условие не позволяет нам исключить for-loop
и векторизовать вычисления.
В исходном коде ошибка
ValueError: Incompatible indexer with Series
происходит в этой строке
x.loc[i, 'Post Available'] = total
потому что total
- это (иногда) серия, а не простое числовое значение. Панды это
попытка выровнять ряд с правой стороны с индексатором, (i, 'Post Available')
, с левой стороны. Индексатор (i, 'Post Available')
получает
преобразуется в кортеж как (0, 4)
, так как Post Available
является столбцом в
индекс 4. Но (0, 4)
не подходит для 1-мерной серии
на правой стороне.
Вы можете подтвердить total
Серия, поместив print(total)
в свой for-loop
,
или отметив, что правая часть
total += df['Inbound'] - df['Outbound']
- это серия.