Столбец сопоставления с использованием двух словарей

У меня есть 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

Как лучше всего этого добиться?

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
11
0
1 989
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Ответ принят как подходящий

Один из способов — использовать 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

Это плохо обобщается. Я подозреваю, что голоса за краткость / лаконичность, но это не очень полезно для тех, кто в будущем захочет переключаться между более чем двумя словарями.

piRSquared 29.04.2019 17:17

Ну, ОП на самом деле не просил обобщающего решения, а скорее одного для этого примера. Однако это можно легко обобщить на несколько случаев с np.select @piRSquared.

yatu 29.04.2019 17:19

Согласитесь, OP не просил об этом, но, на мой взгляд, это лучший ответ. И я бы сказал, что обобщение с использованием np.select не является тривиальным, и вы должны включить является. Это определенно заслужило бы мой голос.

piRSquared 29.04.2019 17:21

Согласен с этим :) обновлен более общим решением @piRSquared

yatu 29.04.2019 17:36

Если это необходимо сделать для многих групп, используйте 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

Хотя я думаю, что ответ Яту наиболее актуален для этого конкретного вопроса, хорошо бы решить и общий случай.

C.Nivs 29.04.2019 17:01

Да, решение Yatu очень хорошее, и его можно легко обобщить на несколько случаев с помощью np.select. Если можно создать словарь диктов без ввода, то я думаю, что это может быть немного лучше, чем все перечисленные условия, необходимые для np.select

ALollz 29.04.2019 17:05

Вы можете создать функцию, которая делает это для одного элемента, а затем использовать применить лямбда для вашего фрейм данных.

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)

Даже если это решит проблему, это будет довольно медленно.

rpanai 29.04.2019 17:01

Вы можете использовать две замены как таковые:

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

Erfan 29.04.2019 17:19

Я использую 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

Другие вопросы по теме