在字符串中替换外文字符

13

我正在处理大量数据,其中大部分是含有非英文字符的姓名。我的目标是将这些姓名与在美国收集的关于它们的某些信息匹配。

例如,我可能想要将名字“Sølvsten”(来自某个名字列表)与“Soelvsten”(存储在某个美国数据库中的名称)进行匹配。以下是我编写的一个函数,用于执行此操作。显然,这个函数很笨拙并且有点随意,但我想知道是否有一个简单的R函数可以将这些外语字符转换为最接近的英语邻居。我知道可能没有标准的方法来进行这种转换,但我只是好奇是否存在这样的转换,并且是否可以通过R函数完成该转换。

# a function to replace foreign characters
replaceforeignchars <- function(x)
{
    require(gsubfn);
    x <- gsub("š","s",x)
    x <- gsub("œ","oe",x)
    x <- gsub("ž","z",x)
    x <- gsub("ß","ss",x)
    x <- gsub("þ","y",x)
    x <- gsub("à","a",x)
    x <- gsub("á","a",x)
    x <- gsub("â","a",x)
    x <- gsub("ã","a",x)
    x <- gsub("ä","a",x)
    x <- gsub("å","a",x)
    x <- gsub("æ","ae",x)
    x <- gsub("ç","c",x)
    x <- gsub("è","e",x)
    x <- gsub("é","e",x)
    x <- gsub("ê","e",x)
    x <- gsub("ë","e",x)
    x <- gsub("ì","i",x)
    x <- gsub("í","i",x)
    x <- gsub("î","i",x)
    x <- gsub("ï","i",x)
    x <- gsub("ð","d",x)
    x <- gsub("ñ","n",x)
    x <- gsub("ò","o",x)
    x <- gsub("ó","o",x)
    x <- gsub("ô","o",x)
    x <- gsub("õ","o",x)
    x <- gsub("ö","o",x)
    x <- gsub("ø","oe",x)
    x <- gsub("ù","u",x)
    x <- gsub("ú","u",x)
    x <- gsub("û","u",x)
    x <- gsub("ü","u",x)
    x <- gsub("ý","y",x)
    x <- gsub("ÿ","y",x)
    x <- gsub("ğ","g",x)

    return(x)
}

注意:我知道存在像Jaro Winkler距离匹配这样的名称匹配算法,但我更愿意做精确匹配。

6个回答

21

尝试使用 chartr R函数进行单字符替换(这应该非常快),然后使用一系列的gsub调用来清理每个一对两个字符替换(这可能会更慢,但它们并不多)。

to.plain <- function(s) {

   # 1 character substitutions
   old1 <- "šžþàáâãäåçèéêëìíîïðñòóôõöùúûüý"
   new1 <- "szyaaaaaaceeeeiiiidnooooouuuuy"
   s1 <- chartr(old1, new1, s)

   # 2 character substitutions
   old2 <- c("œ", "ß", "æ", "ø")
   new2 <- c("oe", "ss", "ae", "oe")
   s2 <- s1
   for(i in seq_along(old2)) s2 <- gsub(old2[i], new2[i], s2, fixed = TRUE)

   s2
}

根据需要将old1new1old2new2添加其中。

这是一个测试:

> s <- "æxš"
> to.plain(s)
[1] "aexs"

更新:在chartr中更正了变量名称。


