Dplyr:清洁双管姓氏
Dplyr: Clean double-barrelled surnames
我有一个 data.frame 的名字,如下所示,其中有一些姓氏的样本,后跟首字母(例如 Smith S 或 Lopez-Garcia M):
df<-data.frame(names=c("Adu-Amankwah E",
"Smith Dawson E",
"Lopez-Garcia M",
"Lopez Garcia MA",
"Garcia MAC",
"Lopez Garcia MA",
"Garcia MAC"))
我想把那些双管齐下的姓氏全部抽出来稍微清理一下:
- 找出带有连字符 (-) 或两个姓氏(例如 Lopez Garcia)的任何一个。
- 我需要替换以下任何一项:Lopez Garcia MA、Lopez-Garcia MA 或 Garcia MAC 和 Lopez-Garcia M。而 Smith Dawson E 应该是 Smith-Dawson E.
输出如下:
df<-data.frame(names=c("Adu-Amankwah E",
"Smith-Dawson E",
"Lopez-Garcia M",
"Lopez-Garcia M",
"Lopez-Garcia M",
"Lopez-Garcia M",
"Lopez-Garcia M"))
正如我在 中提到的,这里的挑战不是 解析 character
字符串,而是定义 逻辑到
- 在代表性标签 (
"Lopez-Garcia M"
) 下关联同名变体(例如 "Garcia MAC"
、"Lopez Garcia MA"
); 现在还在
- 避免将不同名称(如
"Andy Garcia"
)的相似变体(如 "Garcia A"
)混为一谈。
因此,您最好的方法可能是为名称的已知变体定义 mapping
table。
文字映射
文字映射涉及在其真正代表的名称旁边键入每个已知变体。
mapping_lit <- data.frame(
True_Name = c("Adu-Amankwah E", "Smith-Dawson E", "Lopez-Garcia M", "Lopez-Garcia M", "Lopez-Garcia M"),
Variant = c("Adu-Amankwah E", "Smith Dawson E", "Lopez-Garcia M", "Lopez Garcia MA", "Garcia MAC")
)
mapping_lit
#> True_Name Variant
#> 1 Adu-Amankwah E Adu-Amankwah E
#> 2 Smith-Dawson E Smith Dawson E
#> 3 Lopez-Garcia M Lopez-Garcia M
#> 4 Lopez-Garcia M Lopez Garcia MA
#> 5 Lopez-Garcia M Garcia MAC
一旦你有了 mapping
,一个简单的 dplyr::*_join()
就可以了
library(dplyr)
# The LEFT JOIN preserves any names without matches, so you can handle them as you wish.
left_join(
df,
mapping_lit,
by = c("names" = "Variant")
)
结果如下:
names True_Name
1 Adu-Amankwah E Adu-Amankwah E
2 Smith Dawson E Smith-Dawson E
3 Lopez-Garcia M Lopez-Garcia M
4 Lopez Garcia MA Lopez-Garcia M
5 Garcia MAC Lopez-Garcia M
6 Lopez Garcia MA Lopez-Garcia M
7 Garcia MAC Lopez-Garcia M
正则表达式映射
如果您对 regular expressions 足够熟练,您可以只定义一个正则表达式来表示每个 True_Name
:
上的所有变体
mapping_rgx <- data.frame(
True_Name = c("Adu-Amankwah E", "Smith-Dawson E", "Lopez-Garcia M"),
Pattern = c("^(Adu[- ]?)?Amankwah( E)?$", "^(Smith[- ]?)?Dawson( E)?$", "^(Lopez[- ]?)?Garcia( M(AC?)?)?$")
)
mapping_rgx
#> True_Name Pattern
#> 1 Adu-Amankwah E ^(Adu[- ]?)?Amankwah( E)?$
#> 2 Smith-Dawson E ^(Smith[- ]?)?Dawson( E)?$
#> 3 Lopez-Garcia M ^(Lopez[- ]?)?Garcia( M(AC?)?)?$
完成此映射后,您将需要 fuzzyjoin::regex_*_join()
来匹配变体
library(fuzzyjoin)
# The LEFT JOIN preserves any names without matches, so you can handle them as you wish.
regex_left_join(
df,
mapping_rgx,
by = c("names" = "Pattern"),
# Account for typos in capitalization.
ignore_case = TRUE
)
结果如下:
names True_Name Pattern
1 Adu-Amankwah E Adu-Amankwah E (Adu[- ]?)?Amankwah( E)?
2 Smith Dawson E Smith-Dawson E (Smith[- ]?)?Dawson( E)?
3 Lopez-Garcia M Lopez-Garcia M ^(Lopez[- ]?)?Garcia( M(AC?)?)?$
4 Lopez Garcia MA Lopez-Garcia M ^(Lopez[- ]?)?Garcia( M(AC?)?)?$
5 Garcia MAC Lopez-Garcia M ^(Lopez[- ]?)?Garcia( M(AC?)?)?$
6 Lopez Garcia MA Lopez-Garcia M ^(Lopez[- ]?)?Garcia( M(AC?)?)?$
7 Garcia MAC Lopez-Garcia M ^(Lopez[- ]?)?Garcia( M(AC?)?)?$
警告
因为我也, I might not recommend a stringdist
遇到这种情况。每个名字不仅在拼写上不同,而且在结构上也不同。两个不同人
的两个结构相似的条目完全有可能
Variant
True_Name
Garcia A
Andy Garcia
Garcia MAC
Lopez-Garcia M
Lopez-Garcia M
Lopez-Garcia M
与 相同 名称的两个不同结构变体相比,字符串距离更短:
# Run the full gamut of methods for 'stringdist::stringdist()'.
methods <- c(
"osa", "lv", "dl", "hamming", "lcs", "qgram",
"cosine", "jaccard", "jw", "soundex"
)
# Display string distances for variants of the same and of different names:
rbind(
# Compare different names.
sapply(X = methods, FUN = function(x) {stringdist::stringdist(
a = "Garcia MAC", b = "Garcia A",
method = x
)}),
# Compare variations on the same name.
sapply(X = methods, FUN = function(x) {stringdist::stringdist(
a = "Garcia MAC", b = "Lopez-Garcia M",
method = x
)})
)
#> osa lv dl hamming lcs qgram cosine jaccard jw soundex
#> [1,] 2 2 2 Inf 2 2 0.08712907 0.2222222 0.06666667 1
#> [2,] 8 8 8 Inf 8 8 0.27831216 0.5333333 0.20952381 1
我有一个 data.frame 的名字,如下所示,其中有一些姓氏的样本,后跟首字母(例如 Smith S 或 Lopez-Garcia M):
df<-data.frame(names=c("Adu-Amankwah E",
"Smith Dawson E",
"Lopez-Garcia M",
"Lopez Garcia MA",
"Garcia MAC",
"Lopez Garcia MA",
"Garcia MAC"))
我想把那些双管齐下的姓氏全部抽出来稍微清理一下:
- 找出带有连字符 (-) 或两个姓氏(例如 Lopez Garcia)的任何一个。
- 我需要替换以下任何一项:Lopez Garcia MA、Lopez-Garcia MA 或 Garcia MAC 和 Lopez-Garcia M。而 Smith Dawson E 应该是 Smith-Dawson E.
输出如下:
df<-data.frame(names=c("Adu-Amankwah E",
"Smith-Dawson E",
"Lopez-Garcia M",
"Lopez-Garcia M",
"Lopez-Garcia M",
"Lopez-Garcia M",
"Lopez-Garcia M"))
正如我在 character
字符串,而是定义 逻辑到
- 在代表性标签 (
"Lopez-Garcia M"
) 下关联同名变体(例如"Garcia MAC"
、"Lopez Garcia MA"
); 现在还在 - 避免将不同名称(如
"Andy Garcia"
)的相似变体(如"Garcia A"
)混为一谈。
因此,您最好的方法可能是为名称的已知变体定义 mapping
table。
文字映射
文字映射涉及在其真正代表的名称旁边键入每个已知变体。
mapping_lit <- data.frame(
True_Name = c("Adu-Amankwah E", "Smith-Dawson E", "Lopez-Garcia M", "Lopez-Garcia M", "Lopez-Garcia M"),
Variant = c("Adu-Amankwah E", "Smith Dawson E", "Lopez-Garcia M", "Lopez Garcia MA", "Garcia MAC")
)
mapping_lit
#> True_Name Variant
#> 1 Adu-Amankwah E Adu-Amankwah E
#> 2 Smith-Dawson E Smith Dawson E
#> 3 Lopez-Garcia M Lopez-Garcia M
#> 4 Lopez-Garcia M Lopez Garcia MA
#> 5 Lopez-Garcia M Garcia MAC
一旦你有了 mapping
,一个简单的 dplyr::*_join()
就可以了
library(dplyr)
# The LEFT JOIN preserves any names without matches, so you can handle them as you wish.
left_join(
df,
mapping_lit,
by = c("names" = "Variant")
)
结果如下:
names True_Name
1 Adu-Amankwah E Adu-Amankwah E
2 Smith Dawson E Smith-Dawson E
3 Lopez-Garcia M Lopez-Garcia M
4 Lopez Garcia MA Lopez-Garcia M
5 Garcia MAC Lopez-Garcia M
6 Lopez Garcia MA Lopez-Garcia M
7 Garcia MAC Lopez-Garcia M
正则表达式映射
如果您对 regular expressions 足够熟练,您可以只定义一个正则表达式来表示每个 True_Name
:
mapping_rgx <- data.frame(
True_Name = c("Adu-Amankwah E", "Smith-Dawson E", "Lopez-Garcia M"),
Pattern = c("^(Adu[- ]?)?Amankwah( E)?$", "^(Smith[- ]?)?Dawson( E)?$", "^(Lopez[- ]?)?Garcia( M(AC?)?)?$")
)
mapping_rgx
#> True_Name Pattern
#> 1 Adu-Amankwah E ^(Adu[- ]?)?Amankwah( E)?$
#> 2 Smith-Dawson E ^(Smith[- ]?)?Dawson( E)?$
#> 3 Lopez-Garcia M ^(Lopez[- ]?)?Garcia( M(AC?)?)?$
完成此映射后,您将需要 fuzzyjoin::regex_*_join()
来匹配变体
library(fuzzyjoin)
# The LEFT JOIN preserves any names without matches, so you can handle them as you wish.
regex_left_join(
df,
mapping_rgx,
by = c("names" = "Pattern"),
# Account for typos in capitalization.
ignore_case = TRUE
)
结果如下:
names True_Name Pattern
1 Adu-Amankwah E Adu-Amankwah E (Adu[- ]?)?Amankwah( E)?
2 Smith Dawson E Smith-Dawson E (Smith[- ]?)?Dawson( E)?
3 Lopez-Garcia M Lopez-Garcia M ^(Lopez[- ]?)?Garcia( M(AC?)?)?$
4 Lopez Garcia MA Lopez-Garcia M ^(Lopez[- ]?)?Garcia( M(AC?)?)?$
5 Garcia MAC Lopez-Garcia M ^(Lopez[- ]?)?Garcia( M(AC?)?)?$
6 Lopez Garcia MA Lopez-Garcia M ^(Lopez[- ]?)?Garcia( M(AC?)?)?$
7 Garcia MAC Lopez-Garcia M ^(Lopez[- ]?)?Garcia( M(AC?)?)?$
警告
因为我也stringdist
遇到这种情况。每个名字不仅在拼写上不同,而且在结构上也不同。两个不同人
Variant | True_Name |
---|---|
Garcia A | Andy Garcia |
Garcia MAC | Lopez-Garcia M |
Lopez-Garcia M | Lopez-Garcia M |
与 相同 名称的两个不同结构变体相比,字符串距离更短:
# Run the full gamut of methods for 'stringdist::stringdist()'.
methods <- c(
"osa", "lv", "dl", "hamming", "lcs", "qgram",
"cosine", "jaccard", "jw", "soundex"
)
# Display string distances for variants of the same and of different names:
rbind(
# Compare different names.
sapply(X = methods, FUN = function(x) {stringdist::stringdist(
a = "Garcia MAC", b = "Garcia A",
method = x
)}),
# Compare variations on the same name.
sapply(X = methods, FUN = function(x) {stringdist::stringdist(
a = "Garcia MAC", b = "Lopez-Garcia M",
method = x
)})
)
#> osa lv dl hamming lcs qgram cosine jaccard jw soundex
#> [1,] 2 2 2 Inf 2 2 0.08712907 0.2222222 0.06666667 1
#> [2,] 8 8 8 Inf 8 8 0.27831216 0.5333333 0.20952381 1