繁体   English   中英

pandas 中组的模糊匹配

[英]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

这仍然会返回一个列表。

顺便说一句,以这种方式计算子组会根据它看到输入的顺序给出不同的结果,所以要小心。

使用 concat 和 join

我们为每个组制作一个 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 和 join

我们可以用groupbytransform做得更好。 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM