Как изменить определенную функцию для расчета желаемого результата (Pandas)

Я пытаюсь вычислить следующий столбец «new_field» тройным циклом по столбцам «name», «val_id» и «fac_id» со следующими условиями.

1. В каждом цикле 'val_id', если 'product' == 'CL', то минимум из 'val_against' и 'our_val_amt', например. min( val_against (134), our_val_amt (424)) поэтому 'NEW FIELD' = 134. Также, если сумма new_field превышает "our_val_amt", вычтите ее из "our_val_amt". например для val_id "xx4", (200 + 300 + 50) = 550, что превышает our_val_amt = 510, поэтому NEW FILED = 510 - 500 (т.е. 200 + 300 после того, как эта сумма превысит our_val_amt) = 10.

2. Если продукт != 'CL' и находится в той же группе 'val_id'. Остаток нужно вычесть из 'our_val_amt' и вставить в 'new_field'. например, 'our_val_amt' (424) - из шага 1 (134) = 290. Это вставлено над 'NEW FIELD'.

Если [продукт] не имеет «CL», ему просто нужно распределить [our_val_amt] между каждым [val_id]. Например, val_id = 'xx7' our_val_amt = 700, это распространяется в первой строке (650), а затем остается 700 - 650 = 50, вставляется в следующую строку со следующим значением 0, как в примере.

3.Повторите шаги для val_id xx2. Расчет NEW FIELD для CL = 104 и XL = 472 - 104 = 368.

В настоящее время вывод работает правильно для «имя» - compx (строка 0–9) и начинает неправильно рассчитываться дальше. Я также не уверен, как работает этот код, поскольку я новичок в Pandas, и буду признателен, если кто-то может объяснить определенную функцию, как думает программа.

df = pd.DataFrame(data=[["compx","xx1","yy1",424,418,"XL"],["compx","xx1","yy2",424,134,"CL"],["compx","xx2","yy3",472,60,"DL"],["compx","xx2","yy4",472,104,"CL"], ["compx", "xx3", "yy5", 490, 50, "XL"], ["compx", "xx3", "yy6", 490, 500, "CL"], ["compx", "xx3", "yy7", 490, 200, "DL"], ["compx", "xx4", "yy8", 510, 200, "CL"], ["compx", "xx4", "yy9", 510, 300, "CL"], ["compx", "xx4", "yy10", 510, 50, "CL"], ["compy", "xx5", "yy11", 510, 200, "CL"], ["compy", "xx5", "yy12", 510, 300, "CL"], ["compy", "xx5", "yy12", 510, 50, "CL"], ["compy", "xx5", "yy13", 510, 30, "DL"], ["compz", "xx6", "yy14", 350, 200, "CL"], ["compz", "xx6", "yy15", 350, 100, "CL"], ["compz", "xx6", "yy16", 350, 50, "XL"], ["compz", "xx6", "yy17", 350, 50, "DL"], ["compz", "xx7", "yy18", 700, 650, "DL"], ["compz", "xx7", "yy19", 700, 200, "DL"], ["compz", "xx7", "yy20", 700, 400, "XL"] ], columns=["name","val_id","fac_id","our_val_amt","val_against","product"])
df


# Compute tuple of "our_val_amt", "val_against" and "product" for easy processing as one column. It is hard to process multiple columns with "transform()".
df["the_tuple"] = df[["our_val_amt", "val_against", "product"]].apply(tuple, axis=1)

def compute_new_field_for_cl(g):
  # df_g is a tuple ("our_val_amt", "val_against", "product") indexed as (0, 1, 2).
  df_g = g.apply(pd.Series)
  df_g["new_field"] = df_g.apply(lambda row: min(row[0], row[1]) if row[2] == "CL" else 0, axis=1)
  df_g["cumsum"] = df_g["new_field"].cumsum()
  df_g["new_field"] = df_g.apply(lambda row: 0 if row["cumsum"] > row[0] else row["new_field"], axis=1)
  df_g["max_cumsum"] = df_g["new_field"].cumsum()
  df_g["new_field"] = df_g.apply(lambda row: row[0] - row["max_cumsum"] if row["cumsum"] > row[0] else row["new_field"], axis=1)
  return df_g["new_field"]

