基于90%相似度的Pandas字符串分组方案求助(40万行数据)
针对40万行Pandas DataFrame的近似字符串分组方案
核心思路
针对大规模数据的近似字符串分组,核心是减少不必要的相似度计算,同时利用C加速的模糊匹配库(如RapidFuzz)提升效率,避免全量两两比对。以下两种方案可高效适配40万行数据的需求:
方案一:RapidFuzz + DBSCAN聚类
适合唯一字符串数量中等(如1万-10万)的场景,通过聚类将相似字符串归为一组,再为每组指定代表(频次最高的字符串)。
步骤与代码
- 先安装依赖:
pip install rapidfuzz pandas scikit-learn numpy
- 完整实现代码:
import pandas as pd from rapidfuzz import fuzz from sklearn.cluster import DBSCAN import numpy as np # 加载数据(示例数据替换为你的DataFrame) data = pd.DataFrame(data=["Apple", "Apple", "apple", "appel", "aple", "Orange", "Banana"], columns=["Fruits"], dtype="str") # 1. 提取唯一字符串并统计频次,减少计算量 unique_fruits = data["Fruits"].value_counts().reset_index() unique_fruits.columns = ["Fruit", "Count"] fruit_list = unique_fruits["Fruit"].tolist() # 2. 生成相似度转距离的矩阵(1 - 相似度/100,DBSCAN用距离聚类) # 用rapidfuzz的cdist批量计算,效率远高于手动循环 from rapidfuzz import process distance_matrix = 1 - np.array(process.cdist(fruit_list, fruit_list, scorer=fuzz.ratio)) / 100 # 3. DBSCAN聚类:eps=0.1对应90%相似度,min_samples=1确保单个字符串也成组 dbscan = DBSCAN(eps=0.1, min_samples=1, metric="precomputed") unique_fruits["Cluster"] = dbscan.fit_predict(distance_matrix) # 4. 为每个聚类分配代表(取组内频次最高的字符串) cluster_reps = unique_fruits.groupby("Cluster").apply( lambda x: x.loc[x["Count"].idxmax(), "Fruit"] ).reset_index(name="Representative") # 5. 映射回原数据并统计最终结果 unique_fruits = unique_fruits.merge(cluster_reps, on="Cluster") data["Grouped_Fruit"] = data["Fruits"].map(unique_fruits.set_index("Fruit")["Representative"]) result = data["Grouped_Fruit"].value_counts().reset_index() result.columns = ["Fruit", "Occurs"] print(result)
方案二:高频候选优先匹配法
适合唯一字符串数量极大(如10万+)的场景,通过优先处理高频字符串,避免全量距离矩阵计算,内存占用更低。
步骤与代码
import pandas as pd from rapidfuzz import process, fuzz # 加载数据 data = pd.DataFrame(data=["Apple", "Apple", "apple", "appel", "aple", "Orange", "Banana"], columns=["Fruits"], dtype="str") # 1. 按频次降序排列字符串,优先处理高频项 freq_sorted = data["Fruits"].value_counts().sort_values(ascending=False) fruit_to_group = {} group_reps = [] # 2. 遍历高频字符串,为每个字符串匹配已有组或新建组 for fruit in freq_sorted.index: if fruit in fruit_to_group: continue # 查找是否有相似度≥90%的已存在组代表 match = process.extractOne(fruit, group_reps, scorer=fuzz.ratio, score_cutoff=90) if match: fruit_to_group[fruit] = match[0] else: group_reps.append(fruit) fruit_to_group[fruit] = fruit # 3. 映射并统计结果 data["Grouped_Fruit"] = data["Fruits"].map(fruit_to_group) result = data["Grouped_Fruit"].value_counts().reset_index() result.columns = ["Fruit", "Occurs"] print(result)
关键优化点
- 先处理唯一值:40万行数据中重复字符串占比通常很高,先提取唯一值可将计算量降低几个数量级。
- 大小写统一:如果大小写差异不影响分组,可先执行
data["Fruits"] = data["Fruits"].str.lower(),减少无效匹配。 - 调整相似度阈值:若90%阈值不符合需求,可修改
score_cutoff(方案二)或eps参数(方案一,eps=1-阈值/100)。
内容的提问来源于stack exchange,提问作者Nate
相关产品推荐
相关产品推荐

