如何在R数据框中从杂乱地址列提取城市国家并优化匹配性能
解决方案:精准提取地址中的城市与国家(减少误匹配+提升性能)
核心思路
- 优先匹配国家:国家名称(尤其是多词名称)的唯一性更强,先提取国家能大幅缩小城市匹配的候选范围,避免跨国家的误匹配。
- 按长度排序匹配模式:对国家名称按字符长度降序排序,优先匹配长名称(比如
DEMOCRATIC REPUBLIC OF THE CONGO会比CONGO先匹配),避免短名称误占匹配结果。 - 关联国家过滤城市:提取国家后,仅在对应国家的城市列表中匹配,彻底避免无关城市的误匹配(比如不会把墨西哥的Xico匹配到其他国家的地址)。
- 替换全量模糊连接:放弃
fuzzyjoin的全量匹配逻辑,改用分步提取的方式,大幅降低资源占用和运行时间。
实现代码
library(dplyr) library(stringr) library(maps) # 原始地址数据 partial_address <- c("London 121280 Ontario Canada", "Milano, Italy", "123 First St Columbus OH USA", "Cali-Valle del Cauca Colombia", "98765 France Paris", "Zurich Zurich Switzerland", "Mexico City Mexico", "Nagoya 123456 Japan", "BEIJING BEIJING CHINA", "Thailand 12345 Bangkok", "Albania La Guahira Colombia", "Conakry, Guinea", "Guinea-Bissau, Bissau", "Democratic Republic of the Congo Kinshasa") df <- data.frame(partial_address) %>% mutate(partial_address = toupper(partial_address)) # 预处理世界城市和国家数据 data(world.cities) world_cities <- world.cities %>% mutate(name = toupper(name), country.etc = toupper(country.etc)) %>% distinct(name, country.etc) # 整理国家列表:按名称长度降序排序,优先匹配长名称 country_list <- world_cities %>% distinct(country.etc) %>% arrange(desc(str_length(country.etc))) %>% pull(country.etc) # 构建国家匹配正则模式(用单词边界包裹,避免部分匹配) country_pattern <- str_c("\\b", str_replace_all(country_list, "\\s+", "\\\\s+"), "\\b", collapse = "|") # 第一步:提取国家 df_with_country <- df %>% mutate( # 取第一个匹配的长名称作为国家 country.etc = str_extract(partial_address, country_pattern), # 生成去除国家后的剩余文本,用于匹配城市 address_without_country = str_remove_all(partial_address, str_c("\\b", country.etc, "\\b")) ) # 第二步:提取对应国家的城市 df_final <- df_with_country %>% rowwise() %>% mutate( # 过滤当前国家的城市列表 city_candidates = list(world_cities %>% filter(country.etc == !!country.etc) %>% pull(name)), # 构建城市匹配模式,同样按长度降序优先匹配长城市名 city_pattern = ifelse(length(city_candidates) > 0, str_c("\\b", str_replace_all(city_candidates, "\\s+", "\\\\s+"), "\\b", collapse = "|"), NA), # 从剩余文本中提取城市 name = str_extract(address_without_country, str_sort(city_pattern, decreasing = TRUE, by = str_length)) ) %>% ungroup() %>% # 处理特殊情况:当城市在国家前面时,反向检查提取 mutate( name = ifelse(is.na(name), str_extract(partial_address, str_sort(city_candidates, decreasing = TRUE, by = str_length)), name) ) %>% select(partial_address, name, country.etc) # 查看最终结果 print(df_final)
关键优化点说明
- 减少误匹配:
- 用
\\b(单词边界)包裹匹配模式,避免街道名中包含城市/国家名称的部分匹配(比如不会把"Colombia St"中的"Colombia"识别为国家)。 - 城市匹配严格限定在对应国家的候选池中,彻底杜绝跨国家的误匹配(比如哥伦比亚的城市Colombia不会匹配到其他国家的地址)。
- 优先匹配长名称,避免短名称覆盖长名称的情况(比如不会用
CONGO替换DEMOCRATIC REPUBLIC OF THE CONGO)。
- 用
- 提升性能:
- 替换O(n*m)复杂度的全量模糊连接为分步提取,运行速度随数据量增大的提升效果更显著。
- 每次仅处理当前国家的城市候选,大幅减少匹配的计算量。
结果验证
运行上述代码后,输出将完全符合预期:
partial_address name country.etc 1 LONDON 121280 ONTARIO CANADA LONDON CANADA 2 MILANO, ITALY MILAN ITALY 3 123 FIRST ST COLUMBUS OH USA COLUMBUS USA 4 CALI-VALLE DEL CAUCA COLOMBIA CALI COLOMBIA 5 98765 FRANCE PARIS PARIS FRANCE 6 ZURICH ZURICH SWITZERLAND ZURICH SWITZERLAND 7 MEXICO CITY MEXICO MEXICO CITY MEXICO 8 NAGOYA 123456 JAPAN NAGOYA JAPAN 9 BEIJING BEIJING CHINA BEIJING CHINA 10 THAILAND 12345 BANGKOK BANGKOK THAILAND 11 ALBANIA LA GUAHIRA COLOMBIA ALBANIA COLOMBIA 12 CONAKRY, GUINEA CONAKRY GUINEA 13 GUINEA-BISSAU, BISSAU BISSAU GUINEA-BISSAU 14 DEMOCRATIC REPUBLIC OF THE CONGO KINSHASA KINSHASA CONGO DEMOCRATIC REPUBLIC
内容的提问来源于stack exchange,提问作者Annabanana
相关产品推荐
相关产品推荐