# Apply above function and compute new field values for "CL".
df["new_field"] = df.groupby("val_id")[["the_tuple"]].transform(compute_new_field_for_cl)

# Re-compute tuple of "our_val_amt", "new_field" and "product".
df["the_tuple"] = df[["our_val_amt", "new_field", "product"]].apply(tuple, axis=1)

def compute_new_field_for_not_cl(g):
  # df_g is a tuple ("our_val_amt", "new_field", "product") indexed as (0, 1, 2).
  df_g = g.apply(pd.Series)
  result_sr = df_g.where(df_g[2] != "CL")[0] - df_g[df_g[2] == "CL"][1].sum()
  result_sr = result_sr.fillna(0) + df_g[1]
  return result_sr

# Apply above function and compute new field values for "CL".
df["new_field"] = df.groupby("val_id")[["the_tuple"]].transform(compute_new_field_for_not_cl)

df = df.drop("the_tuple", axis=1)
df

Попытка достижения набора данных и вывода new_field.

name    |val_id |fac_id     |   our_val_amt |   val_against |   product |   new_field
compx   |   xx1 |   yy1     |   424         |   418         |   XL      |   290
compx   |   xx1 |   yy2     |   424         |   134         |   CL      |   134
compx   |   xx2 |   yy3     |   472         |   60          |   DL      |   368
compx   |   xx2 |   yy4     |   472         |   104         |   CL      |   104
compx   |   xx3 |   yy5     |   490         |   50          |   XL      |   0
compx   |   xx3 |   yy6     |   490         |   500         |   CL      |   490
compx   |   xx3 |   yy7     |   490         |   200         |   DL      |   0
compx   |   xx4 |   yy8     |   510         |   200         |   CL      |   200
compx   |   xx4 |   yy9     |   510         |   300         |   CL      |   300
compx   |   xx4 |   yy10    |   510         |   50          |   CL      |   10
compy   |   xx5 |   yy11    |   510         |   200         |   CL      |   200
compy   |   xx5 |   yy12    |   510         |   300         |   CL      |   300
compy   |   xx5 |   yy12    |   510         |   50          |   CL      |   10
compy   |   xx5 |   yy13    |   510         |   30          |   DL      |   0
compz   |   xx6 |   yy14    |   350         |   200         |   CL      |   200
compz   |   xx6 |   yy15    |   350         |   100         |   CL      |   100
compz   |   xx6 |   yy16    |   350         |   50          |   XL      |   50
compz   |   xx6 |   yy17    |   350         |   50          |   DL      |   0
compz   |   xx7 |   yy18    |   700         |   650         |   DL      |   650
compz   |   xx7 |   yy19    |   700         |   200         |   DL      |   50
compz   |   xx7 |   yy20    |   700         |   400         |   XL      |   0

Набор данных и вывод new_field, который я сейчас получаю

name    |val_id |fac_id     |   our_val_amt |   val_against |   product |   new_field
compx   |   xx1 |   yy1     |   424         |   418         |   XL      |   290
compx   |   xx1 |   yy2     |   424         |   134         |   CL      |   134
compx   |   xx2 |   yy3     |   472         |   60          |   DL      |   368
compx   |   xx2 |   yy4     |   472         |   104         |   CL      |   104
compx   |   xx3 |   yy5     |   490         |   50          |   XL      |   0
compx   |   xx3 |   yy6     |   490         |   500         |   CL      |   490
compx   |   xx3 |   yy7     |   490         |   200         |   DL      |   0
compx   |   xx4 |   yy8     |   510         |   200         |   CL      |   200
compx   |   xx4 |   yy9     |   510         |   300         |   CL      |   300
compx   |   xx4 |   yy10    |   510         |   50          |   CL      |   10
compy   |   xx5 |   yy11    |   510         |   200         |   CL      |   200
compy   |   xx5 |   yy12    |   510         |   300         |   CL      |   300
compy   |   xx5 |   yy12    |   510         |   50          |   CL      |   10
compy   |   xx5 |   yy13    |   510         |   30          |   DL      |   10
compz   |   xx6 |   yy14    |   350         |   200         |   CL      |   200
compz   |   xx6 |   yy15    |   350         |   100         |   CL      |   100
compz   |   xx6 |   yy16    |   350         |   50          |   XL      |   50
compz   |   xx6 |   yy17    |   350         |   50          |   DL      |   50
compz   |   xx7 |   yy18    |   700         |   650         |   DL      |   700
compz   |   xx7 |   yy19    |   700         |   200         |   DL      |   700
compz   |   xx7 |   yy20    |   700         |   400         |   XL      |   700

