Группировка и ссылки на сдвинутые значения - PullRequest
0 голосов
/ 28 апреля 2018

Я пытаюсь отслеживать уровни запасов отдельных предметов с течением времени Сравнение прогнозируемого исходящего и доступности. Есть времена в который прогнозируемый исходящий превышает доступность и когда это происходит, я хочу, чтобы Post Available было 0. Я пытаюсь создать Pre Available и Post Available столбцы ниже:

 Item  Week  Inbound  Outbound  Pre Available  Post Available 
 A        1      500       200            500             300 
 A        2        0       400            300               0 
 A        3      100         0            100             100 
 B        1       50        50             50               0 
 B        2        0        80              0               0 
 B        3        0        20              0               0 
 B        4       20        20             20               0 

Я попробовал следующий код:

def custsum(x):

      total = 0
      for i, v in x.iterrows():
         total += df['Inbound'] - df['Outbound']
         x.loc[i, 'Post Available'] = total
         if total < 0:
            total = 0
      return x

df.groupby('Item').apply(custsum)

Но я получаю следующее сообщение об ошибке:

ValueError: Incompatible indexer with Series

Я относительный новичок в Python, поэтому любая помощь будет принята с благодарностью. Спасибо!

Ответы [ 2 ]

0 голосов
/ 29 апреля 2018

Вы можете использовать

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']

- это серия.

0 голосов
/ 28 апреля 2018

Нет необходимости в функции самоопределения, вы можете использовать groupby + shift для создания PreAvailable и использовать clip (установка нижней границы как 0) для PostAvailable

df['PostAvailable']=(df.Inbound-df.Outbound).clip(lower=0)
df['PreAvailable']=df.groupby('item').apply(lambda x  : x['Inbound'].add(x['PostAvailable'].shift(),fill_value=0)).values
df
Out[213]: 
  item  Week  Inbound  Outbound  PreAvailable  PostAvailable
0    A     1      500       200         500.0            300
1    A     2        0       400         300.0              0
2    A     3      100         0         100.0            100
3    B     1       50        50          50.0              0
4    B     2        0        80           0.0              0
5    B     3        0        20           0.0              0
6    B     4       20        20          20.0              0
...