替换R

时间:2018-04-06 17:22:34

标签: r regex string stringi

给定英国邮政编码的“模板”,例如“A9 9AA”,其中“A”是字母占位符,“9”是数字占位符,我想生成随机邮政编码字符串,如“H8 4GB” 。字母可以是任何大写字母,编号从0到9。

因此,如果模板是“AA9A 9AA”,那么我想要像“WC1A 9LK”这样的字符串。我现在忽略了生成“真正的”邮政编码,所以如果“WC1A”是一个有效的外向代码,我不会感到困扰。

我试图让stringi包中的函数起作用,但问题似乎是替换或匹配模板中的“A”只会替换第一个替换,例如:

 stri_replace_all_fixed("A9 9AA",c("A","A","A"), c("X","Y","Z"), vectorize_all=FALSE)
[1] "X9 9XX"

所以它不会用替换向量中的每个元素替换每个“A”(但这是设计)。

也许stringi或者基础R中有一些我错过的东西 - 我想把它放在那些包中,所以我不会臃肿我正在做的事情。

蛮力方法是分割模板,进行替换,将结果粘贴在一起,但我想看看是否有更快,自然的矢量化解决方案。

总结一下:

foo("A9 9AA") # return like "B6 5DE"
foo(c("A9 9AA","A9 9AA","A9A 9AA")) # returns c("Y6 5TH","D4 8JH","W0Z 3KQ")

这是一个非矢量化版本,它依赖于构建表达式并对其进行评估......

random_pc <- function(fmt){
    cc = gsub(" ",'c(" ")',gsub("9","sample(0:9,1)",gsub("A","sample(LETTERS,1)",strsplit(fmt,"")[[1]])))
    paste(eval(parse(text=paste0("c(",paste(cc,collapse=","),")"))),collapse="")    
}

> random_pc("AA9 9AA")
[1] "KO6 1AY"

3 个答案:

答案 0 :(得分:4)

据我了解,OP希望以指定的格式随机创建UK POST CODE。我认为sprintf可以提供帮助:

sprintf("%s%s %d%d%s", sample(LETTERS,1),sample(LETTERS,1), sample(0:9,1),
                sample(0:9,1), sample(LETTERS,1) )
#1] "BC 59D"

现在,如果目的是使用9A提供格式,那么步骤将首先用9%d替换A { {1}}。

选项#2

使用%spaste0使用自定义函数可以实现另一个选项:

sapply

答案 1 :(得分:1)

这是一个解决方案(矢量化懒惰的方式),它分割格式然后根据字符或数字替换:

randpc <- Vectorize(function(s){
    s = strsplit(s,"")[[1]]
    NUMS = as.character(0:9)
    nLet = sum(s %in% LETTERS)
    nDig = sum(s %in% NUMS)
    s[s %in% LETTERS] = sample(LETTERS, nLet, replace=TRUE)
    s[s %in% NUMS] = sample(NUMS, nDig, replace=TRUE)
    paste0(s, collapse="")
})

返回显示格式字符串的命名向量具有有用的副作用:

> randpc(c("AA9 9AA","A9 9AA"))
  AA9 9AA    A9 9AA 
"QS4 4LW"  "S9 7EU" 

它的灵活性在于它可以根据另一个邮政编码创建邮政编码,因为它接受格式字符串中的任何字母或数字:

> randpc(rep("LA1 4YF",3))
  LA1 4YF   LA1 4YF   LA1 4YF 
"OL2 5OJ" "YK3 3YB" "FV0 1LW" 

答案 2 :(得分:0)

我不确定什么算是蛮力,因为字符串上的拆分替换组合工作流对我来说似乎最直观。但是,我的第一次尝试非常慢,有很多模板。我也曾希望stri_replace_all(replacement = sample(LETTERS, 1))这样的东西能起作用,但它也只能用同一个字母代替。

这是使用stri_replace_first稍微不同的方法,替换模板字符的第一个实例,直到没有模板字符为止。这意味着我将模板切换为字母小写l和数字n,因为邮政编码只有大写字母和数字(据我所知)。我认为100k模板的运行时间更合理(~10秒),这也仅使用stringi

library(stringi)

make_postcodes <- function(templates){
  postcodes <- templates
  while (any(stri_detect_regex(postcodes, "l|n"))){
    for (i in 1:length(templates)){
      postcodes[i] <- stri_replace_first_fixed(
        str = postcodes[i],
        pattern = "l",
        replacement = sample(LETTERS, 1)
        )
      postcodes[i] <- stri_replace_first_fixed(
        str = postcodes[i],
        pattern = "n",
        replacement = sample(0:9, 1)
        )
    }
  }
  postcodes
}

make_postcodes("ln nll")
#> [1] "W8 3MX"
make_postcodes(c("ln nll", "ln nll", "lnl nll"))
#> [1] "H1 6TN"  "C5 6YI"  "A3I 2DB"

test = stri_trim_both(stri_rand_strings(100000, sample(5:9, 1), pattern = "[nl\\ ]"))
tictoc::tic("Time to convert 100,000 templates")
x <- make_postcodes(test)
tictoc::toc()
#> Time to convert 100,000 templates: 12.03 sec elapsed
head(test)
#> [1] "lnnl"  "ll l"  "nl n"  "ll  l" "ll l"  "ll n"
head(x)
#> [1] "G91U"  "HU N"  "2Q 7"  "EU  Z" "PD I"  "SM 4"

reprex package(v0.2.0)创建于2018-04-06。