使用Python的fuzzywuzzy匹配两个数据集生成二进制标记
嘿,我懂你现在的困境——用双重循环处理5000行的df1和20万行的df2,那可是10亿次迭代,效率低到完全没法用。咱们换个高效的思路来解决这个相似地址匹配的问题,分几步来:
第一步:先做地址标准化
相似地址经常会因为大小写、标点、缩写(比如St/Street,Ave/Avenue)导致匹配失败,所以先统一格式是关键。你可以写个标准化函数:
import re def standardize_address(address): # 转成小写,消除大小写差异 address = address.lower() # 移除所有标点符号 address = re.sub(r'[^\w\s]', '', address) # 替换常见的地址缩写(根据你的数据集调整缩写规则) address = re.sub(r'\bst\b', 'street', address) address = re.sub(r'\bave\b', 'avenue', address) address = re.sub(r'\blr\b', 'lane', address) # 去除多余的空格 address = re.sub(r'\s+', ' ', address).strip() return address # 给两个数据集添加标准化后的地址列 df1['standardized_address'] = df1['address'].apply(standardize_address) df2['standardized_address'] = df2['address'].apply(standardize_address)
第二步:缩小匹配范围,减少计算量
直接全量匹配20万行太浪费资源,咱们先按city+state+postal_code分组,只在同一区域内找相似地址——毕竟跨城市的相似地址概率极低,这样能把匹配范围缩小N倍:
from collections import defaultdict # 把df2按区域分组,存储每组的标准化地址列表 df2_region_groups = defaultdict(list) for _, row in df2.iterrows(): region_key = (row['city'], row['state'], row['postal_code']) df2_region_groups[region_key].append(row['standardized_address'])
第三步:用高效模糊匹配工具做相似性判断
推荐用rapidfuzz(比老牌的fuzzywuzzy快得多,适合大数据场景)来计算地址相似度,设定一个阈值(比如80分,你可以根据实际数据调整),超过阈值就算相似:
from rapidfuzz import process, fuzz def check_similar_address(row): region_key = (row['city'], row['state'], row['postal_code']) # 如果当前区域在df2里没有数据,直接标记为0 if region_key not in df2_region_groups: return 0 # 在同区域的地址中找最高相似度得分 highest_match_score = process.extractOne( row['standardized_address'], df2_region_groups[region_key], scorer=fuzz.token_set_ratio # 这个评分器对语序不敏感,适合地址匹配 )[1] # 超过阈值返回1,否则返回0 return 1 if highest_match_score >= 80 else 0 # 给df1添加二进制标记列 df1['has_similar_address'] = df1.apply(check_similar_address, axis=1)
可选进阶方法:TF-IDF+余弦相似度
如果你的地址格式特别复杂,模糊匹配不够准确,可以试试用文本向量化的方法:
from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics.pairwise import cosine_similarity import numpy as np # 先训练TF-IDF模型,用所有标准化地址作为语料 all_standardized_addresses = pd.concat([df1['standardized_address'], df2['standardized_address']]) tfidf_vectorizer = TfidfVectorizer(ngram_range=(1, 2)) # 考虑1-2个词的组合,更贴合地址结构 tfidf_vectorizer.fit(all_standardized_addresses) # 初始化标记列 df1['has_similar_address'] = 0 # 按区域分组处理 for region_key, df2_addresses in df2_region_groups.items(): # 筛选df1中当前区域的行 df1_region_mask = (df1['city'] == region_key[0]) & \ (df1['state'] == region_key[1]) & \ (df1['postal_code'] == region_key[2]) if not df1_region_mask.any(): continue # 提取当前区域的地址,转换成TF-IDF向量 df1_region_addresses = df1.loc[df1_region_mask, 'standardized_address'] df1_tfidf = tfidf_vectorizer.transform(df1_region_addresses) df2_tfidf = tfidf_vectorizer.transform(df2_addresses) # 计算余弦相似度矩阵 similarity_matrix = cosine_similarity(df1_tfidf, df2_tfidf) # 找到每行的最大相似度,超过阈值(比如0.8)标记为1 max_similarities = np.max(similarity_matrix, axis=1) df1.loc[df1_region_mask, 'has_similar_address'] = (max_similarities >= 0.8).astype(int)
一些注意事项
- 阈值调整:不管用模糊匹配还是余弦相似度,都要根据你的实际数据测试阈值,找到准确率和召回率的平衡点
- 缺失值处理:如果
city/state/postal_code有缺失,你可以选择忽略这些字段分组,或者把缺失值归为同一组单独处理 - 依赖安装:
rapidfuzz需要单独安装,执行pip install rapidfuzz即可
内容的提问来源于stack exchange,提问作者Priya T
相关产品推荐
相关产品推荐

