假设我有一个数据帧,如下所示:
dat <- data.frame("firstName" = c("John", "John", "Mary", "Bob", "Mary", "Bob"), "age"= c(21, 24, 35, 30, 20, 27))我想创建第三个变量dat$id,如果一个观察值的年龄在另一个观察值的+/- 5年内并且具有相同的firstName,则该变量分配相同的数字。因此,数据帧将如下所示:
dat <- data.frame("firstName" = c("John", "John", "Mary", "Bob", "Mary", "Bob"), "age"= c(21, 24, 35, 30, 20, 27), "id"= c(1,1,2,3,4,3))我有一个非常大的名字和年龄的数据集,我想找到一种更自动化的分配id的方法。我考虑从20岁开始每5年创建一次年龄箱,但这不会匹配不同箱中的观察值,但仍然在5年内。
发布于 2020-05-21 04:25:16
1) sqldf/igraph sqldf将每行与具有相同名称、年龄在5以内的行进行匹配,并且行本身不是。如果没有这样的匹配,则将行与自身进行匹配,以便将所有行都考虑在内。然后,可以将行及其匹配转换为边缘列表,并且随后将其转换为图形,例如,找到连接的分量并将成员资格ids分配给原始数据帧的行。
在示例数据中,每个连接的组件的大小都是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)给予:
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我们可以像这样可视化这个图:
plot(g)(在图形之后继续)

2) Base R这个解决方案在一定程度上受到其他解决方案的推动,但它有显著的优势,因为它只使用base R,只有2行代码,如(1)也可以处理任何大小的连接组件,生成正确的答案,并且是完全矢量化的。它的工作方式是对数据进行排序,然后根据显示的条件向前拉取id或生成一个新的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发布于 2020-05-21 04:38:37
没有额外的包
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发布于 2020-05-21 04:09:25
下面是来自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 2https://stackoverflow.com/questions/61921752
复制相似问题