如何在Pandas中识别并合并格式差异的相似企业名称?
企业名称标准化解决方案(针对大规模数据集)
一、前置关键:名称预处理
先清洗掉干扰信息,统一格式,减少后续相似性计算的噪声:
- 统一转为大写(或小写),消除大小写差异影响
- 移除标点符号(如
.、,),解决示例中70/71条的标点差异问题 - 替换常见缩写(如
LTD→LIMITED、CO→COMPANY),统一表述 - 移除通用企业后缀(如
INC、CORP、LLC、AG、KK),聚焦核心名称 - 调整语序(如将
BOSCH GMBH ROBERT改为ROBERT BOSCH GMBH,适配企业名称常见语序)
Pandas实现代码:
import pandas as pd import re # 加载数据集为DataFrame df = pd.DataFrame.from_dict(your_data_dict, orient='index', columns=['company_name']) # 预处理函数 def clean_company_name(name): # 转大写 name = name.upper() # 移除标点 name = re.sub(r'[.,]', '', name) # 替换缩写 abbreviation_map = { 'LTD': 'LIMITED', 'CO': 'COMPANY', 'CORP': 'CORPORATION', 'GMBH': 'GMBH' # 可选择替换为全称或保留缩写,按需调整 } for abbr, full in abbreviation_map.items(): name = re.sub(rf'\b{abbr}\b', full, name) # 移除通用后缀 suffixes = ['INC', 'LIMITED', 'COMPANY', 'CORPORATION', 'LLC', 'AG', 'KK', 'GMBH'] for suffix in suffixes: name = re.sub(rf'\b{suffix}\b', '', name).strip() # 调整语序(针对姓氏/后缀在后的情况,可按需扩展规则) parts = name.split() if len(parts) >= 2 and parts[-1].isupper() and len(parts[-1]) <= 10: name = ' '.join([parts[-1]] + parts[:-1]) return name df['cleaned_name'] = df['company_name'].apply(clean_company_name)
二、相似性检测与Louvain聚类(对应你的步骤1)
Pandas本身无内置Louvain算法,可结合scikit-learn、python-louvain实现:
1. 文本向量化
用字符级TF-IDF转换短文本(企业名称)为向量,更适合捕捉字符层面的相似性:
from sklearn.feature_extraction.text import TfidfVectorizer # 字符n-gram取2-4,适配短文本特征 vectorizer = TfidfVectorizer(analyzer='char', ngram_range=(2,4)) tfidf_matrix = vectorizer.fit_transform(df['cleaned_name'])
2. 近似最近邻查找(解决17万条数据的O(n²)计算瓶颈)
直接两两计算相似度效率极低,用近似最近邻筛选高相似度对:
from sklearn.neighbors import NearestNeighbors # 余弦相似度,每个样本取Top10相似项 nn = NearestNeighbors(n_neighbors=10, metric='cosine', algorithm='brute') nn.fit(tfidf_matrix) distances, indices = nn.kneighbors(tfidf_matrix) # 构建相似边列表,相似度=1-余弦距离,阈值设为0.8(可按需调整) edges = [] threshold = 0.8 for i in range(len(df)): for j in range(1, len(indices[i])): # 跳过自身 sim = 1 - distances[i][j] if sim >= threshold: edges.append((i, indices[i][j], sim))
3. Louvain社区检测
用社区聚类将相似名称归为同一组:
import community as community_louvain import networkx as nx # 构建无向图 G = nx.Graph() for u, v, sim in edges: G.add_edge(u, v, weight=sim) # 运行Louvain算法,得到每个样本的社区ID partition = community_louvain.best_partition(G, weight='weight') # 社区ID映射到DataFrame,无连接的样本设为-1 df['community_id'] = df.index.map(lambda x: partition.get(x, -1))
三、提取统一企业名称
针对每个社区,选择最具代表性的名称,常用方法:
- 频率优先:选社区中出现次数最多的原始名称
- 核心词提取:选清洗后名称的最长公共前缀(如从
YAMAWA MFG CO LTD提取YAMAWA) - 长度优先:选最短/最长的名称作为统一标识
代码示例:
# 方法1:选频率最高的原始名称 def get_unified_name(group): name_counts = group['company_name'].value_counts() return name_counts.idxmax() # 方法2:提取最长公共前缀作为核心名称 def get_core_name(group): cleaned_names = group['cleaned_name'].tolist() if not cleaned_names: return '' prefix = cleaned_names[0] for name in cleaned_names[1:]: while not name.startswith(prefix): prefix = prefix[:-1] if not prefix: break return prefix.strip() # 应用到每个社区 df['unified_name'] = df.groupby('community_id')['company_name'].transform(get_unified_name) # 若用核心名称: # df['unified_name'] = df.groupby('community_id').apply(get_core_name).reset_index(level=0, drop=True)
四、大规模数据集优化建议
- 用
faiss替代sklearn的近似最近邻,提升百万级数据的检索效率 - 先归并完全相同的清洗后名称,减少后续聚类计算量
- 单独处理人名类条目(如示例中的
NAGAI TAKAYUKI),适配人名标准化规则 - 对相似度阈值区间(如0.7-0.8)的社区做人工抽查,调整阈值精度
内容的提问来源于stack exchange,提问作者Lusian
相关产品推荐
相关产品推荐

