问题描述
我有两个数据帧,对于数据帧1中的每个特定行,我试图根据已定义的重要标准(班次,年龄,水平)从数据帧2中找到最接近的匹配项。例如,假设我有数据帧1:
shift_1 <- c(1,1,2)
length_1 <- c(100,120,5,70)
level_1<- c(1,3,4)
age_1 <- c(4.5,3.2,2.5)
df_1 <- data.frame(shift_1,level_1,age_1,length_1)
shift_1 level_1 age_1 length_1
1 1 1 4.5 100
2 1 3 3.2 120
3 0 5 3.0 5
4 2 4 2.5 70
对于这个数据帧的每一行,我想在数据帧2中找到最接近的匹配,如下所示:
shift_2 <- c(1,2,0)
length_2 <- c(100,200,40,180,10)
level_2<- c(3,4,5)
age_2 <- c(2.5,5.5,2.2,3.1,5)
df_2 <- data.frame(shift_2,level_2,age_2,length_2)
shift_2 level_2 age_2 length_2
1 1 3 2.5 100
2 1 4 5.5 200
3 2 4 2.2 40
4 1 3 3.1 180
5 0 5 5.0 10
基于以下条件: 完全匹配必须。级别必须完全匹配。年龄的差价为20%。
如果找到匹配项:我们要添加匹配行的索引号和匹配信息,否则我们将输入NA。所以预期的结果是这样的:
shift_r level_r age_r length_r index shift_match level_match age_match length_match
1 1 1 4.5 100 NA NA NA NA NA
2 1 3 3.2 120 4 1 3 3.1 180
3 0 5 3.0 5 NA NA NA NA NA
4 2 4 2.5 70 3 2 4 2.2 40
能否请您告知我该如何处理?有没有可以简化此任务的库?
解决方法
您需要“非等价”或“范围”联接。这是在R的public static void main(String[] args) {
int x = 2;
int [] myArray = {1,2,3,4,5};
for (int i = 0; i < myArray.length; i++) {
if(x == myArray[i])
{
//do something
}
}
}
}
和fuzzyjoin
包中实现的。由于SQL也支持它,因此人们也可以使用data.table
。
遗憾的是,sqldf
本机不支持此功能。由于此操作在SQL中受支持,因此如果您的数据在数据库中,则dplyr
将允许使用其dbplyr
,但不是本机使用。
首先,我们需要添加20%的公差:
sql_on
fuzzyjoin
df_1$age_1_start <- df_1$age_1 * 0.8
df_1$age_1_end <- df_1$age_1 * 1.2
df_1
# shift_1 level_1 age_1 length_1 age_1_start age_1_end
# 1 1 1 4.5 100 3.60 5.40
# 2 1 3 3.2 120 2.56 3.84
# 3 0 5 3.0 5 2.40 3.60
# 4 2 4 2.5 70 2.00 3.00
data.table
fuzzyjoin::fuzzy_left_join(
df_1,df_2,by = c("shift_1" = "shift_2","level_1" = "level_2","age_1_start" = "age_2","age_1_end" = "age_2"),match_fun = list(`==`,`==`,`<=`,`>=`))
# shift_1 level_1 age_1 length_1 age_1_start age_1_end shift_2 level_2 age_2 length_2
# 1 1 1 4.5 100 3.60 5.40 NA NA NA NA
# 2 1 3 3.2 120 2.56 3.84 1 3 3.1 180
# 3 0 5 3.0 5 2.40 3.60 NA NA NA NA
# 4 2 4 2.5 70 2.00 3.00 2 4 2.2 40
此软件包倾向于根据右边的名称来重命名左边的(library(data.table)
DT_1 <- as.data.table(df_1) # must include age_1_start and age_1_end from above
DT_2 <- as.data.table(df_2)
DT_2[DT_1,on = .(shift_2 == shift_1,level_2 == level_1,age_2 >= age_1_start,age_2 <= age_1_end)]
# shift_2 level_2 age_2 length_2 age_2.1 age_1 length_1
# 1: 1 1 3.60 NA 5.40 4.5 100
# 2: 1 3 2.56 180 3.84 3.2 120
# 3: 0 5 2.40 NA 3.60 3.0 5
# 4: 2 4 2.00 40 3.00 2.5 70
)连接,这可能会令人沮丧。为此,您随后需要进行一些清理。
sqldf
DT_1
如果您知道SQL,那么最后一个可能是最直观和最容易理解的。但是请记住,对于较大的框架,它会将整个框架复制到内存存储SQLite实例中……这不是“免费的”。
sqldf::sqldf(
"select t1.*,t2.*
from df_1 t1
left join df_2 t2 on t1.shift_1 = t2.shift_2 and t1.level_1 = t2.level_2
and t1.age_1_start <= t2.age_2 and t1.age_1_end >= t2.age_2")
# shift_1 level_1 age_1 length_1 age_1_start age_1_end shift_2 level_2 age_2 length_2
# 1 1 1 4.5 100 3.60 5.40 NA NA NA NA
# 2 1 3 3.2 120 2.56 3.84 1 3 3.1 180
# 3 0 5 3.0 5 2.40 3.60 NA NA NA NA
# 4 2 4 2.5 70 2.00 3.00 2 4 2.2 40
实现为您提供了强大的功能,它的参数对我而言似乎很容易理解。结果正如我期望的那样命名。但是,这是三个实现中最慢的一个(使用此数据)。 (仅当您的真实数据“非常”大时才应对此加以考虑。)
如果您还不知道fuzzyjoin
,尽管它的速度非常快,但它的R的方言可能会让不了解情况的人模糊不清。我相信它具有data.table
一样强大的功能,尽管我还没有测试所有极端情况,以查看其中一个是否支持另一种不支持的东西。
fuzzyjoin