Я хотел бы объединить два кадра данных 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, но я думаю, что он не может работать с «и» и «&».
Прошу прощения, если что-то неясно. Большое спасибо за любую помощь заранее!
Почему у "Anna Tessmann" 10 в Info2 из df_analysis? Также у вас опечатка в data2, "Testmann" вместо "Tessmann". Можешь перепроверить?
Я думаю, что это опечатка, судя по картинкам, я исправил






Я думаю, вам нужно сделать столбец, который может соответствовать именам. Тогда он будет работать нормально.
Вот что-то, что работает. Это может не всегда работать в зависимости от уникальности имен в данных.
Кроме того, в данных вашего примера была опечатка, но я исправил ее ниже. (тессманн был тестманном)
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
Спасибо! Прекрасно работал и с моим фактическим набором данных!
Другое возможное решение:
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
Кажется, я понимаю, но вам нужен код с надписью
if df2.name has 2 names then value / 2 else value then append that to db1?