В python объединить два фрейма данных с ключом слияния одного фрейма данных, содержащимся в ключе другого фрейма данных

Я хотел бы объединить два кадра данных df1 и df2, чтобы сравнить два значения, информация 1 и информация 2. Ключ для их объединения скрыт в столбцах имен. Df1 является «чистым», так как в нем есть столбец имени и столбец фамилии. Df2, однако, сложно. Есть только столбец имени, и имена могут быть даны по-разному. Стандартный регистр — это имя и фамилия, но, как показано на рисунке ниже, он может содержать два имени, разделенных «и» или «&», или даже может быть чем-то совершенно другим, например, школой.

Вот фиктивные данные в коде:

data1 = [['Anna','Tessmann',10], ['Ben','Fachmann',20], ['John','Smith',10]]
df1 = pd.DataFrame(data1, columns=['FirstName','LastName','Info1'])


data2 = [['Ben Fachmann',30], ['School AAA',40], ['John and Melissa Smith',50], ['Bob & Anna Tessmann',20]]
df2= pd.DataFrame(data2, columns=['Name','Info2'])

Кто-нибудь знает эффективный способ объединить эти два? Есть ли возможность объединиться на st, например, «df2.Name содержит df1.Lastname»? Или я пытался разобрать df2.Name, я нашел nameparser import HumanName, но я думаю, что он не может работать с «и» и «&».

Прошу прощения, если что-то неясно. Большое спасибо за любую помощь заранее!

Кажется, я понимаю, но вам нужен код с надписью if df2.name has 2 names then value / 2 else value then append that to db1?

ajgrinds 12.05.2023 18:12

Почему у "Anna Tessmann" 10 в Info2 из df_analysis? Также у вас опечатка в data2, "Testmann" вместо "Tessmann". Можешь перепроверить?

Timeless 12.05.2023 18:17

Я думаю, что это опечатка, судя по картинкам, я исправил

mozway 12.05.2023 18:21
Почему в 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 может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
4
3
80
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Я думаю, вам нужно сделать столбец, который может соответствовать именам. Тогда он будет работать нормально.

Вот что-то, что работает. Это может не всегда работать в зависимости от уникальности имен в данных.

Кроме того, в данных вашего примера была опечатка, но я исправил ее ниже. (тессманн был тестманном)

import pandas as pd

data1 = [['Anna','Tessmann',10], ['Ben','Fachmann',20], ['John','Smith',10]]
df1 = pd.DataFrame(data1, columns=['FirstName','LastName','Info1'])


data2 = [['Ben Fachmann',30], ['School AAA',40], ['John and Melissa Smith',50], ['Bob & Anna Tessmann',20]]
df2= pd.DataFrame(data2, columns=['Name','Info2'])

# make a column to identify which indices in df1 match to df2
df2['merge_index'] = None
for _ind, _row in enumerate(df1.to_dict(orient='records')):
    df2.loc[df2.Name.str.contains(_row['FirstName']) & df2.Name.str.contains(_row['LastName']), 'merge_index'] = _ind

# merge df1 index to df2.merge_index column and select columns to keep
merged = pd.merge(left=df1, right=df2, how='left', left_index=True, right_on='merge_index')[['FirstName', 'LastName', 'Info1', 'Info2']]

Выход: объединены

      FirstName  LastName  Info1  Info2
3      Anna      Tessmann     10     20
0       Ben      Fachmann     20     30
2      John      Smith        10     50
Ответ принят как подходящий

Вы можете использовать двойную подстроку merge:

import re

pattern1 = '|'.join(map(re.escape, df1['FirstName']))
pattern2 = '|'.join(map(re.escape, df1['LastName']))

match1 = df2['Name'].str.extractall(f'(?P<FirstName>{pattern1})').droplevel(1)
match2 = df2['Name'].str.extractall(f'(?P<LastName>{pattern2})').droplevel(1)

out = df1.merge(df2.join(match1).join(match2),
                on=['FirstName', 'LastName'])

Выход:

  FirstName  LastName  Info1                    Name  Info2
0      Anna  Tessmann     10     Bob & Anna Tessmann     20
1       Ben  Fachmann     20            Ben Fachmann     30
2      John     Smith     10  John and Melissa Smith     50

Спасибо! Прекрасно работал и с моим фактическим набором данных!

Anna 15.05.2023 11:47

Другое возможное решение:

L1 = df1[["FirstName", "LastName"]].agg(set, axis=1).tolist()
L2 = list(zip([s.split() for s in df2["Name"]], df2["Info2"]))
​
df_analysis = (
  df1.assign(Infos2=[next((v for (lst, v) in L2 if s.issubset(lst)), None) for s in L1])
)

Выход :

print(df_analysis)

  FirstName  LastName  Info1  Infos2
0      Anna  Tessmann     10      20
1       Ben  Fachmann     20      30
2      John     Smith     10      50

Во-первых, мы заменяем все «&» на «и» для согласованности, затем разделяем по «и» и разбиваем это на несколько строк с одним и тем же индексом.

Затем мы разделяем и расширяем столбцы. Те, у кого есть только имена, могут использовать заполнение по фамилии своего супруга. Мы объединяем эту разобранную таблицу имен обратно в df2 только по индексу, чтобы сопоставить имена с info2.

Тогда это простое слияние с df1.

names = df2["Name"].str.replace("&", "and")
names = names.str.split("and").explode()
names = names.str.strip()

names = (
    names.str.split(" ", expand=True)
    .rename(columns = {0: "FirstName", 1: "LastName"})
    .fillna(method = "bfill")
)

df1.merge(
    names.merge(df2, left_index=True, right_index=True), on=["FirstName", "LastName"]
)

Другое возможное решение, основанное на идее замены and и & фамилией в каждой строке df2:

(df1.assign(Name = df1['FirstName'] + ' ' + df1['LastName'])
 .merge(df2.assign(Name = df2['Name'].str.replace(
     r'and\s|&\s', lambda x: x.string.split()[-1] + ',', regex=True)
                   .str.split(','))
 .explode('Name'), on='Name', how='left')
 .drop('Name', axis=1))

Выход:

  FirstName  LastName  Info1  Info2
0      Anna  Tessmann     10     20
1       Ben  Fachmann     20     30
2      John     Smith     10     50

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