说我有这两个数据框:
> df1 <- data.frame(name = c('John Doe',
'Jane F. Doe',
'Mark Smith Simpson',
'Sam Lee'))
> df1
name
1 John Doe
2 Jane F. Doe
3 Mark Smith Simpson
4 Sam Lee
> df2 <- data.frame(family = c('Doe', 'Smith'), size = c(2, 6))
> df2
family size
1 Doe 2
2 Smith 6
我想合并两个数据帧以获得这个:
name family size
1 John Doe Doe 2
2 Jane F. Doe Doe 2
3 Mark Smith Simpson Smith 6
4 Sam Lee <NA> NA
但除了以下非常复杂的解决方案之外,我无法绕过这样做的方式,这对我的真实数据变得非常混乱,这些数据有超过100个&#34;姓氏&#34; :
> df3 <- within(df1, {
family <- ifelse(test = grepl('Doe', name),
yes = 'Doe',
no = ifelse(test = grepl('Smith', name),
yes = 'Smith',
no = NA))
})
> merge(df3, df2, all.x = TRUE)
family name size
1 Doe John Doe 2
2 Doe Jane F. Doe 2
3 Smith Mark Smith Simpson 6
4 <NA> Sam Lee NA
我已尝试查看pmatch
以及R partial match in data frame提供的解决方案,但仍无法找到我正在寻找的内容。
答案 0 :(得分:1)
以下是一种策略,您可以lapply
使用grep
匹配所有姓氏。这将在任何位置找到它们。首先让我定义一个辅助函数
transindex<-function(start=1) {
function(x) {
start<<-start+1
ifelse(x, start-1, NA)
}
}
我也将使用函数coalesce.R使事情变得更简单。我在这里运行代码以匹配df2
到df1
idx<-do.call(coalesce, lapply(lapply(as.character(df2$family),
function(x) grepl(paste0("\\b", x, "\\b"), as.character(df1$name))),
transindex()))
从内部开始锻炼,我循环遍历df2
和grep
中的所有姓氏以获取这些值(将“\ b”添加到模式中,以便匹配整个单词)。 grepl
将返回逻辑向量(TRUE / FALSE)。然后,我应用上面的帮助函数transindex()
将这些向量更改为匹配的df2
中的行的索引,或NA
。由于行可能匹配多个族,因此我只需使用coalesce
辅助函数选择第一行。
不是说我可以将df1
中的行与df2
匹配,我可以将它们与
cbind(df1, size=df2[idx,])
name family size
# 1 John Doe Doe 2
# 1.1 Jane F. Doe Doe 2
# 2 Mark Smith Simpson Smith 6
# NA Sam Lee <NA> NA
答案 1 :(得分:1)
您可以将名称拆分为查找表格式,而不是尝试使用正则表达式和部分匹配,其中人名的每个组件都保存在一行中,并与其全名匹配:
df1 <- data.frame(name = c('John Doe',
'Jane F. Doe',
'Mark Smith Simpson',
'Sam Lee'),
stringsAsFactors = FALSE)
df2 <- data.frame(family = c('Doe', 'Smith'), size = c(2, 6),
stringsAsFactors = FALSE)
library(tidyr)
library(dplyr)
str_df <- function(x) {
ss <- strsplit(unlist(x)," ")
data.frame(family = unlist(ss),stringsAsFactors = FALSE)
}
splitnames <- df1 %>%
group_by(name) %>%
do(str_df(.))
splitnames
name family
1 Jane F. Doe Jane
2 Jane F. Doe F.
3 Jane F. Doe Doe
4 John Doe John
5 John Doe Doe
6 Mark Smith Simpson Mark
7 Mark Smith Simpson Smith
8 Mark Smith Simpson Simpson
9 Sam Lee Sam
10 Sam Lee Lee
现在您可以将其与df2
合并或加入以获得答案:
left_join(df2,splitnames)
Joining by: "family"
family size name
1 Doe 2 Jane F. Doe
2 Doe 2 John Doe
3 Smith 6 Mark Smith Simpson
潜在问题:如果一个人的名字与其他人的姓氏相同,你会得到一些不正确的比赛!
答案 2 :(得分:0)
另一种看起来有效的方法,至少是样本数据:
df1name = as.character(df1$name)
df1name
#[1] "John Doe" "Jane F. Doe" "Mark Smith Simpson" "Sam Lee"
regmatches(df1name, regexpr(paste(df2$family, collapse = "|"), df1name), invert = T) <- ""
df1name
#[1] "Doe" "Doe" "Smith" ""
cbind(df1, df2[match(df1name, df2$family), ])
# name family size
#1 John Doe Doe 2
#1.1 Jane F. Doe Doe 2
#2 Mark Smith Simpson Smith 6
#NA Sam Lee <NA> NA