使用 data.table
,可以通过 update on join 解决重编码问题:
DT[.(V1 = 1:2, to = 0:1), on = "V1", V1 := i.to]
DT[.(V2 = LETTERS[1:3], to = c("T", "K", "D")), on = "V2", V2 := i.to]
将
DT
转换为
V1 V2 V4
1: 0 T 1
2: 0 K 2
3: 1 D 3
4: 0 T 4
5: 0 K 5
6: 1 D 6
7: 0 T 7
8: 0 K 8
9: 1 D 9
10: 0 T 10
11: 0 K 11
12: 1 D 12
编辑: @Frank建议使用i.to
来确保安全。
解释
表达式.(V1 = 1:2, to = 0:1)
和.(V2 = LETTERS[1:3], to = c("T", "K", "D"))
分别在运行时创建查找表格即时生成。
或者,也可以预先设置查找表格。
lut1 <- data.table(V1 = 1:2, to = 0:1)
lut2 <- data.table(V2 = LETTERS[1:3], to = c("T", "K", "D"))
lut1
V1 to
1: 1 0
2: 2 1
lut2
V2 to
1: A T
2: B K
3: C D
然后,
更新连接变为
DT[lut1, on = "V1", V1 := i.to]
DT[lut2, on = "V2", V2 := i.to]
编辑2: 关于“我如何动态使用这段代码?”的答案
mat asked “我如何动态使用这段代码?”
因此,这里提供了一个修改后的版本,其中要更新的列的名称作为字符变量my_var_name
提供,但查找表仍然是即时创建的:
my_var_name <- "V1"
DT[.(from = 1:2, to = 0:1), on = paste0(my_var_name, "==from"),
(my_var_name) := i.to]
my_var_name <- "V2"
DT[.(from = LETTERS[1:3], to = c("T", "K", "D")), on = paste0(my_var_name, "==from"),
(my_var_name) := i.to]
有三点需要注意:
- 查找表的第一列不再使用动态命名,而是固定为
from
。这需要在不同命名的列之间进行连接(外键连接)。要连接的列的名称必须通过on
参数指定。
on
参数接受字符字符串,用于形如"V1==from"
的外键连接。此字符串是使用paste0()
动态创建的。
- 在表达式
(my_var_name) := i.to
中,括号将变量my_var_name
强制使用其内容。
使用预定义的查找表进行动态代码
现在,虽然要重新编码的列由变量动态指定,但要使用的查找表仍然在语句中硬编码,这意味着我们只完成了一半:我们还需要动态选择适当的查找表。
可以通过将查找表存储在列表中来实现这一点,其中每个列表元素的名称都根据应该重新编码的DT
列命名:
lut_list <- list(
V1 = data.table(from = 1:2, to = 0:1),
V2 = data.table(from = LETTERS[1:3], to = c("T", "K", "D"))
)
lut_list
$V1
from to
<int> <int>
1: 1 0
2: 2 1
$V2
from to
<char> <char>
1: A T
2: B K
3: C D
现在,我们也可以动态地从列表中选择适当的查找表:
my_var_name <- "V1"
DT[lut_list[[my_var_name]], on = paste0(my_var_name, "==from"),
(my_var_name) := i.to]
进一步地,我们可以通过一个循环来重新编码
DT
的所有相关列:
for (v in intersect(names(lut_list), colnames(DT))) {
DT[lut_list[[v]], on = paste0(v, "==from"), (v) := i.to]
}
请注意,
DT
是通过引用更新的,即只有受影响的元素被替换,而不是复制整个对象。因此,
for
循环在同一数据对象上进行迭代应用。这是
data.table的特殊之处,无法与data.frames或tibbles一起使用。
V2
可能要是字符型的,因为你想要将“C”改成“D”,对吗?虽然我可能对您如何重新编码V2
的方式有所误解。 - Mike H.V2
很可能需要是字符类型。 - user3077008