#Loop through example dataset
for id in df['val_id']:
#total assigned value
df['tot_val'] = 0
#loop through facilities
for fac in df['fac_id']:
if df['product'].isin(['XL', 'CL', 'DL']).all():
df['row'] = min(df['our_val_amt'] - df['tot_val'], df['val_against'])
else:
df['row'] = 0
df['tot_val'] = df['tot_val'] + df['row']
df['row'] = df['row'] + 1
Sample to test val_against > val_amt change
# 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, "XL"]], columns=["name","val_id","fac_id","our_val_amt","val_against","product"])
Я пытаюсь в Pandas выполнить двойной цикл через столбцы «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 = 550 - 510 = 10.
2. Если продукт != 'CL' и находится в той же группе 'val_id'. Остаток, который нужно вычесть из «our_val_amt», будет вставлен в «NEW FIELD». например, 'our_val_amt' (424) - из шага 1 (134) = 290. Это вставлено над 'NEW FIELD'.
3.Повторите шаги для val_id xx2. Расчет NEW FIELD для CL = 104 и XL = 472 - 104 = 368.
Набор данных с вычисляемыми полями, помеченными **
| name | val_id | fac_id | our_val_amt | val_against | product | **row** | **totval**|**Field Want**
| compx | xx1 | yy1 | 424 | 418 | XL | 290 | 0 | 290
| compx | xx1 | yy2 | 424 | 134 | CL | 134 | 134 | 134
| compx | xx2 | yy3 | 472 | 60 | DL | 0 | 0 | 368
| compx | xx2 | yy4 | 472 | 104 | CL | 104 | 104 | 104
| 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 |
Ожидаемый результат
| 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 |
**row** и **totval** — это поля, которые я создал, пытаясь создать поле, которое мне нужно, с помощью приведенной выше логики.
Я попытался сгруппировать переменные вместе и иметь «продукт» в виде столбцов, но, к сожалению, я получаю более 6000 столбцов и понятия не имею, как вычислить расчет для этого сценария.
Я также пытался выполнить цикл, но получил неверные результаты/ошибки
error: Истинное значение Серии неоднозначно. Используйте a.empty, a.bool(), a.item(), a.any() или a.all()
Что, если 'val_against' >= 'our_val_amt'?
Привет, Ажар, это правильно, всегда есть пара CL и не CL для данного val_id. Если существует несколько CL для одного val_id, он сначала передается им, а затем распределяется среди не-CL. Другой пример: Val_id xx1 имеет val amt 442, но на этот раз содержит три продукта (XL – 418, CL – 134, CL – 100). Этот сценарий будет распространяться следующим образом: CL - 100, CL - 134 и остаток 190 до XL. Если val_against > our_val_amt, мы берем все 'our_val_amt'. например, val_id xx1 'our_val_amt' = 100 и val_against = 150 для CL и 100 для XL. Берем 100 против CL и оставляем XL с 0.
Привет, Ажар, задаю вопрос, если у вас есть время посмотреть. stackoverflow.com/questions/75216508/… Заранее спасибо
Вы можете решить это в 2 шага:
0
для new_field
, если условие вычисления не выполняется.Комментарии добавляются перед каждым шагом, чтобы объяснить, что он делает.
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"],
], columns=["name","val_id","fac_id","our_val_amt","val_against","product"])
# 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)
print(df)
Выход:
name val_id fac_id our_val_amt val_against product new_field
0 compx xx1 yy1 424 418 XL 290.0
1 compx xx1 yy2 424 134 CL 134.0
2 compx xx2 yy3 472 60 DL 368.0
3 compx xx2 yy4 472 104 CL 104.0
4 compx xx3 yy5 490 50 XL 0.0
5 compx xx3 yy6 490 500 CL 490.0
6 compx xx3 yy7 490 200 DL 0.0
7 compx xx4 yy8 510 200 CL 200.0
8 compx xx4 yy9 510 300 CL 300.0
9 compx xx4 yy10 510 50 CL 10.0
Привет Ажар, Спасибо за вашу помощь с программой. К сожалению, при запуске скрипта мой new_field выводит 708, 134, 428 и 104 (без изменений в скрипте). Кроме того, знаете ли вы, как я могу добавить условие в оператор where для сценариев, где our_val_amt > val_against? Я попытался создать новый result_sr, но, похоже, перезаписал существующий и сделал & | операторы без особого успеха.
Похоже, второй результат_sr = result_sr.fillna(o) + df_g[1] вызывает 708 и 428
Я вижу новые значения полей в соответствии с вашим ожидаемым результатом. Можете ли вы добавить неудачный пример и ожидаемый результат?
Также добавьте примеры данных и ожидаемые результаты для нескольких случаев использования CL и других вариантов использования.
Извинения, я теперь разрешил неожиданный вывод. Я только спрашиваю теперь, как обслуживать val_against >= our_val_amt в созданном коде. result_sr = df_g.where((df_g[2] == "CL") & (df_g[1] < df_g[0]))[1] Как включить в оператор - df_g.where((df_g[2] = = "CL") & (df_g[1] > df_g[0]))[0] Спасибо!!
Пожалуйста, добавьте образец для условия val_against >= our_val_amt, чтобы я мог протестировать изменения.
Пожалуйста, найдите пример, прикрепленный к кадру данных. Я добавил два сценария с одним val_against >= our_val_amt, а также несколькими CL для тестирования. Еще раз спасибо
Почему новое поле 10
в последней записи? Или это опечатка? Думаю, так и должно быть 50
.
Нет 10 правильно. Это потому, что наш val amt = 510 и у нас заканчивается. 300 + 200 + 10 = 510.
Я не понимаю вашей логики для последней строки. Это противоречит остальной логике для "CL". Вы должны обновить вопрос с правильной логикой.
Привет, Ажар, если есть возможность ответить на продолжение этого вопроса stackoverflow.com/questions/75216508/…
Всегда ли у вас есть пара записей CL и non-CL для заданного val_id? Другими словами, как вы обрабатываете несколько записей CL или несколько записей, не относящихся к CL, для расчета остатка?