楼主: huapeng66
1413 6

[问答] 请问要遍历所有的邮编匹配地址,怎么提高循环效率? [推广有奖]

  • 6关注
  • 0粉丝

博士生

47%

还不是VIP/贵宾

-

威望
0
论坛币
5217 个
通用积分
7.0196
学术水平
3 点
热心指数
4 点
信用等级
2 点
经验
24876 点
帖子
133
精华
0
在线时间
320 小时
注册时间
2018-9-16
最后登录
2023-3-21

楼主
huapeng66 学生认证  发表于 2021-5-8 18:57:00 |AI写论文

+2 论坛币
k人 参与回答

经管之家送您一份

应届毕业生专属福利!

求职就业群
赵安豆老师微信:zhaoandou666

经管之家联合CDA

送您一个全额奖学金名额~ !

感谢您参与论坛问题回答

经管之家送您两个论坛币!

+2 论坛币
目前匹配的代码可以正常运行,但就是效率比较低,请问有啥优化的建议?
代码如下:
  1. #------------------------------导入包-------------------------------------------
  2. library(stringr)
  3. library(openxlsx)
  4. library(tidyverse)
  5. #-----------------------------筛选美国数据--------------------------------------
  6. data_us <- read.xlsx("path/data_us.xlsx") #导入数据集
  7. post <- read.xlsx("path/post.xlsx") #导入邮编数据
  8. testFuc <- function(data_us,post){
  9.   data_us$state <- data_us$city <- NA #新增州和城市变量
  10.   N <- NROW(post) #各城市邮编区间数量
  11.   for (i in 1:N){
  12.       templist <- post[i,5] #提取邮编
  13.       if (nchar(templist) <= 13){  #判断邮编区间长度是否小于等于13
  14.         startPost <- str_sub(templist,1,5) #提取起始邮编
  15.         endPost <- str_sub(templist,-5,-1) #提取末尾邮编
  16.         IdPost <- as.numeric(startPost):as.numeric(endPost) #按起始到末尾将邮编排列成向量
  17.         for (j in IdPost){
  18.           if(j < 10000){j <- paste0(0,j)} #处理0开头的邮编
  19.           IndexPost <- grepl(j,data_us$ap_addr) #在地址中匹配邮编
  20.           data_us$city[IndexPost] <- post[i,6]  #利用邮编填写邮编对应的城市
  21.           data_us$state[IndexPost] <- post[i,2] #利用邮编填写邮编对应的州
  22.         }
  23.       }else{ #判断邮编区间长度大于13的情况
  24.         startPost1 <- str_sub(templist,1,5)
  25.         endPost1 <- str_sub(templist,9,13)
  26.         startPost2 <- str_sub(templist,16,20)
  27.         endPost2 <- str_sub(templist,-5,-1)
  28.         IdPost <- c(as.numeric(startPost1):as.numeric(endPost1),as.numeric(startPost2):as.numeric(endPost2))
  29.         for(j in IdPost){
  30.           IndexPost <- grepl(j,data_us$ap_addr)
  31.           data_us$city[IndexPost] <- post[i,6]
  32.           data_us$state[IndexPost] <- post[i,2]
  33.         }
  34.       }
  35.   }
  36.   return(data_us) #返回数据表
  37. }

  38. newdata < testFuc(data_us,post) #调取函数
复制代码


二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

关键词:Library string BRARY tring xlsx

post.xlsx
下载链接: https://bbs.pinggu.org/a-3453783.html

88.91 KB

data_us.xlsx

639.31 KB

回帖推荐

zhou1_20 发表于5楼  查看完整内容

该问题分为两个任务:1. 提取data_us中邮编 2.重构post中索引. 最后合并. 下面的方法运行1.5秒即可,请参考。

owenqi 发表于2楼  查看完整内容

我试了一下楼主的代码,确实比较花时间,我个人的理解是,实际上你需要的是把ap_addr里面的邮编匹配post里面的邮编,那为了避免循环匹配,我建议这样: 1. 生成一个新的post的表,这个表完成所有的州和城市的邮编 2. 在data_us生成一列,专门储存邮编 3. 将这两个表以data_us为基础,做左连接(left_join), 以邮编作为键。 理论上循环应该可以完全避免,但我水平有限,在生成邮编表的时候,有些地方为了省事还是用了循环 我 ...

