我有一个参考数据框(df1),其中包含三列“特性”(性别,年份,代码)和两列“值”(金额,状态)。看起来像这样,但是有很多行:
gender year code amount status
M 2011 A 15 EMX
M 2011 A 123 NOX
F 2015 B 0 MIX
F 2018 A 12 NOX
F 2015 B 11 NOX
我还有另一个数据框(df2),其中只有三个“特性”列。例如:
gender year code
M 2011 A
M 2011 A
F 2018 A
F 2015 B
对于df2中的每一行,我想根据df1的“特征”中的匹配项分配“值”。如果有多个匹配项,我想随机选择成对的“值”。因此,当df2中存在重复的“特征”时,它们可能会以不同的“值”对结束,但是所有这些变量在df1中将具有完全匹配的结果。本质上,对于特征的每种组合,我希望值的分布在两个表之间匹配。
例如,“ df2”中的最后一行(性别= F,年份= 2015,代码= B)与“ df1”中的两行匹配:第三行(amont = 0,状态= MIX)和第五行(金额= 11,状态= NOX)。然后,应随机选择这些匹配行之一。对于基于性别,年份和代码在“ df2”和“ df1”之间多次匹配的所有此类情况,应选择一个随机行。
到目前为止,我的方法是开始使用dplyr
在两个数据帧之间执行left_join
。但是,这为df2中的每一行提供了所有可能的“值”,而不是随机选择一个。因此,我必须按特征分组并选择一个。这样会产生一个很大的中间表,而且效率不高。
我想知道是否有人建议使用更有效的方法?以前,我发现加入data.table
软件包的速度更快,但实际上对软件包没有很好的了解。我还想知道我是否应该进行联接,还是应该只使用sample
函数?
非常感谢任何帮助。
答案 0 :(得分:4)
根据'gender','year','code'(d1[d2, on = .(gender, year, code), ...]
)中的匹配项,使用'd2'查找'd1'中的行。对于每个匹配项(by = .EACHI
,请采样一行(sample(.N, 1L)
)。用它来索引“数量”和“状态”。
d1[d2, on = .(gender, year, code),
{ri <- sample(.N, 1L)
.(amount = amount[ri], status = status[ri])}, by = .EACHI]
# sample based on set.seed(1)
# gender year code amount status
# 1: M 2011 A 15 EMX
# 2: M 2011 A 15 EMX
# 3: F 2018 A 12 NOX
# 4: F 2015 B 11 NOX
请注意,Enhanced functionality of mult
argument上存在一个未解决的问题,即x
中的多行与i
中的行匹配时如何处理情况。当前,有效选项为"all"
(默认),"first"
或"last"
。但是,如果/在实施问题时,可以使用mult = "random"
(sample(.N, size = 1L)
)在匹配项中选择随机行(行)。
答案 1 :(得分:1)
我的data.table
游戏非常弱,但是这是一种使用与上述类似的方法的潜在解决方案。首先,我定义数据帧。
# Define data frames
df1 <- read.table(text= "gender year code amount status
M 2011 A 15 EMX
M 2011 A 123 NOX
F 2015 B 0 MIX
F 2018 A 12 NOX
F 2015 B 11 NOX", header = TRUE)
df2 <- read.table(text = "gender year code
M 2011 A
M 2011 A
F 2018 A
F 2015 B", header = TRUE)
然后,为重现性设置随机数生成器种子并加载库。
# Set RNG seed
set.seed(4)
# Load library
library(data.table)
接下来,我将数据帧转换为数据表。
# Convert to data tables
dt1 <- data.table(df1)
dt2 <- data.table(df2)
在这里,我进行实际的联接,等等。我已经循环执行了5次以显示结果的随机性。
for(i in c(1:5)){
# Add row numbers
dt3 <- dt2[, rn :=.I
][dt1,on = .(gender, year, code)
][, .SD[sample(.N)[1]], .(gender, year, code, rn)
][, rn := NULL]
# Check results
print(dt3)
}
#> gender year code amount status
#> 1: M 2011 A 123 NOX
#> 2: M 2011 A 15 EMX
#> 3: F 2015 B 0 MIX
#> 4: F 2018 A 12 NOX
#> gender year code amount status
#> 1: M 2011 A 123 NOX
#> 2: M 2011 A 123 NOX
#> 3: F 2015 B 11 NOX
#> 4: F 2018 A 12 NOX
#> gender year code amount status
#> 1: M 2011 A 123 NOX
#> 2: M 2011 A 123 NOX
#> 3: F 2015 B 11 NOX
#> 4: F 2018 A 12 NOX
#> gender year code amount status
#> 1: M 2011 A 15 EMX
#> 2: M 2011 A 15 EMX
#> 3: F 2015 B 11 NOX
#> 4: F 2018 A 12 NOX
#> gender year code amount status
#> 1: M 2011 A 123 NOX
#> 2: M 2011 A 15 EMX
#> 3: F 2015 B 0 MIX
#> 4: F 2018 A 12 NOX
由reprex package(v0.3.0)于2019-06-12创建
我实际上所做的是在数据表中添加行号,这将帮助我缩减最终的数据表。我联接数据表,然后将源自dt2
中单个行的所有行分组,并使用sample
随机提取其中的一行。 (这段代码是从@akrun here借来的。)最后,我删除了行号列。
答案 2 :(得分:1)
df2 %>%
mutate(
amount = pmap_chr(
.l = df2,
.f = ~ df1 %>%
filter(gender == ..1, year == ..2, code == ..3) %>%
select(amount) %>%
sample_n(1) %>%
pull(amount)
),
status = pmap_chr(
.l = df2,
.f = ~ df1 %>%
filter(gender == ..1, year == ..2, code == ..3) %>%
select(status) %>%
sample_n(1) %>%
pull(status)
)
)
这很慢,我个人会避免这样做,但这是一种方法。
答案 3 :(得分:1)
我希望这样会很有效:
df1[, row := .I]
keys <- c("year", "gender", "code")
setkeyv(df1, keys)
setkeyv(df2, keys)
for (rowdf2 in seq_len(nrow(df2))) {
set(df2, i = rowdf2, j = "rowindf1", value = df1[df2[rowdf2], x.row[sample(.N, 1)]])
}
setkeyv(df1, "row")
df1[df2[, .(rowindf1)]]
示例输出:
# gender year code amount status row
# 1: M 2011 A 123 NOX 2
# 2: M 2011 A 15 EMX 1
# 3: F 2015 B 11 NOX 5
# 4: F 2018 A 12 NOX 4