Wil*_*mar 4 python search match dataframe pandas
警告:您即将看到非常。非常糟糕的代码。我知道,只是不知道如何解决。我已经尝试了几种替代方案,但我缺乏 Pandas (或 numpy - 也许这是一个更好的替代方案)的经验。你被警告了!
我有两个数据帧,我需要从数据帧二中存在的数据帧一中查找匹配信息。我来给你展示:
# DataFrame 1
d1 = {'name': ['John Doe', 'Jane Doe'],
'email': ['john@example.com', 'jane@example.com'],
'phone': ['15181111111', '15182222222']}
df1 = pd.DataFrame(data=d1)
###
# DataFrame 2
d2 = {'name': ['Fred Flinstone', 'Barney Rubble', 'Betty Rubble'],
'email': ['john@example.com', 'barney@example.com', 'betty@example.com'],
'Mobile': ['15183333333', '15182222222', '15184444444'],
'LandLine': ['15181111111', '15182222222', '15185555555']}
df2 = pd.DataFrame(data=d2)
Run Code Online (Sandbox Code Playgroud)
所以我的目标是找到哪些行与(电子邮件、电话)df2中可用数据的每部分(但名称)相匹配df1。当找到匹配项时,我需要保留两个数据帧中所有数据的记录。
现在,开始咬指甲,深呼吸,看看我正在做的耻辱。它确实有效,但您很快就会意识到问题是什么:
# Empty dataframe to store matches
df_found = pd.DataFrame(columns=['df1 Name', 'df1 email', 'df1 phone', 'df2 name', 'df2 email', 'df2 mobile', 'df2 landline'])
# Search for matches
for row_df1 in df1.itertuples():
tmp_df = df2[df2['email'].str.contains(row_df1.email, na=False, case=False)]
if(len(tmp_df) > 0):
for row_df2 in tmp_df.itertuples():
df_found.loc[len(df_found)] = [row_df1.name, row_df1.email, row_df1.phone, row_df2.name, row_df2.email, row_df2.Mobile, row_df2.LandLine]
tmp_df = df2[df2['Mobile'].str.contains(row_df1.phone, na=False, case=False)]
if(len(tmp_df) > 0):
for row_df2 in tmp_df.itertuples():
df_found.loc[len(df_found)] = [row_df1.name, row_df1.email, row_df1.phone, row_df2.name, row_df2.email, row_df2.Mobile, row_df2.LandLine]
tmp_df = df2[df2['LandLine'].str.contains(row_df1.phone, na=False, case=False)]
if(len(tmp_df) > 0):
for row_df2 in tmp_df.itertuples():
df_found.loc[len(df_found)] = [row_df1.name, row_df1.email, row_df1.phone, row_df2.name, row_df2.email, row_df2.Mobile, row_df2.LandLine]
#Drop duplicates - Yes of course there are many
df_found.drop_duplicates(keep='first',inplace=True)
Run Code Online (Sandbox Code Playgroud)
就这样,我在循环内有一系列循环,每个循环都遍历相同的数据并增加临时数据帧和火柴持有者数据帧。
最后我得到了结果:
但速度却太可怕了。我的真实数据框第一列有 29 列,第二列有 55 列。第一个大约有 10 万条记录,第二个大约有 50 万条记录。目前,在我的 i7(没有 GPU 和 16GB RAM)中,这个过程大约需要四个小时。
如果您已经能够呼吸并且不再用头撞墙,我会很感激一些关于如何正确做到这一点的想法。
非常感谢!
向数据帧添加一行需要复制整个数据帧 - 因此一次一行构建数据帧是一个 O(n^2) 操作,而且非常慢。此外,Series.str.contains 需要检查每个字符串值是否包含。由于您要将每一行与其他每一行进行比较,因此这也是一个 O(n^2) 操作。
一般来说,Pandas 中的单行操作表明代码非常慢。
您可以执行 SQL 样式的联接来执行您在此处尝试执行的操作。
email_merge = df1.merge(df2, on=["email"], suffixes=("", "_right"))
mobile_merge = df1.merge(df2, left_on=["phone"], right_on=["Mobile"], suffixes=("", "_right"))
landline_merge = df1.merge(df2, left_on=["phone"], right_on=["LandLine"], suffixes=("", "_right"))
Run Code Online (Sandbox Code Playgroud)
第一行在电子邮件字段之间进行连接。第二个连接针对第一种电话。第三个连接针对第二种电话。顺便说一句,你最终会得到很多重复的东西。
然后,您可以将每个数据帧连接在一起:
print(pd.concat([email_merge, landline_merge, mobile_merge], sort=True))
Run Code Online (Sandbox Code Playgroud)
这给了我以下结果:
LandLine Mobile email email_right name name_right phone
0 15181111111 15183333333 john@example.com NaN John Doe Fred Flinstone 15181111111
0 15181111111 15183333333 john@example.com john@example.com John Doe Fred Flinstone 15181111111
1 15182222222 15182222222 jane@example.com barney@example.com Jane Doe Barney Rubble 15182222222
0 15182222222 15182222222 jane@example.com barney@example.com Jane Doe Barney Rubble 15182222222
Run Code Online (Sandbox Code Playgroud)