沙发
owenqi 在职认证  学生认证  发表于 2021-5-9 04:38:28
我试了一下楼主的代码,确实比较花时间,我个人的理解是,实际上你需要的是把ap_addr里面的邮编匹配post里面的邮编,那为了避免循环匹配,我建议这样:
1. 生成一个新的post的表,这个表完成所有的州和城市的邮编
2. 在data_us生成一列,专门储存邮编
3. 将这两个表以data_us为基础,做左连接(left_join), 以邮编作为键。
理论上循环应该可以完全避免,但我水平有限,在生成邮编表的时候,有些地方为了省事还是用了循环
我尝试了一下,这样应该几秒钟就可以出结果。
  1. library(dplyr)
  2. library(tibble)
  3. library(stringr)

  4. # for simplicity use en colnames
  5. colnames(post) <- c('srange', 'state', 'slink', 'spn', 'crange', 'city', 'clink', 'cpn')

  6. state_post <- post %>% group_by(state) %>% distinct(srange)
  7. city_post <- post %>% group_by(city) %>% distinct(crange)

  8. # manipulation on Washington DC, the only special postal range
  9. washington <- state_post[11,]
  10. state_post <- state_post[-11,]
  11. # according to the format, there is a space after the comma
  12. washington <- tibble(srange = unlist(str_split(washington$srange, ', ')),
  13.                      state = washington$state)

  14. state_post <- state_post %>% bind_rows(washington) %>% arrange(srange)

  15. # manipulating state postal codes
  16. sp_start <- str_sub(state_post$srange, end = 5) %>% as.numeric
  17. sp_end <- str_sub(state_post$srange, start = 9) %>% as.numeric

  18. # all possible state postal codes
  19. sp_list <- list()
  20. for(i in 1: length(sp_start))
  21.   sp_list[[i]] <- tibble(postal = sprintf("%05d", seq(sp_start[i], sp_end[i])),
  22.                          state = state_post$state[i])
  23. sp_tbl <- bind_rows(sp_list)

  24. # manipulation on cities with two seq of postal code
  25. cp_long <- city_post %>% filter(nchar(crange) > 13)
  26. cnames <- cp_long$city
  27. cnames <- rep(cnames, each = 2)
  28. cpost_spl <- unlist(sapply(cp_long$crange, str_split, ', '))
  29. cp_long <- tibble(crange = cpost_spl, city = cnames)

  30. cp_short <- city_post %>% filter(nchar(crange) == 13)
  31. city_post <- bind_rows(cp_short, cp_long) %>% arrange(crange)

  32. cp_start <- str_sub(city_post$crange, end = 5) %>% as.numeric
  33. cp_end <- str_sub(city_post$crange, start = 9) %>% as.numeric

  34. sc_list <- list()
  35. for(i in 1:length(cp_start))
  36.   sc_list[[i]] <- tibble(postal = sprintf("%05d", seq(cp_start[i], cp_end[i])),
  37.                     city = city_post$city[i])
  38. sc_tbl <- bind_rows(sc_list)

  39. # merging state and cities matching by postal code
  40. post_info <- left_join(sc_tbl, sp_tbl)

  41. # extract US postal code (5-digit) from ap_addr
  42. data_us <- data_us %>% mutate(postal = str_match(ap_addr, '\\b\\d{5}\\b'))
  43. add_info <- left_join(data_us, post_info)
复制代码


我留意到你这个数据存在一些问题,导致最后有一些重复项
1. 有的邮编对应多于一个城市和地区,比如‘11102’可以对应纽约,长岛和Astoria
2. 有的邮编地址里面写明了城市,但是在post里面是找不到的,比如‘01701’对应的是麻省的‘Framingham’,但是post里面的邮编范围是不包括这个城市的。
3. 有的人包括多于两个以上的美国地址,比如data_us第23行,包含了95054(加州圣克拉拉)和97124(俄勒冈希尔斯堡), 这个问题我没具体处理,但是实际操作相当于你把这个地址复制含有n个独立的邮编复制n次。
已有 1 人评分学术水平 热心指数 信用等级 收起 理由
Sunknownay + 6 + 6 + 6 热心帮助其他会员

总评分: 学术水平 + 6  热心指数 + 6  信用等级 + 6   查看全部评分

藤椅
huapeng66 学生认证  发表于 2021-5-10 08:56:12
owenqi 发表于 2021-5-9 04:38
我试了一下楼主的代码,确实比较花时间,我个人的理解是,实际上你需要的是把ap_addr里面的邮编匹配post里 ...
谢谢,您的建议非常棒!新学了很多技能!

针对数据的几点疑问:
1、我提供的邮编段是没有全覆盖整个美国的邮编;
2、是存在多个地址放在一起的,用分号分隔;

我还有两点疑问,请帮忙建议:
1、如果地址中路标包含数字,刚好5个数字的话是否会被误判为邮编?
2、目前美国数据是刚好邮编为5位数,其他有些国家的邮编还含有字母和数字组合,或者是位数不一致的情况。通过正则将邮编提出来似乎比较困难,如果未提出邮编除了grepl()循环,还有没有其他好的建议?

板凳
owenqi 在职认证  学生认证  发表于 2021-5-10 10:25:44
huapeng66 发表于 2021-5-10 08:56
谢谢,您的建议非常棒!新学了很多技能!

