如何匹配+;/-在R中彼此有5个?

如何匹配+;/-在R中彼此有5个?,r,string-matching,R,String Matching,假设我有一个如下所示的数据帧: dat <- data.frame("firstName" = c("John", "John", "Mary", "Bob", "Mary", "Bob"), "age"= c(21, 24, 35, 30, 20, 27)) 我有一个非常大的姓名和年龄数据集,希望找到一种更自动化的分配id的方法。我考虑过从20岁开始每5年创建一次年龄箱,但这与在不同箱中但仍在5岁以内的观察结果不匹配 这里有一个从dplyr开始的lag方法: library(dplyr

假设我有一个如下所示的数据帧:

dat <- data.frame("firstName" = c("John", "John", "Mary", "Bob", "Mary", "Bob"), "age"= c(21, 24, 35, 30, 20, 27))

我有一个非常大的姓名和年龄数据集,希望找到一种更自动化的分配id的方法。我考虑过从20岁开始每5年创建一次年龄箱,但这与在不同箱中但仍在5岁以内的观察结果不匹配

这里有一个从
dplyr
开始的
lag
方法:

library(dplyr)
dat %>%
  group_by(firstName) %>%
  arrange(firstName,age) %>%
  mutate(id = cumsum(!(age - (lag(age,default = -Inf) ) <= 5)))
# A tibble: 6 x 3
# Groups:   firstName [3]
  firstName   age    id
  <fct>     <dbl> <int>
1 Bob          27     1
2 Bob          30     1
3 John         21     1
4 John         24     1
5 Mary         20     1
6 Mary         35     2

库(dplyr)
dat%>%
分组人(名字)%>%
排列(名字、年龄)%>%
变异(id=cumsum(!(age-(lag(age,default=-Inf))1)sqldf/igraph将每一行与具有相同名称的行进行匹配,年龄在5岁以内,且该行不是自身。如果不存在此类匹配,则将该行与自身进行匹配,以便对所有行进行说明。然后,这些行及其匹配可以转换为edgelist,然后再转换为igraph,g。查找的连接组件并分配原始数据帧行的成员身份ID

在示例数据中,每个连接的组件的大小为1或2,但这种方法可以处理任何大小,而不仅仅是那些大小

library(igraph)
library(sqldf)

s <- sqldf("select a.rowid, a.*, b.rowid as match 
  from dat a left join dat b
    on a.firstname = b.firstname and 
      abs(a.age - b.age) < 5 and
      a.rowid != b.rowid")
e <- cbind(s$rowid, s$match) # edgelist
e[is.na(s$match), 2] <- e[is.na(s$match), 1]  
g <- graph_from_edgelist(e)
transform(dat, id = components(g)$membership)
我们可以将图形可视化如下:

plot(g)
(图后续)

2)Base R此解决方案在一定程度上受其他解决方案的推动,但其显著优势在于它只使用Base R,只使用两行代码,如(1)还可以处理任何大小的连接组件,生成正确答案并完全矢量化。它的工作原理是对数据进行排序,然后根据显示的条件向前拉id或生成新id

o <- with(dat, order(firstName, age))
transform(dat[o,], id = cumsum(c(1, diff(xtfrm(firstName)) | diff(age) > 5)))

没有附加包

dat <- data.frame("firstName" = c("John", "John", "Mary", "Bob", "Mary", "Bob"), "age"= c(21, 24, 35, 30, 20, 27))
n <- length(dat$firstName)

vals <- list()
for (i in 1:n) {
    fname <- dat$firstName[i]
    age <- dat$age[i]
    index <- which(fname == dat$firstName &
     (age > dat$age - 5) &
     (age < dat$age + 5))
    vals[[i]] <- index
}

vals <- unique(vals)
dat$id <- NA

for (i in 1:length(vals)) {
    dat$id[vals[[i]]] <- i
}

这是按“名字”分组的吗?我想将相同的id分配给具有相同名字且年龄在+/-5范围内的观察值。它们当前没有按名字分组。如果有一个年龄为29岁的John的条目,会发生什么情况?它是同时匹配其他John的条目还是仅匹配第二个条目?如果您有其他OB,会发生什么包含相同的名字,但差异在部分而非全部的范围内?例如,John1在John2的5范围内,John2在John3的5范围内,但John1不在John3的5范围内。如果您认为您实际上拥有唯一的Johns,那么您可能需要首先对名称进行唯一分类,然后在完成范围测试后重新分类。I假设两个或多个观察者的名字相同,年龄在+/-5以内,他们是相同的观察者,将被分配相同的id。我将组成一个id,从1到“匹配”的数量@Chuck P如果有两个John's的年龄=29,它将与两个John's匹配。您的解决方案将在30岁时仅向数据帧添加一个John而崩溃。
dat不确定为什么会发生这种情况,但添加
dat$id添加了基本解决方案。
o <- with(dat, order(firstName, age))
transform(dat[o,], id = cumsum(c(1, diff(xtfrm(firstName)) | diff(age) > 5)))
  firstName age id
6       Bob  27  1
4       Bob  30  1
1      John  21  2
2      John  24  2
5      Mary  20  3
3      Mary  35  4
dat <- data.frame("firstName" = c("John", "John", "Mary", "Bob", "Mary", "Bob"), "age"= c(21, 24, 35, 30, 20, 27))
n <- length(dat$firstName)

vals <- list()
for (i in 1:n) {
    fname <- dat$firstName[i]
    age <- dat$age[i]
    index <- which(fname == dat$firstName &
     (age > dat$age - 5) &
     (age < dat$age + 5))
    vals[[i]] <- index
}

vals <- unique(vals)
dat$id <- NA

for (i in 1:length(vals)) {
    dat$id[vals[[i]]] <- i
}
  firstName age id
1      John  21  1
2      John  24  1
3      Mary  35  2
4       Bob  30  3
5      Mary  20  4
6       Bob  27  3