[英]Fuzzy matching for groups in pandas
我有以下数据集:
import pandas as pd
from fuzzywuzzy import fuzz
adf = pd.DataFrame({'my_addresses':['234 Rue Morgue','234 R. Morgue','234 La rue morgue, 234','312 La rue moulin','24 Rue Marbeau','24 La Rue Marbeau', '28 Boulevard Suchet','28 Blvd Suchet'],'my_group':['A1','A1','A1','A1','B2','B2','B2','B2']})
adf
my_addresses my_group
0 234 Rue Morgue A1
1 234 R. Morgue A1
2 234 La rue morgue, 234 A1
3 312 La rue moulin A1
4 24 Rue Marbeau B2
5 24 La Rue Marbeau B2
6 28 Boulevard Suchet B2
7 28 Blvd Suchet B2
我想应用自定义 function。这个 function 使用模糊匹配创建给定 pandas object 的子组。
def match_groups(grp_obj, threshold = 67):
groups = []
for i in range(len(grp_obj.to_list())):
groups.append(i+1)
for i, val_i in enumerate(grp_obj.to_list()):
for j, val_j in enumerate(grp_obj.to_list()):
if j>i:
ratio = fuzz.ratio(val_i, val_j)
if ratio >= threshold:
groups[j] = groups[i]
return groups
例如,让我们计算组 B2 的子组:
match_groups(adf[adf['my_group']=='B2'].my_addresses, 67)
它返回这个列表:
[1, 1, 3, 3]
行。 现在我想将这个 function 应用到每个组。 这是预期的结果:
my_addresses my_group my_subgroup
0 234 Rue Morgue A1 1
1 234 R. Morgue A1 1
2 234 La rue morgue, 234 A1 1
3 312 La rue moulin A1 4
4 24 Rue Marbeau B2 1
5 24 La Rue Marbeau B2 1
6 28 Boulevard Suchet B2 3
7 28 Blvd Suchet B2 3
所以我尝试了这个:
adf['my_subgroup'] = adf.groupby('my_group')['my_addresses'].apply(match_groups)
但它返回我:
my_addresses my_group my_subgroup
0 234 Rue Morgue A1 NaN
1 234 R. Morgue A1 NaN
2 234 La rue morgue, 234 A1 NaN
3 312 La rue moulin A1 NaN
4 24 Rue Marbeau B2 NaN
5 24 La Rue Marbeau B2 NaN
6 28 Boulevard Suchet B2 NaN
7 28 Blvd Suchet B2 NaN
我认为我的问题是 function 返回一个列表,但我不确定如何将其作为所需列传递给数据框。 请,任何帮助将不胜感激。
一种方法可能是创建并行 DataFrame,然后加入。 以下是该方法的几个变体。 可能有更好的方法。
这是一个稍微修改过的match_groups
function,所以它需要一个 Series 而不是 DataFrame:
def match_groups(addresses, threshold):
subgroups = [i for i in range(1, len(addresses)+1)]
for i, val_i in enumerate(addresses):
for j, val_j in enumerate(addresses):
if j>i:
ratio = fuzz.ratio(val_i, val_j)
if ratio >= threshold:
subgroups[j] = subgroups[i]
return subgroups
这仍然会返回一个列表。
顺便说一句,以这种方式计算子组会根据它看到输入的顺序给出不同的结果,所以要小心。
我们为每个组制作一个 DataFrame,计算我们的子组,并保留原始 DataFrame 的索引值, adf
,然后将它们与concat一起粘贴回去:
df_subgroups = pd.concat(
pd.DataFrame({
"my_subgroup": match_groups(
adf.my_addresses[adf.my_group==grp],
threshold=67)},
index=adf.index[adf.my_group==grp])
for grp in adf.my_group.unique()
)
结果如下所示:
my_subgroup
0 1
1 1
2 1
3 4
4 1
5 1
6 3
7 3
现在, 加入共享索引:
adf.join(df_subgroups)
my_addresses my_group my_subgroup
0 234 Rue Morgue A1 1
1 234 R. Morgue A1 1
2 234 La rue morgue, 234 A1 1
3 312 La rue moulin A1 4
4 24 Rue Marbeau B2 1
5 24 La Rue Marbeau B2 1
6 28 Boulevard Suchet B2 3
7 28 Blvd Suchet B2 3
我们可以用groupby和transform做得更好。 Transform 负责上面的连接和索引保留步骤。
df_subgroups = adf.groupby('my_group').transform(match_groups, threshold=67)
df_subgroups.columns = ["my_subgroup"]
我们得到与之前相同的结果 - 保留原始索引的 DataFrame,以便我们可以加入。 令人讨厌的是,它将新列命名为“my_addresses”,所以我们给它起我们想要的名字。
my_subgroup
0 1
1 1
2 1
3 4
4 1
5 1
6 3
7 3
然后,应用与之前相同的连接:
adf.join(df_subgroups)
my_addresses my_group my_subgroup
0 234 Rue Morgue A1 1
1 234 R. Morgue A1 1
2 234 La rue morgue, 234 A1 1
3 312 La rue moulin A1 4
4 24 Rue Marbeau B2 1
5 24 La Rue Marbeau B2 1
6 28 Boulevard Suchet B2 3
7 28 Blvd Suchet B2 3
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.