针对数据的几点疑问:
1.是会被误判的,比如你数据中的第10256行(10161 W. Park Run Drive, Suite 150  Las Vegas, Nevada 89145),可能的解决方法是,你从这个字符串的最后往前判断,这样可以有效规避一些路名。这里比较优雅的方法我不太清楚,我自己的话,就用的是stringi包里面的stri_match_last_regex,把第54行变成
  1. library(stringi)
  2. data_us <- data_us %>% mutate(postal = stri_match_last_regex(ap_addr, ‘\\d{5}‘’))
复制代码
2. 邮编这个因为还是比较规范,所以还是建议正则表达式,你可以把他复制给一个变量,然后去维护这个变量,举个例子,加拿大的邮编是6位,数字字母混搭,然后要满足,不能使用“DFIOQU”,而且首字母不能使用WZ,那这样的话,你要用的正则表达式就比较复杂,但可以表示为。
  1. regex <- '[ABCEGHJKLMNPRSTVXY][0-9][ABCEGHJKLMNPRSTVWXYZ] ?[0-9][ABCEGHJKLMNPRSTVWXYZ][0-9]'
复制代码
关于正则表达式,我也是入门,都是在网上查的,具体也给不出什么简洁的解法。

3. 关于你提到的ap和ap_addr存放多个地址,这个我有留意到,这个可以用str_split去分开,不过我也注意到,有的你这里的数据中有部分(大概是48个还是47个我记不清了)情况下,n个人对应2n个地址,其中可能的原因是有的地址一遍是中文一遍是英文。还有大概5-6个情况是n个人却有m个地址,其中n,m互质。这种情况出现不多,可以人工判断。方法综合之前我区分邮编长度是否为13的是差不多的。这里不知道你是那数据作为练习还是实际需求,就不深入讨论了。

报纸
zhou1_20 发表于 2021-5-10 11:45:59
该问题分为两个任务:1. 提取data_us中邮编 2.重构post中索引. 最后合并. 下面的方法运行1.5秒即可,请参考。

  1. library(stringr)
  2. library(openxlsx)
  3. library(tidyverse)
  4. library(rlist)

  5. data_us <- read.xlsx("data_us.xlsx") #导入数据集
  6. post <- read.xlsx("post.xlsx") #导入邮编数据


  7. # 提取有所邮编,"99999"代表空邮编
  8. ST = Sys.time()
  9. post_us <-
  10. data_us$ap_addr%>%str_extract_all('\\d{5}') %>%
  11.   list.apply(function(x) ifelse(length(x)!=0,tail(x,1),'99999')) %>%
  12.   list.apply(function(x) ifelse(is.na(x),'99999',x))%>%unlist()

  13. data_us$post_us = post_us

  14. # 重新定义post_us索引
  15. posts <-
  16. post[,c(2,5,6)]%>%group_split(城市邮政编码)%>%
  17.   list.apply(
  18.     function(x){
  19.       post_us <-x$城市邮政编码 %>%str_split(',')%>%unlist()%>%
  20.         str_split('-')%>%
  21.         lapply(function(x){
  22.           x1 = x%>%str_trim()%>%as.numeric()
  23.           x1[1]:x1[2]
  24.         })%>%unlist()%>% as.character()%>%
  25.         sapply(function(x) ifelse(nchar(x)==4, paste0(0,x), x))%>%
  26.         unlist()
  27.       x%>%merge(data.frame(post_us = post_us))
  28.     }
  29.   )%>%list.rbind()

  30. # 合并
  31. data_usnew <- left_join(data_us,posts[,-2],by="post_us")

  32. Sys.time() - ST
复制代码
已有 2 人评分论坛币 学术水平 热心指数 信用等级 收起 理由
Sunknownay + 3 + 3 + 3 热心帮助其他会员
owenqi + 5 优雅

总评分: 论坛币 + 5  学术水平 + 3  热心指数 + 3  信用等级 + 3   查看全部评分

地板
huapeng66 学生认证  发表于 2021-5-10 13:31:30
zhou1_20 发表于 2021-5-10 11:45
该问题分为两个任务:1. 提取data_us中邮编 2.重构post中索引. 最后合并. 下面的方法运行1.5秒即可,请参考 ...
谢谢,领教了。简洁明了!

7
huapeng66 学生认证  发表于 2021-5-10 13:33:42
owenqi 发表于 2021-5-10 10:25
1.是会被误判的,比如你数据中的第10256行(10161 W. Park Run Drive, Suite 150  Las Vegas, Nevada 891 ...
学习了,谢谢!

您需要登录后才可以回帖 登录 | 我要注册

本版微信群
加好友,备注cda
拉您进交流群
GMT+8, 2026-1-9 03:16