如何在 R / 中执行复杂的多列匹配
How to perform complex multicolumn match in R /
我希望根据不止一列的条件匹配两个数据框,但不知道如何做。所以如果有我的数据集:
df1 <- data.frame(lower=c(0,5,10,15,20), upper=c(4,9,14,19,24), x=c(12,45,67,89,10))
df2 <- data.frame(age=c(12, 14, 5, 2, 9, 19, 22, 18, 23))
我希望匹配 df2 中落入 df1 中 lower 和 upper 之间范围的年龄,目的是向 df2 添加一个额外的列,其中包含 df1 中的 x 值,其中年龄介于 upper 和 lower 之间。即我希望 df2 看起来像
age x
12 67
14 67
5 45
....etc.
如何实现这样的匹配?
我会在 df1$x
选择中使用简单的 sapply
和 "anded" 条件,如下所示:
df2$x <- sapply( df2$age, function(x) { df1$x[ x >= df1$lower & x <= df1$upper ] })
给出:
> df2
age x
1 12 67
2 14 67
3 5 45
4 2 12
5 9 45
6 19 89
7 22 10
8 18 89
9 23 10
例如,对于 12 岁,括号内的选择给出:
> 12 >= df1$lower & 12 <= df1$upper
[1] FALSE FALSE TRUE FALSE FALSE
所以通过这个逻辑向量得到 df1$x
很容易,因为你的范围不重叠
使用 data.table
中的 foverlaps
是您要查找的内容:
library(data.table)
setDT(df1)
setDT(df2)[,age2:=age]
setkey(df1,lower,upper)
foverlaps(df2, df1, by.x = names(df2),by.y=c("lower","upper"))[,list(age,x)]
# age x
# 1: 12 67
# 2: 14 67
# 3: 5 45
# 4: 2 12
# 5: 9 45
# 6: 19 89
# 7: 22 10
# 8: 18 89
# 9: 23 10
这是另一种矢量化方法,使用 findInterval
对融化的数据集
library(data.table)
df2$x <- melt(setDT(df1), "x")[order(value), x[findInterval(df2$age, value)]]
# age x
# 1 12 67
# 2 14 67
# 3 5 45
# 4 2 12
# 5 9 45
# 6 19 89
# 7 22 10
# 8 18 89
# 9 23 10
这里的想法是
- 首先,整理您的数据,这样
lower
和 upper
将在同一列中,并且 x
将具有与该新列对应的值,
- 然后,根据这些范围对数据进行排序(
findInterval
需要)。
- 最后,运行
findInterval
在 x
列中找到正确的发生率
这是一个可能的 dplyr
/tidyr
版本
library(tidyr)
library(dplyr)
df1 %>%
gather(variable, value, -x) %>%
arrange(value) %>%
do(data.frame(x = .$x[findInterval(df2$age, .$value)])) %>%
cbind(df2, .)
# age x
# 1 12 67
# 2 14 67
# 3 5 45
# 4 2 12
# 5 9 45
# 6 19 89
# 7 22 10
# 8 18 89
# 9 23 10
我希望根据不止一列的条件匹配两个数据框,但不知道如何做。所以如果有我的数据集:
df1 <- data.frame(lower=c(0,5,10,15,20), upper=c(4,9,14,19,24), x=c(12,45,67,89,10))
df2 <- data.frame(age=c(12, 14, 5, 2, 9, 19, 22, 18, 23))
我希望匹配 df2 中落入 df1 中 lower 和 upper 之间范围的年龄,目的是向 df2 添加一个额外的列,其中包含 df1 中的 x 值,其中年龄介于 upper 和 lower 之间。即我希望 df2 看起来像
age x
12 67
14 67
5 45
....etc.
如何实现这样的匹配?
我会在 df1$x
选择中使用简单的 sapply
和 "anded" 条件,如下所示:
df2$x <- sapply( df2$age, function(x) { df1$x[ x >= df1$lower & x <= df1$upper ] })
给出:
> df2
age x
1 12 67
2 14 67
3 5 45
4 2 12
5 9 45
6 19 89
7 22 10
8 18 89
9 23 10
例如,对于 12 岁,括号内的选择给出:
> 12 >= df1$lower & 12 <= df1$upper
[1] FALSE FALSE TRUE FALSE FALSE
所以通过这个逻辑向量得到 df1$x
很容易,因为你的范围不重叠
使用 data.table
中的 foverlaps
是您要查找的内容:
library(data.table)
setDT(df1)
setDT(df2)[,age2:=age]
setkey(df1,lower,upper)
foverlaps(df2, df1, by.x = names(df2),by.y=c("lower","upper"))[,list(age,x)]
# age x
# 1: 12 67
# 2: 14 67
# 3: 5 45
# 4: 2 12
# 5: 9 45
# 6: 19 89
# 7: 22 10
# 8: 18 89
# 9: 23 10
这是另一种矢量化方法,使用 findInterval
对融化的数据集
library(data.table)
df2$x <- melt(setDT(df1), "x")[order(value), x[findInterval(df2$age, value)]]
# age x
# 1 12 67
# 2 14 67
# 3 5 45
# 4 2 12
# 5 9 45
# 6 19 89
# 7 22 10
# 8 18 89
# 9 23 10
这里的想法是
- 首先,整理您的数据,这样
lower
和upper
将在同一列中,并且x
将具有与该新列对应的值, - 然后,根据这些范围对数据进行排序(
findInterval
需要)。 - 最后,运行
findInterval
在x
列中找到正确的发生率
这是一个可能的 dplyr
/tidyr
版本
library(tidyr)
library(dplyr)
df1 %>%
gather(variable, value, -x) %>%
arrange(value) %>%
do(data.frame(x = .$x[findInterval(df2$age, .$value)])) %>%
cbind(df2, .)
# age x
# 1 12 67
# 2 14 67
# 3 5 45
# 4 2 12
# 5 9 45
# 6 19 89
# 7 22 10
# 8 18 89
# 9 23 10