如何使用将规则应用于非公共列的自定义函数合并两个数据集?
How can I merge two data sets with a custom function that applies a rule to non-common columns?
我正在尝试合并两个不同大小的数据框,但由于数据的面板结构,我 运行 遇到了困难。
考虑下面的示例,其中 'toy.left' 是三个变量的面板:坐标 ('coord') 和在特定月份分配给该坐标的名称 ('name') ('month')。接下来,考虑由四个变量组成的 'toy.right,':一个名称 ('name'),该名称分配给该坐标的任期开始时间 ('tenure.start'),以及它们的结束时间该坐标的分配 ('tenure.end') 任期。
toy.left <- tribble(~month, ~coord, ~name,
"2000-01-01", 1301, "Alpha",
"2000-03-01", 1301, "Beta",
"2000-06-01", 1302, "Charlie",
"2000-09-01", 1303, "Delta",
"2000-12-01", 1303, "Epsilon")
toy.right <- tribble(~name, ~coord, ~tenure.start, ~tenure.end,
"Alpha", 1301, "2000-02-01", "2000-04-01",
"Beta", 1301, "1999-11-01", "2000-04-01",
"Charlie", 1302, "2000-04-01", "2000-07-01",
"Delta", 1303, "2000-08-01", "2000-10-01",
"Epsilon", 1303, "2000-11-01", "2001-01-01",
"Delta", 1303, "2002-01-01", "2004-01-01")
我想合并这两个数据集,但是有一些规则使 dplyr 中的 merge() 变得困难。例如,我不能简单地使用 inner_join() 并通过 'name' 和 'coord' 合并,因为这违反了数据的面板结构。如果我这样做,个人的任期不会与观察月份重叠(首先,请参阅第 1 和第 2 行,它们应该倒置;其次,请参阅第 4 和第 5 行,其中 merge() 重复月份观察,但是应该只包括第 4 行)。
toy.left %>%
inner_join(toy.right, by = c("name", "coord"))
*Output*
month coord name tenure.start tenure.end
2000-01-01 1301 Alpha 2000-02-01 2000-04-01
2000-03-01 1301 Beta 1999-11-01 2000-04-01
2000-06-01 1302 Charlie 2000-04-01 2000-07-01
2000-09-01 1303 Delta 2000-08-01 2000-10-01
2000-09-01 1303 Delta 2002-01-01 2004-01-01
2000-12-01 1303 Epsilon 2000-11-01 2001-01-01
为了解决这个问题,我可以通过'name,''coord,'和[=40合并数据=] 但我需要根据日期是否介于 'tenure.start' 和 'tenure.end.' 之间来确定合并条件 'month' dplyr 中的合并()。
我知道自定义函数或循环可能是解决此问题的最佳方法,但我不确定从哪里开始。此外,原始数据集有超过 150 万个观察值,这可能会产生更多问题。
我欢迎你的建议!
(所有这些都是在将 month
和 tenure.*
转换为 Date
-class 之后。)
模糊连接
fuzzyjoin::fuzzy_inner_join(
toy.left, toy.right,
by=c("name", "coord", month="tenure.start", month="tenure.end"),
match_fun=list(`==`, `==`, `>=`, `<=`))
# # A tibble: 4 x 7
# month coord.x name.x name.y coord.y tenure.start tenure.end
# <date> <dbl> <chr> <chr> <dbl> <date> <date>
# 1 2000-03-01 1301 Beta Beta 1301 1999-11-01 2000-04-01
# 2 2000-06-01 1302 Charlie Charlie 1302 2000-04-01 2000-07-01
# 3 2000-09-01 1303 Delta Delta 1303 2000-08-01 2000-10-01
# 4 2000-12-01 1303 Epsilon Epsilon 1303 2000-11-01 2001-01-01
sqldf
sqldf::sqldf(
"select tl.name, tl.coord, tl.month, tr.[tenure.start], tr.[tenure.end]
from [toy.left] tl
inner join [toy.right] tr on tl.name=tr.name and tl.coord=tr.coord
and tl.month between tr.[tenure.start] and tr.[tenure.end]")
# name coord month tenure.start tenure.end
# 1 Beta 1301 2000-03-01 1999-11-01 2000-04-01
# 2 Charlie 1302 2000-06-01 2000-04-01 2000-07-01
# 3 Delta 1303 2000-09-01 2000-08-01 2000-10-01
# 4 Epsilon 1303 2000-12-01 2000-11-01 2001-01-01
(我使用 [tenure.start]
和括号符号来区分 table 标识符 tl
和列名 tenure.start
,其中 SQL列名称中的点通常表示 schema.tablename.columnname
类命名法。)
data.table
这会进行左连接,其他类型不会。为了确定哪些应该被删除,因为左而不是内部,我将添加一列到 toy.left
:
library(data.table)
setDT(toy.left)
setDT(toy.right)
toy.left[, val := 2]
toy.left[toy.right, on = .(name, coord, month >= tenure.start, month <= tenure.end)][ !is.na(val),]
# month coord name val month.1
# <Date> <num> <char> <num> <Date>
# 1: 1999-11-01 1301 Beta 2 2000-04-01
# 2: 2000-04-01 1302 Charlie 2 2000-07-01
# 3: 2000-08-01 1303 Delta 2 2000-10-01
# 4: 2000-11-01 1303 Epsilon 2 2001-01-01
data.table
有重命名列的方法,因此请注意。当我不确定我知道命名将如何结束时,我经常复制周围的列以便它总是清楚的......但我这样做的部分原因是懒惰去学习它究竟是如何决定的结果名称。
我正在尝试合并两个不同大小的数据框,但由于数据的面板结构,我 运行 遇到了困难。
考虑下面的示例,其中 'toy.left' 是三个变量的面板:坐标 ('coord') 和在特定月份分配给该坐标的名称 ('name') ('month')。接下来,考虑由四个变量组成的 'toy.right,':一个名称 ('name'),该名称分配给该坐标的任期开始时间 ('tenure.start'),以及它们的结束时间该坐标的分配 ('tenure.end') 任期。
toy.left <- tribble(~month, ~coord, ~name,
"2000-01-01", 1301, "Alpha",
"2000-03-01", 1301, "Beta",
"2000-06-01", 1302, "Charlie",
"2000-09-01", 1303, "Delta",
"2000-12-01", 1303, "Epsilon")
toy.right <- tribble(~name, ~coord, ~tenure.start, ~tenure.end,
"Alpha", 1301, "2000-02-01", "2000-04-01",
"Beta", 1301, "1999-11-01", "2000-04-01",
"Charlie", 1302, "2000-04-01", "2000-07-01",
"Delta", 1303, "2000-08-01", "2000-10-01",
"Epsilon", 1303, "2000-11-01", "2001-01-01",
"Delta", 1303, "2002-01-01", "2004-01-01")
我想合并这两个数据集,但是有一些规则使 dplyr 中的 merge() 变得困难。例如,我不能简单地使用 inner_join() 并通过 'name' 和 'coord' 合并,因为这违反了数据的面板结构。如果我这样做,个人的任期不会与观察月份重叠(首先,请参阅第 1 和第 2 行,它们应该倒置;其次,请参阅第 4 和第 5 行,其中 merge() 重复月份观察,但是应该只包括第 4 行)。
toy.left %>%
inner_join(toy.right, by = c("name", "coord"))
*Output*
month coord name tenure.start tenure.end
2000-01-01 1301 Alpha 2000-02-01 2000-04-01
2000-03-01 1301 Beta 1999-11-01 2000-04-01
2000-06-01 1302 Charlie 2000-04-01 2000-07-01
2000-09-01 1303 Delta 2000-08-01 2000-10-01
2000-09-01 1303 Delta 2002-01-01 2004-01-01
2000-12-01 1303 Epsilon 2000-11-01 2001-01-01
为了解决这个问题,我可以通过'name,''coord,'和[=40合并数据=] 但我需要根据日期是否介于 'tenure.start' 和 'tenure.end.' 之间来确定合并条件 'month' dplyr 中的合并()。
我知道自定义函数或循环可能是解决此问题的最佳方法,但我不确定从哪里开始。此外,原始数据集有超过 150 万个观察值,这可能会产生更多问题。
我欢迎你的建议!
(所有这些都是在将 month
和 tenure.*
转换为 Date
-class 之后。)
模糊连接
fuzzyjoin::fuzzy_inner_join(
toy.left, toy.right,
by=c("name", "coord", month="tenure.start", month="tenure.end"),
match_fun=list(`==`, `==`, `>=`, `<=`))
# # A tibble: 4 x 7
# month coord.x name.x name.y coord.y tenure.start tenure.end
# <date> <dbl> <chr> <chr> <dbl> <date> <date>
# 1 2000-03-01 1301 Beta Beta 1301 1999-11-01 2000-04-01
# 2 2000-06-01 1302 Charlie Charlie 1302 2000-04-01 2000-07-01
# 3 2000-09-01 1303 Delta Delta 1303 2000-08-01 2000-10-01
# 4 2000-12-01 1303 Epsilon Epsilon 1303 2000-11-01 2001-01-01
sqldf
sqldf::sqldf(
"select tl.name, tl.coord, tl.month, tr.[tenure.start], tr.[tenure.end]
from [toy.left] tl
inner join [toy.right] tr on tl.name=tr.name and tl.coord=tr.coord
and tl.month between tr.[tenure.start] and tr.[tenure.end]")
# name coord month tenure.start tenure.end
# 1 Beta 1301 2000-03-01 1999-11-01 2000-04-01
# 2 Charlie 1302 2000-06-01 2000-04-01 2000-07-01
# 3 Delta 1303 2000-09-01 2000-08-01 2000-10-01
# 4 Epsilon 1303 2000-12-01 2000-11-01 2001-01-01
(我使用 [tenure.start]
和括号符号来区分 table 标识符 tl
和列名 tenure.start
,其中 SQL列名称中的点通常表示 schema.tablename.columnname
类命名法。)
data.table
这会进行左连接,其他类型不会。为了确定哪些应该被删除,因为左而不是内部,我将添加一列到 toy.left
:
library(data.table)
setDT(toy.left)
setDT(toy.right)
toy.left[, val := 2]
toy.left[toy.right, on = .(name, coord, month >= tenure.start, month <= tenure.end)][ !is.na(val),]
# month coord name val month.1
# <Date> <num> <char> <num> <Date>
# 1: 1999-11-01 1301 Beta 2 2000-04-01
# 2: 2000-04-01 1302 Charlie 2 2000-07-01
# 3: 2000-08-01 1303 Delta 2 2000-10-01
# 4: 2000-11-01 1303 Epsilon 2 2001-01-01
data.table
有重命名列的方法,因此请注意。当我不确定我知道命名将如何结束时,我经常复制周围的列以便它总是清楚的......但我这样做的部分原因是懒惰去学习它究竟是如何决定的结果名称。