Поиск и замена определенных терминов в разных df

#python #regex #pandas #dataframe

Вопрос:

У меня есть два df с серией терминов, которые представляют эмоцию (например, гнев, счастье), и мне нужно перебрать все строки другого df в поисках строк, содержащих некоторые термины в серии, и обменять их значение на имя эмоции, которое представляет термин, или добавить другое значение (например, Нет), если ни один из терминов не соответствует одному из двух df.

Я попытаюсь объяснить это на приведенных ниже примерах:

 # Two df2 with target terms:

anger = pd.DataFrame({'anger': ['Angry','Acerbic', 'Aggressive', 'Bitter', 'Fiery', 'Outraged', 'Rebellious', 'Snide']})

happiness= pd.DataFrame({'happiness':['Happy','Cheerful','Bright', 'Celebratory', 'Effervescent', 'Gleeful', 'Humorous', 
                              'Irreverent', 'Rambunctious', 'Raucous', 'Rollicking', 'Silly', 'Sweet', 'Whimsical', 'Witty', 'Joyous']
})

 

Цель Df:

настроения
Агрессивный, Грамотный, Бунтарский, Веселый
Шумный, Яркий, Едкий, Глупый

Ожидаемый df:

настроения
Злости, Никакой, Злости, Никакой
Нет, Счастье, Гнев, Счастье

Есть ли какой-нибудь способ сделать это?

Комментарии:

1. Довольно грубый способ, который я могу придумать, — это создать функцию для замены данного слова (глупо) соответствующим чувством (гнев), а затем использовать df.apply для применения этой функции ко всем строкам (можно использовать .split()) для разделения каждой строки на несколько слов. Но я не уверен, насколько эффективно это было бы

Ответ №1:

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

 # Create patterns
anger_pat = fr"b({'|'.join(anger['anger'])})b"
happiness_pat = fr"b({'|'.join(happiness['happiness'])})b"

# Replace patterns
df['moods'] = df['moods'].replace({anger_pat: 'Anger',
                                   happiness_pat: 'Happiness',
                                   r'b(?!Anger|Happinessb)w ': 'None'}, 
                                  regex=True)
 

Поскольку replace функция выполняется последовательно, предыдущие замены уже сделаны. Итак, последний шаблон означает: «если слово не «Гнев» или «Счастье», замените его «Нет».

Выход:

 >>> df
                               moods
0           Anger, None, Anger, None
1  None, Happiness, Anger, Happiness

>>> print(anger_pat)
b(Angry|Acerbic|Aggressive|Bitter|Fiery|Outraged|Rebellious|Snide)b

>>> print(happiness_pat)
b(Happy|Cheerful|Bright|Celebratory|Effervescent|Gleeful|Humorous|Irreverent|Rambunctious|Raucous|Rollicking|Silly|Sweet|Whimsical|Witty|Joyous)b
 

Ответ №2:

Вы можете anger объединить / happiness фреймы данных вместе, .explode целевой df и .merge их. Затем .groupby они возвращаются:

 x = (
    pd.concat([anger.stack(), happiness.stack()])
    .to_frame(name="moods")
    .droplevel(0)
    .reset_index()
)
df["moods"] = df["moods"].str.split(", ")
df = df.explode("moods").reset_index()

print(
    df.merge(x, on="moods", how="left")
    .fillna("None")
    .groupby("index_x")["index_y"]
    .agg(", ".join)
    .reset_index(drop=True)
    .to_frame(name="moods")
)
 

С принтами:

                                moods
0           anger, None, anger, None
1  None, happiness, anger, happiness