У меня есть 2 кадра данных (покупка и продажа) следующим образом:
ПОКУПКА:
ПРОДАЖИ:
Мне нужен выходной фрейм данных, в котором, если человек (Name
) продает товар, Amt
и Qty
должны быть вычтены из фрейма данных покупки, а новый фрейм данных должен быть создан с оставшимися Amt
и Qty
, как показано ниже:
ВЫХОДНОЙ КАДР ДАННЫХ:
Обратите внимание, что любые предметы, проданные человеком (Name
), были вычтены из кадра данных покупки, а оставшиеся предметы (Amt
и Qty
) сохранены в новом выходном кадре данных. Также человек D
никогда не продавал какие-либо предметы, даже если это должно быть включено в выходной фрейм данных.
Заранее спасибо!
Датафрейм
import pandas as pd
Purchases = {
"Name": ["A", "B", "B", "C", "D", "A"],
"item": ["Item1", "Item2", "Item1", "Item3", "Item4", "Item3"],
"voucher": ["Purchase", "Purchase", "Purchase", "Purchase", "Purchase", "Purchase"],
"Amt": [10000, 500, 2000, 1000, 500, 5000],
"Qty": [100, 50, 20, 100, 100, 50],
}
Purchases = pd.DataFrame(Purchases)
Sales = {
"Name": ["A", "B", "B", "C"],
"item": ["Item1", "Item2", "Item1", "Item3"],
"voucher": ["Sales", "Sales", "Sales", "Sales"],
"Amt": [5300, 450, 1675, 1800],
"Qty": [50, 40, 15, 100],
}
Sales = pd.DataFrame(Sales)
@Corralien Я не вижу где, кажется, сейчас 1800
Во фрейме данных SALES. Результат 200, поэтому вычитание должно быть 1000-800, а не 1000-1800.
Purchases = Purchases.set_index(['Name', 'item'])
Sales = Sales.set_index(['Name', 'item'])
Purchases['Amt'].update(Purchases['Amt'].sub(Sales['Amt']))
Purchases['Qty'].update(Purchases['Qty'].sub(Sales['Qty']))
Purchases = Purchases.reset_index().sort_values(by=['Name'])
print(Purchases)
Name item voucher Amt Qty
0 A Item1 Purchase 4700 50
5 A Item3 Purchase 5000 50
1 B Item2 Purchase 50 10
2 B Item1 Purchase 325 5
3 C Item3 Purchase -800 0
4 D Item4 Purchase 500 100
Спасибо за эту информацию, Корралиен, ìnplace
будет объявлен устаревшим в следующей версии Pandas (например, Pandas 2). Так что теперь нужно взять хорошие привычки ;-)
Вероятно, update
тоже устаревает. (Я надеюсь :-)). Я думаю, что все методы всегда должны возвращать копию (явный лучше, чем неявный). Только .loc
, .iloc
и другие назначения элементов должны изменять DataFrame/Series на месте, потому что они являются явными. ИМХО :-)
Да, конечно, это было бы предпочтительнее, но update
не устарело в Pandas 2: pandas.pydata.org/docs/reference/api/pandas.Series.update.html (версия 2.0.0 (стабильная)). Но я должен признать, даже если update
очень полезен, мне не нравятся методы, возвращающие None (источники ошибок).
Вы можете использовать merge
:
# dfP = PURCHASE dataframe
# dfS = SALES dataframe
out = (Purchases.merge(Sales.drop(columns='voucher'), on=['Name', 'item'],
suffixes=(None, '_'), how='left')
.assign(Amt=lambda x: x['Amt'] - x.pop('Amt_').fillna(0).astype(int),
Qty=lambda x: x['Qty'] - x.pop('Qty_').fillna(0).astype(int),
voucher='Remaining'))
Выход:
>>> out
Name item voucher Amt Qty
0 A Item1 Remaining 4700 50
1 B Item2 Remaining 50 10
2 B Item1 Remaining 325 5
3 C Item3 Remaining -800 0
4 D Item4 Remaining 500 100
5 A Item3 Remaining 5000 50
Используя старое доброе выравнивание индекса:
tmp = Purchases.set_index(['Name', 'item'])
out = (tmp
.sub(Sales.set_index(['Name', 'item'])[['Amt', 'Qty']])
.combine_first(tmp).assign(voucher='Remaining')
.reset_index()[Purchases.columns]
)
Выход:
Name item voucher Amt Qty
0 A Item1 Remaining 4700.0 50.0
1 A Item3 Remaining 5000.0 50.0
2 B Item1 Remaining 325.0 5.0
3 B Item2 Remaining 50.0 10.0
4 C Item3 Remaining -800.0 0.0
5 D Item4 Remaining 500.0 100.0
Я не знаю, люблю я тебя или ненавижу тебя, лол ;-p
@LaurentB. тогда скорее люби меня, пожалуйста, в этом мире достаточно ненависти :p
хорошо, это то, что показывает мне мой кубик :-p
Я думаю, что в вашем входном фрейме данных есть ошибка: (C, item3) должно быть 800, а не 1800.