#python #pandas #dataframe
Вопрос:
У меня есть следующие кадры данных:
Фрейм данных 1:
Фрукты | Овощ |
---|---|
Манго | Шпинат |
Apple | Капуста |
Арбуз | Раздавить |
Персик | Цуккини |
Фрейм данных 2:
Предмет | Цена/фунт |
---|---|
Манго | 2 |
Шпинат | 1 |
Apple | 4 |
Персик | 2 |
Цуккини | 1 |
Я хочу удалить строки из фрейма данных 1, когда оба столбца отсутствуют в серии «Элемент» фрейма данных 2, и я хочу создать следующий фрейм данных 3 на основе фреймов данных 1 и 2:
Фрукты | Овощ | Цена Комбинации |
---|---|---|
Манго | Шпинат | 3 |
Персик | Цуккини | 3 |
Третий столбец в фрейме данных 3 представляет собой сумму цен на товары из фрейма данных 2.
Ответ №1:
Сочетание melt
, merge
и unstack
:
(df1[(df1['Fruit'].isin(df2['Item'])) amp; (df1['Vegetable'].isin(df2['Item']))]
.reset_index()
.melt(id_vars = 'index',value_vars = ['Fruit','Vegetable'])
.merge(df2,left_on='value',right_on = 'Item')
.drop(columns = 'Item')
.set_index(['index','variable']).unstack(level = 1)
.transform(lambda g: g.assign(Combination_Price=g.xs('Price/lb',axis=1,level=0).sum(axis=1)))
)
производит комбинированную цену и разбивку по ингредиентам, на всякий случай
value Price/lb Combination_Price
variable Fruit Vegetable Fruit Vegetable
index
0 Mango Spinach 2 1 3
3 Peach Zucchini 2 1 3
Ответ №2:
Вы можете сделать это в два этапа:
- Замаскируйте свой фрейм данных 1 таким образом, чтобы он содержал только строки, из которых в фрейме данных 2 выходят как фрукты, так и овощи.Элемент
- Используйте
Series.map
для получения значений, связанных с оставшимися строками, и сложите их вместе, чтобы получить цену комбинации.
# Make our df2 information easier to work with.
# It is now a Series whose index is the Item and values are the prices.
# This allows us to work with it like a dictionary
>>> item_pricing = df2.set_index("Item")["Price/lb"]
>>> items = item_pricing.index
# get rows where BOTH fruit is in items amp; Vegetable is in items
>>> mask = df1["Fruit"].isin(items) amp; df1["Vegetable"].isin(items)
>>> subset = df1.loc[mask].copy() # .copy() tells pandas we want this subset to be independent of the larger dataframe
>>> print(subset)
Fruit Vegetable
0 Mango Spinach
3 Peach Zucchini
# On each column (fruit and vegetable) use .map to obtain the price of those items
# then sum those columns together into a single price
>>> subset["combo_price"] = subset.apply(lambda s: s.map(item_pricing)).sum(axis=1)
>>> print(subset)
Fruit Vegetable combo_price
0 Mango Spinach 3
3 Peach Zucchini 3
Все вместе без комментариев:
item_pricing = df2.set_index("Item")["Price/lb"]
items = item_pricing.index
mask = df1["Fruit"].isin(items) amp; df1["Vegetable"].isin(items)
subset = df1.loc[mask].copy()
subset["combo_price"] = subset.apply(lambda s: s.map(item_pricing)).sum(axis=1)
Комментарии:
1. Такое элегантное решение! Она охватывает большую его часть. Однако, если в столбце «Цена/фунт» указано значение NaN, последняя строка кода не работает.
2. Я попробовал nansum(), но это не решение.
3. Ну, это зависит от того, как вы хотите рассчитать цену комбинации, содержащей a
NaN
. Приведенный здесь код приведет к комбинации всего, что должноNaN
остатьсяNaN
, что я считаю подходящим. Что ты имел в виду?4. Я подумываю о замене значений NaN на 0.
5. замените первую строку
item_pricing = df2.set_index("Item")["Price/lb"]
на этуitem_pricing = df2.set_index("Item")["Price/lb"].fillna(0)
, и она должна работать.
Ответ №3:
Вы можете сочетать stack()
— map()
с unstack()
— sum()
:
df3 = (df1.join(df1
.stack().map(df2.set_index('Item')['Price/lb'])
.unstack().sum(axis=1, min_count=2).rename('Combination Price')
).dropna())
# Fruit Vegetable Combination Price
# 0 Mango Spinach 3.0
# 3 Peach Zucchini 3.0
Пошаговое объяснение
Стек df1
, чтобы мы могли сопоставить цены сразу:
stacked = df1.stack().map(df2.set_index('Item')['Price/lb'])
# 0 Fruit 2.0
# Vegetable 1.0
# 1 Fruit 4.0
# Vegetable NaN
# 2 Fruit NaN
# Vegetable NaN
# 3 Fruit 2.0
# Vegetable 1.0
# dtype: float64
Распакуйте обратно в исходную форму:
unstacked = stacked.unstack()
# Fruit Vegetable
# 0 2.0 1.0
# 1 4.0 NaN
# 2 NaN NaN
# 3 2.0 1.0
Сумма с min_count=2
которой означает, что сумма будет nan
равна, если не существует 2 значений (как фруктовых, так и овощных)
combo = unstacked.sum(axis=1, min_count=2)
# 0 3.0
# 1 NaN
# 2 NaN
# 3 3.0
# dtype: float64
Присоединяйтесь df1
и отбросьте nan
строки:
df3 = df1.join(combo.rename('Combination Price')).dropna()
# Fruit Vegetable Combination Price
# 0 Mango Spinach 3.0
# 3 Peach Zucchini 3.0
Ответ №4:
Вы можете сделать это с помощью двух внутренних соединений, как показано ниже. Конечный результат содержится в df3.
df_temp = pd.merge(df1, df2, left_on='Fruit', right_on='Item', how='inner')
df3 = pd.merge(df_temp, df2, left_on='Vegetable', right_on='Item', how='inner')
df3['Combined price'] = df3['Price/lb_x'] df3['Price/lb_y']
df3.drop(columns = ['Item_x','Price/lb_x','Item_y','Price/lb_y'], inplace = True)