Ваше объяснение противоречит ожидаемым значениям (650, 50, 0) для val_id = "xx7". В описании вы ожидаете, что значения new_field будут вычтены из our_val_amt, если product ! = "CL"; но в ожидаемом результате вы ничего не вычли из 700; но вместо этого скопировал val_against. Это не ясно. Как вы вычисляете значения для xx7?

Azhar Khan 01.02.2023 06:34

Привет, Ажар, извини за путаницу. Я ожидаю, что это произойдет, если продукт «CL» находится в пределах [val_id]. В примере для val_id = 'xx7' нет [product] = 'CL'. Если [продукт] не имеет «CL», ему просто нужно распределить [our_val_amt] между каждым [val_id]. Например, val_id = 'xx7' our_val_amt = 700, это распространяется в первой строке (650), а затем остается 700 - 650 = 50, вставляется в следующую строку со следующим значением 0, как в примере.

svenvuko 01.02.2023 07:31

На самом деле жаль, что вы смотрите на то, что выводит код. Пожалуйста, посмотрите на «Набор данных и вывод new_field, пытающийся достичь».

svenvuko 01.02.2023 07:50
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
3
54
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Код дал сбой в крайнем случае, когда есть несколько продуктов, не относящихся к CL, и our_val_amt нужно было распределить так, чтобы последние несколько продуктов могли получить значение 0. Я спросил об этом варианте использования в последнем/предыдущем вопросе; но оно осталось без ответа. У вас могут быть такие угловые случаи, и вам необходимо выполнить комплексный тест.

Ниже приведена обновленная логика. Комментарии добавляются перед каждой строкой обработки, чтобы объяснить, что она делает.

df = pd.DataFrame(data=[["compx","xx1","yy2",424,134,"CL",134],["compx","xx1","yy1",424,418,"XL",290],["compx","xx2","yy4",472,104,"CL",104],["compx","xx2","yy3",472,60,"DL",368],["compx","xx3","yy6",490,500,"CL",490],["compx","xx3","yy5",490,50,"XL",0],["compx","xx3","yy7",490,200,"DL",0],["compx","xx4","yy8",510,200,"CL",200],["compx","xx4","yy9",510,300,"CL",300],["compx","xx4","yy10",510,50,"CL",10],["compy","xx5","yy11",510,200,"CL",200],["compy","xx5","yy12",510,300,"CL",300],["compy","xx5","yy12",510,50,"CL",10],["compy","xx5","yy13",510,30,"DL",0],["compz","xx6","yy14",350,200,"CL",200],["compz","xx6","yy15",350,100,"CL",100],["compz","xx6","yy16",350,50,"XL",50],["compz","xx6","yy17",350,50,"DL",0],["compz","xx7","yy18",700,650,"DL",650],["compz","xx7","yy19",700,200,"DL",50],["compz","xx7","yy20",700,400,"XL",0]], columns=["name","val_id","fac_id","our_val_amt","val_against","product","new_field_expected"])


# Compute tuple of "our_val_amt", "val_against" and "product" for easy processing as one column. It is hard to process multiple columns with "transform()".
df["the_tuple"] = df[["our_val_amt", "val_against", "product"]].apply(tuple, axis=1)

def compute_new_field_for_cl(g):
  # df_g is a tuple ("our_val_amt", "val_against", "product") indexed as (0, 1, 2).
  df_g = g.apply(pd.Series)
  df_g["new_field"] = df_g.apply(lambda row: min(row[0], row[1]) if row[2] == "CL" else 0, axis=1)
  # Cumulative sum for comparison
  df_g["cumsum"] = df_g["new_field"].cumsum()
  # Previous row's sum for comparison
  df_g["cumsum_prev"] = df_g["cumsum"].shift(periods=1)
  # if our_val_amt >= sum then use min(our_val_amt, val_against)
  # else if our_val_amt < sum then take partial of first record such that our_val_amt == sum else take `0` for the rest records
  df_g["new_field"] = df_g.apply(lambda row: 0 if row["cumsum_prev"] > row[0] else row[0] - row["cumsum_prev"] if row["cumsum"] > row[0] else row["new_field"], axis=1)
  return df_g["new_field"]

