У меня есть df:
ColA ColB
1 1
2 3
2 2
1 2
1 3
2 1
Я хотел бы использовать два разных словаря для изменения значений в ColB. Я хотел бы использовать d1, если значение в ColA равно 1, и d2, если значение в ColB равно 2.
d1 = {1:'a',2:'b',3:'c'}
d2 = {1:'d',2:'e',3:'f'}
В результате чего:
ColA ColB
1 a
2 f
2 e
1 b
1 c
2 d
Как лучше всего этого добиться?






Один из способов — использовать np.where для map значений в ColB, используя тот или иной словарь, в зависимости от значений ColA:
import numpy as np
df['ColB'] = np.where(df.ColA.eq(1), df.ColB.map(d1), df.ColB.map(d2))
Который дает:
ColA ColB
0 1 a
1 2 f
2 2 e
3 1 b
4 1 c
5 2 d
Для более общего решения вы можете использовать np.select, который работает для нескольких условий. Давайте добавим еще одно значение в ColA и словарь, чтобы увидеть, как это можно сделать с тремя разными сопоставлениями:
print(df)
ColA ColB
0 1 1
1 2 3
2 2 2
3 1 2
4 3 3
5 3 1
values_to_map = [1,2,3]
d1 = {1:'a',2:'b',3:'c'}
d2 = {1:'d',2:'e',3:'f'}
d3 = {1:'g',2:'h',3:'i'}
#create a list of boolean Series as conditions
conds = [df.ColA.eq(i) for i in values_to_map]
# List of Series to choose from depending on conds
choices = [df.ColB.map(d) for d in [d1,d2,d3]]
# use np.select to select form the choice list based on conds
df['ColB'] = np.select(conds, choices)
В результате чего:
ColA ColB
0 1 a
1 2 f
2 2 e
3 1 b
4 3 i
5 3 g
Ну, ОП на самом деле не просил обобщающего решения, а скорее одного для этого примера. Однако это можно легко обобщить на несколько случаев с np.select @piRSquared.
Согласитесь, OP не просил об этом, но, на мой взгляд, это лучший ответ. И я бы сказал, что обобщение с использованием np.select не является тривиальным, и вы должны включить является. Это определенно заслужило бы мой голос.
Согласен с этим :) обновлен более общим решением @piRSquared
Если это необходимо сделать для многих групп, используйте dict из dicts, чтобы отобразить каждую группу отдельно. В идеале вы можете найти какой-нибудь функциональный способ создать d:
d = {1: d1, 2: d2}
df['ColB'] = pd.concat([gp.ColB.map(d[idx]) for idx, gp in df.groupby('ColA')])
ColA ColB
0 1 a
1 2 f
2 2 e
3 1 b
4 1 c
5 2 d
Хотя я думаю, что ответ Яту наиболее актуален для этого конкретного вопроса, хорошо бы решить и общий случай.
Да, решение Yatu очень хорошее, и его можно легко обобщить на несколько случаев с помощью np.select. Если можно создать словарь диктов без ввода, то я думаю, что это может быть немного лучше, чем все перечисленные условия, необходимые для np.select
Вы можете создать функцию, которая делает это для одного элемента, а затем использовать применить лямбда для вашего фрейм данных.
def your_func(row):
if row["ColA"] == 1:
return d1[row["ColB"]]
elif row["ColB"] == 2:
return d2[row["ColB"]]
else:
return None
df["ColB"] = df.apply(lambda row: your_func(row), axis=1)
Даже если это решит проблему, это будет довольно медленно.
Вы можете использовать две замены как таковые:
df.loc[df['ColA'] == 1,'ColB'] = df['ColB'].replace(d1, regex=True)
df.loc[df['ColA'] == 2,'ColB'] = df['ColB'].replace(d2, regex=True)
Я надеюсь, что это помогает, BR
Вы можете использовать новый словарь, в котором ключами являются tuples, и сопоставить его с заархивированными столбцами.
d = {**{(1, k): v for k, v in d1.items()}, **{(2, k): v for k, v in d2.items()}}
df.assign(ColB=[*map(d.get, zip(df.ColA, df.ColB))])
ColA ColB
0 1 a
1 2 f
2 2 e
3 1 b
4 1 c
5 2 d
Или мы можем получить симпатичную лямбду для отображения.
ПРИМЕЧАНИЕ: Я выровнял словари для переключения между ними в зависимости от их относительного положения в списке [0, d1, d2]. В этом случае не имеет значения, что находится на первой позиции. Ставлю 0 произвольно.
df.assign(ColB=[*map(lambda x, y: [0, d1, d2][x][y], df.ColA, df.ColB)])
ColA ColB
0 1 a
1 2 f
2 2 e
3 1 b
4 1 c
5 2 d
Для надежности я бы держался подальше от симпатичного и сопоставил лямбду, у которой есть возможность значения по умолчанию.
df.assign(ColB=[*map(lambda x, y: {1: d1, 2: d2}.get(x, {}).get(y), df.ColA, df.ColB)])
ColA ColB
0 1 a
1 2 f
2 2 e
3 1 b
4 1 c
5 2 d
Недооцененный ответ, как только будет больше двух диктовок! +1
Я использую concat с reindex
idx=pd.MultiIndex.from_arrays([df.ColA, df.ColB])
df.ColB=pd.concat([pd.Series(x) for x in [d1,d2]],keys=[1,2]).reindex(idx).values
df
Out[683]:
ColA ColB
0 1 a
1 2 f
2 2 e
3 1 b
4 1 c
5 2 d
Это плохо обобщается. Я подозреваю, что голоса за краткость / лаконичность, но это не очень полезно для тех, кто в будущем захочет переключаться между более чем двумя словарями.