谢谢,Gabor(我假定你和 http://r.789695.n4.nabble.com/template/NamlServlet.jtp?macro=user_nodes&user=39147 是同一个人)。我测试了到目前为止发布的所有三个解决方案,这看起来是最快的(尽管我只观察了执行时间,并没有真正计时,并且它是在一台没有插电的笔记本电脑上运行的,所以谁知道什么在驱动效率:-))。 - krishnan
难道不应该是s1 <- chatr(old1,new1,s)吗? - Lucarno
谢谢。是的。现在已经修复了。 - G. Grothendieck
我在这里遇到了编码问题。在Windows上,使用chartr::base可以用于单个字符的替换,但是对包含UTF-8内容“œ”的向量进行多字符替换的循环不起作用。(其余的连字号都正常工作。)我的解决方法(咳咳iconv(s,“UTF-8”,“latin1”)咳咳)产生了一个问题: “œ”被转换为“o”(由iconv::base),而不是由循环转换为“oe”。我猜这是由于它在ISO-8859-1中被省略造成的,但我找不到解决方案。有什么想法吗? - aae
通过使用stringi :: stri_trans_general(“œ”,“Latin-ASCII”)解决了问题,它可以做到iconv()和gsub()无法做到的。 - aae
这很棒,但我在 Kaggle 的科学论文语料库上尝试了一下,似乎会在被解释为",â€"的任何地方出现错误,只是提供信息而已。chartr(old1, new1, s)中的无效输入 'Using this “cancer pathway approach,†TSGs regulating cell signaling, `。 - Hack-R

12

为了可能获得更好的结果而进行编辑...

这种方法可能并不适用于所有情况,但是可以值得研究iconv。从?iconv中得知:

Description:

 This uses system facilities to convert a character vector between
 encodings: the ‘i’ stands for ‘internationalization’.

例子:

test <- c("Sølvsten", "Günther")
iconv(test, "latin1", "ASCII//TRANSLIT")
#[1] "Solvsten" "Gunther" 

我认为将数据与代码分离有其优点,虽然这并不是非常简化。这与以下问题非常相似:

R: 使用gsub替换字符,如何创建函数?

定义“from”和“to”:

fromto <- read.table(text="
from to
š s
œ oe
ž z
ß ss
þ y
à a
á a
â a
ã a
ä a
å a
æ ae
ç c
è e
é e
ê e
ë e
ì i
í i
î i
ï i
ð d
ñ n
ò o
ó o
ô o
õ o
ö o
ø oe
ù u
ú u
û u
ü u
ý y
ÿ y
ğ g",header=TRUE)

那么函数:

replaceforeignchars <- function(dat,fromto) {
  for(i in 1:nrow(fromto) ) {
    dat <- gsub(fromto$from[i],fromto$to[i],dat)
  }
  dat
}

test <- c("Sølvsten", "Günther")
replaceforeignchars(test,fromto)
#[1] "Soelvsten" "Gunther"

8
您可以安装uni2ascii C程序,并从R中调用它。
uni2ascii <- function(string) {
    cmd <- sprintf("echo %s | uni2ascii -B", string)
    system(cmd, intern = TRUE, ignore.stderr = TRUE)
}

uni2ascii <- Vectorize(uni2ascii, USE.NAMES = FALSE)

uni2ascii(c("Sølvsten", "ğ", "œ"))
## [1] "Solvsten" "g"        "oe"

4

同时,您也可以使用stringi包中的stri_trans_general()函数。

library(stringi)

x <- c("š", "ž", "ğ", "ß", "þ", "à", "á", "â", "ã", "ä", "å", "æ", 
       "ç", "è", "é", "ê", "ë", "ì", "í", "î", "ï", "ð", "ñ", "ò", 
       "ó", "ô", "õ", "ö", "ø", "œ", "ù", "ú", "û", "ü", "ý", "ÿ")
y <- stri_trans_general(x, "Latin-ASCII")

data.frame(x, y, stringsAsFactors = FALSE)
#>    x  y
#> 1  š  s
#> 2  ž  z
#> 3  ğ  g
#> 4  ß ss
#> 5  þ th
#> 6  à  a
#> 7  á  a
#> 8  â  a
#> 9  ã  a
#> 10 ä  a
#> 11 å  a
#> 12 æ ae
#> 13 ç  c
#> 14 è  e
#> 15 é  e
#> 16 ê  e
#> 17 ë  e
#> 18 ì  i
#> 19 í  i
#> 20 î  i
#> 21 ï  i
#> 22 ð  d
#> 23 ñ  n
#> 24 ò  o
#> 25 ó  o
#> 26 ô  o
#> 27 õ  o
#> 28 ö  o
#> 29 ø  o
#> 30 œ oe
#> 31 ù  u
#> 32 ú  u
#> 33 û  u
#> 34 ü  u
#> 35 ý  y
#> 36 ÿ  y

请注意,这将把“ø”转换为“o”。
stri_trans_general("Sølvsten", "Latin-ASCII")
#> [1] "Solvsten"

1
扩展thelatemail的答案:原始的replaceforeignchars函数包含一个循环,对于大文本可能会消耗资源。 这里有一个应用函数,它不需要显式循环就可以完全实现相同的功能。目前,它适用于单个字符串(例如不是字符串向量)。
replaceforeignchars <- function(dat,fromto) {
   paste0(apply(matrix(unlist(strsplit(dat,""))),1,FUN=function(x) {ifelse(x %in% fromto$from, as.character( fromto[fromto$from==x, 'to']),  x)}), collapse="") 
} 
test <- c("Sølvsten")
replaceforeignchars(test,fromto)
[1] "Solvsten"

1

在dpprdan的回答基础上,再加上使用stringi::stri_trans_general,您可以定义转换中的自定义规则/偏差。在我的经验中,使用"Latin-ASCII"stri_trans_general中,有9次中的9次能够给我期望的转换结果。

在我的情况下,我希望字母ø被转换为oe,字母å被转换为aa。而"Latin-ASCII"的正常行为将分别返回oa

## Define custom rules for å and ø, otherwise transliterate according to Latin-ASCII
custom_rules <- "å > aa;
                 ø > oe;
                 ::Latin-ASCII;"

stringi::stri_trans_general(c("Tårnby", "Søborg"), id = custom_rules, rules = TRUE)
[1] "Taarnby" "Soeborg"

它是大小写敏感的,因此如果您的数据中出现大写字母,您需要定义它们。个人而言,我只是将所有文本转换为小写。我从音译数据中制作了一个列表,其中包含音译前后的字母,以跟踪任何意外行为。
到目前为止,我的数据中已经有33个“奇怪”的字母被音译,而øå是我想要不同规则的仅有的两个字母。
有关自定义规则的更多信息,请单击此处。如果您希望音译过程中忽略某个字母,可以在此处找到示例。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接