# Apply above function and compute new field values for "CL".
df["new_field"] = df.groupby("val_id")[["the_tuple"]].transform(compute_new_field_for_cl)


# Re-compute tuple of "our_val_amt", "val_against", "new_field" and "product".
df["the_tuple"] = df[["our_val_amt", "val_against", "new_field", "product"]].apply(tuple, axis=1)

def compute_new_field_for_not_cl(g):
  # df_g is a tuple ("our_val_amt", "val_against", "new_field", "product") indexed as (0, 1, 2, 3).
  df_g = g.apply(pd.Series)
  # print(df_g)
  cl_sum = df_g[df_g[3] == "CL"][2].sum()
  if cl_sum > 0:
    df_g["new_field"] = df_g.where(df_g[3] != "CL")[0] - df_g[df_g[3] == "CL"][2].sum()
    df_g["new_field"] = df_g["new_field"].fillna(df_g[2])
    # Cumulative sum for comparison
    df_g["cumsum"] = df_g["new_field"].cumsum()
    # if our_val_amt < sum then take diff (our_val_amt - sum) else take `0` for the rest records
    df_g["new_field"] = df_g.apply(lambda row: 0 if row["cumsum"] > row[0] else row["new_field"], axis=1)
  else:
    df_g["new_field"] = df_g.apply(lambda row: min(row[0], row[1]) if row[3] != "CL" else row[2], axis=1)
    # Cumulative sum for comparison
    df_g["cumsum"] = df_g["new_field"].cumsum()
    # Previous row's sum for comparison
    df_g["cumsum_prev"] = df_g["cumsum"].shift(periods=1)
    # if our_val_amt >= sum then use min(our_val_amt, val_against)
    # else if our_val_amt < sum then take partial of first record such that our_val_amt == sum else take `0` for the rest records
    df_g["new_field"] = df_g.apply(lambda row: 0 if row["cumsum_prev"] > row[0] else row[0] - row["cumsum_prev"] if row["cumsum"] > row[0] else row["new_field"], axis=1)

  return df_g["new_field"]

# Apply above function and compute new field values for "CL".
df["new_field"] = df.groupby("val_id")[["the_tuple"]].transform(compute_new_field_for_not_cl)


df = df.drop("the_tuple", axis=1)

print(df)

Выход:

     name val_id fac_id  our_val_amt  val_against product  new_field_expected  new_field
0   compx    xx1    yy2          424          134      CL                 134     134.00
1   compx    xx1    yy1          424          418      XL                 290     290.00
2   compx    xx2    yy4          472          104      CL                 104     104.00
3   compx    xx2    yy3          472           60      DL                 368     368.00
4   compx    xx3    yy6          490          500      CL                 490     490.00
5   compx    xx3    yy5          490           50      XL                   0       0.00
6   compx    xx3    yy7          490          200      DL                   0       0.00
7   compx    xx4    yy8          510          200      CL                 200     200.00
8   compx    xx4    yy9          510          300      CL                 300     300.00
9   compx    xx4   yy10          510           50      CL                  10      10.00
10  compy    xx5   yy11          510          200      CL                 200     200.00
11  compy    xx5   yy12          510          300      CL                 300     300.00
12  compy    xx5   yy12          510           50      CL                  10      10.00
13  compy    xx5   yy13          510           30      DL                   0       0.00
14  compz    xx6   yy14          350          200      CL                 200     200.00
15  compz    xx6   yy15          350          100      CL                 100     100.00
16  compz    xx6   yy16          350           50      XL                  50      50.00
17  compz    xx6   yy17          350           50      DL                   0       0.00
18  compz    xx7   yy18          700          650      DL                 650     650.00
19  compz    xx7   yy19          700          200      DL                  50      50.00
20  compz    xx7   yy20          700          400      XL                   0       0.00

Другие вопросы по теме