У меня есть фрейм данных, который выглядит так:
Я хочу сгруппировать фрейм данных по #PROD и #CURRENCY и заменить TP содержимым оффшорных данных в столбце Loc, не создавая два фрейма данных и не объединяя их.
Окончательный результат будет выглядеть примерно так:
Мне удалось создать выходные данные, разделив фрейм данных на два (оншорный и оффшорный), а затем создав объединение #PROD и #CURRENCY. Однако мне было интересно, есть ли более чистый способ сделать это?
Код для Dataframe:
import pandas as pd
data=[['Offshore','NY','A','USD','ABC_USD'],['Onshore','BH','A','USD',''], ['Onshore','AE','A','USD',''],\
['Offshore','NY','A','GBP','GBP_ABC'],['Onshore','BH','A','GBP',''], ['Onshore','AE','A','GBP',''],\
['Onshore','BH','A','EUR',''],['Onshore','AE','A','EUR','']]
df = pd.DataFrame(data, columns=['Loc', 'Country','#PROD','#CURRENCY','TP'])
df
Я думаю, что merge
— самый верный и чистый подход. Не нужно ни сортировать данные, ни беспокоиться о побочных эффектах groupby
.
Вы можете использовать это:
df["TP"] = (
df.sort_values("Loc") # ensure Offshore comes before Onshore
.replace("", np.nan) # replace "" with nan so it can be forward filled
.groupby(["#PROD", "#CURRENCY"])["TP"]
.ffill()
.fillna("")
)
Loc Country #PROD #CURRENCY TP
0 Offshore NY A USD ABC_USD
1 Onshore BH A USD ABC_USD
2 Onshore AE A USD ABC_USD
3 Offshore NY A GBP GBP_ABC
4 Onshore BH A GBP GBP_ABC
5 Onshore AE A GBP GBP_ABC
6 Onshore BH A EUR
7 Onshore AE A EUR
Вы можете использовать groupby
, затем transform
, чтобы получить значение first
столбца TP
, где Loc
соответствует Offshore
.
Я заранее сортирую DataFrame
, чтобы значения Offshore
были выше значений Onshore
.
Пытаться:
df['TP'] = (
df.sort_values('Loc')
.groupby(['#PROD', '#CURRENCY'])['TP']
.transform('first')
)
print(df)
Выход:
Loc Country #PROD #CURRENCY TP
0 Offshore NY A USD ABC_USD
1 Onshore BH A USD ABC_USD
2 Onshore AE A USD ABC_USD
3 Offshore NY A GBP GBP_ABC
4 Onshore BH A GBP GBP_ABC
5 Onshore AE A GBP GBP_ABC
6 Onshore BH A EUR
7 Onshore AE A EUR
Я думаю, что слияние — самый простой и эффективный способ сделать это:
df['TP'] = df[cols].merge(df[df['Loc'].eq('Offshore')], how='left')['TP'].values
Не нужно сортировать, не нужно беспокоиться о том, какие значения присутствуют изначально.
Альтернативно:
cols = ['#PROD', '#CURRENCY']
s = (df[cols].reset_index().merge(df[df['Loc'].eq('Offshore')])
.set_index('index')['TP']
)
df.loc[s.index, 'TP'] = s
Выход:
Loc Country #PROD #CURRENCY TP
0 Offshore NY A USD ABC_USD
1 Onshore BH A USD ABC_USD
2 Onshore AE A USD ABC_USD
3 Offshore NY A GBP GBP_ABC
4 Onshore BH A GBP GBP_ABC
5 Onshore AE A GBP GBP_ABC
6 Onshore BH A EUR NaN
7 Onshore AE A EUR NaN
Общее решение, которое будет работать для любого количества переменных, можно получить, используя что-то вроде этого:
group_variables = ["#PROD", "#CURRENCY"]
target_variable = "TP"
groups = df.groupby(group_variables)
for location, value in groups[target_variable].apply(lambda x: "".join(x)).items():
condition = pd.concat([df[var].eq(v) for var, v in zip(group_variables, location)], axis=1).all(axis=1)
df.loc[condition, (target_variable)] = value
Выход:
Loc Country #PROD #CURRENCY TP
0 Offshore NY A USD ABC_USD
1 Onshore BH A USD ABC_USD
2 Onshore AE A USD ABC_USD
3 Offshore NY A GBP GBP_ABC
4 Onshore BH A GBP GBP_ABC
5 Onshore AE A GBP GBP_ABC
6 Onshore BH A EUR
7 Onshore AE A EUR
Открыто заново: потому что нам нужно фильтровать, где находится
Loc
Offshore
, а не просто получать значениеTP
.