pandas:按键分组,将乱七八糟的字符串聚类

pandas: grouping by key to cluster messy strings

我有一个 table 看起来像这样:

company_id,company_name
1,Amazon
1,Amazon Ltd
2,Google
1,Amazon
2,Gogle
3,Facebook Ltd
3,Facebook LTD
1,AMAZON
1,AMAZON LTD
2,GOOGLE
3,Facebook
3,Face book

所以我对每个公司都有一个唯一标识符,但它们的文本表示不同。我努力的是消除这些不一致,并有一些类似的东西:

company_id,company_name
1,Amazon
1,Amazon
2,Google
1,Amazon
2,Google
3,Facebook
3,Facebook
1,Amazon
1,Amazon
2,Google
3,Facebook
3,Facebook

我对选择标准并没有死心塌地 - 它可以是所述组中最常见的值,也可以是随机值。但我需要的是高效的东西,因为我的 table 已经增长到包含数百万行。

我的解决方案是创建一个包含 id -> name 的独特组合的散列图,然后根据这些进行替换。大致如下:

dd = df.drop_duplicates().set_index('company_id').to_dict()['company_name']
df.company_name = df.company_id
df.company_name = df.company_name.replace(dd)

它在较小的集合上运行良好,但由于它创建的哈希映射很大,它变得相当慢且内存效率低下。

我也尝试过基于 company_idgroupby 并用随机值替换每个组中的所有 company_name,但我无法修改底层数据框(没有 .locing 它,这是双倍低效的)。

想到的最后一个选项是创建一个具有唯一值的单独框架 (df.drop_duplicates('company_id')) 并将其与基于 company_id 的原始框架合并,但这听起来并不可怕也有效率。

您可以按 company_id 分组并访问公司 ID:

df.groupby('company_id').first().loc[df.company_id]

结果:

如果您想要作为列,请重置索引:

df.groupby('company_id').first().loc[df.company_id].reset_index()

结果:

我已经使用 map 在相当大的 DataFrame 上测试了您的解决方案,它看起来非常高效:

prng = np.random.RandomState(0)
df = pd.DataFrame({'company_id': prng.randint(10**6, size=10**7), 
                   'company_name': prng.rand(10**7).astype('str')})
# It has 10m unique identifiers each having 10 entries on average
# ranges from 1 to 28.

df.head()
   company_id         company_name
0      985772   0.4097176168442743
1      305711    0.506659503051052
2      435829  0.45049621797963846
3      117952  0.21756825314220174
4      963395  0.07977409062048224

现在您可以在 company_id 和第一次出现的 company_name 之间为该 ID 创建一个映射器:

%%timeit
mapper = df.drop_duplicates(subset='company_id').set_index('company_id')['company_name']
df['company_id'].map(mapper)
1 loop, best of 3: 1.86 s per loop

由于 transform 也是一种可能,尽管我创建的这个特定数据集的性能不如 map:

%timeit df.groupby('company_id')['company_name'].transform('first')
1 loop, best of 3: 2.33 s per loop

.loc 看起来效率很低:

%timeit df.groupby('company_id').first().loc[df.company_id]
1 loop, best of 3: 26.4 s per loop

一般来说,您最好对此类数据使用分类。请参阅另一个讨论 here