为 data.tables 编写通过引用替换的高效函数
Writing efficient functions for data.tables that replace by reference
我有一个 data.table,其中包含多个二进制列,这些列具有相同的值,我想在一次操作中重新编码。我已经修改了一个最初为 data.frames 编写的函数,但我不确定我是否真的以我修改它的方式利用了 data.table 的速度:具体来说,我怀疑该函数可能仍然是复制值。
如何确保函数通过引用替换值?
这是一个玩具数据集:
# Example data:
id <- c(1,2,3,4,5)
fruit <- c("apple", "orange", "banana", "strawbery", "rasberry")
mydate <- c("2015-09-01", "2015-09-02", "2015-11-15", "2016-02-24", "2016-03-08")
eaten <- c("y", "y", "n", "y", "u")
present <- c("n", "n", "y", "y", "y")
dt <- data.table(id, fruit, mydate, eaten, present)
dt[, mydate := as.Date(mydate, format = "%Y-%m-%d")]
dt[, sex := c("m", "f", "f", "m", "f")]
# Columns to update:
bincols <- c("eaten", "present")
重新编码之前,数据如下所示:
> dt
id fruit mydate eaten present sex
1: 1 apple 2015-09-01 y n m
2: 2 orange 2015-09-02 y n f
3: 3 banana 2015-11-15 n y f
4: 4 strawbery 2016-02-24 y y m
5: 5 rasberry 2016-03-08 u y f
函数如下:
recode.multi <- function(datacols, oldval, newval) {
for (i in 1:length(datacols)) {
datacols[datacols == oldval[i]] = newval[i]
}
datacols
}
...应用于数据:
dt[, (bincols) := lapply(.SD, recode.multi, oldval = c("u", "n", "y"), newval = c(NA_real_, 0, 1)), .SDcols = bincols]
...和输出,它根据需要更新值但不确定在此过程中是否正在复制列?
> dt
id fruit mydate eaten present sex
1: 1 apple 2015-09-01 1 0 m
2: 2 orange 2015-09-02 1 0 f
3: 3 banana 2015-11-15 0 1 f
4: 4 strawbery 2016-02-24 1 1 m
5: 5 rasberry 2016-03-08 NA 1 f
我尝试将函数中的最后一个“=”更改为“:=”,但在检查 'datacols' 是否为 data.table 时出现错误。在函数中添加一个子句来检查是否是。data.table == TRUE 没有解决问题(返回相同的错误)。
任何关于实现此功能的最data.table 适当方法的想法将不胜感激。
我会做...
recodeDT = data.table(old = c("u", "n", "y"), new = c(NA_integer_, 0L, 1L), key = "old")
dt[, (bincols) := lapply(.SD, function(x) recodeDT[.(x), new]), .SDcols = bincols]
为了清楚起见,我认为最好将任何有限重映射存储在 table 中,但我不知道这样是否更有效率。如果您将变量存储为因子,您可以简单地调整水平,这应该非常快。你可以使用 setattr(x, "levels", z)
,也许。
旁注:您可能希望将它们编码为整数而不是浮点数。
这与 Frank 的类似,但将参数传递给构建翻译向量和 returns 翻译的函数。您不需要在函数内部进行循环,因为 lapply
、 :=
和 .SDcols 函数正在 [.data.table
.
内部进行循环
recode_dt <- function(datacol, oldval, newval)
{ trans <- setNames(newval, oldval)
trans[ datacol ] }
dt[, (bincols) := lapply(.SD, recode_dt, oldval = c("u", "n", "y"),
newval = c(NA_real_, 0, 1)),
.SDcols = bincols]
dt
#===============
id fruit mydate eaten present sex
1: 1 apple 2015-09-01 1 0 m
2: 2 orange 2015-09-02 1 0 f
3: 3 banana 2015-11-15 0 1 f
4: 4 strawbery 2016-02-24 1 1 m
5: 5 rasberry 2016-03-08 NA 1 f
请注意,您的列实际上并不是您从其中一条评论中想到的那样的因素。如果您构建了一个 data.frame 作为中间步骤,它们可能已经存在。
为了确定我的原始解决方案是否在复制值,我将我的、Frank 和 42 的解决方案应用于我的真实数据集,该数据集有 8933 个观察值和 150 列要使用函数更新。以下 system.time
的结果:
@Amy M: 332.82 秒
@Frank: 0.15 秒
@42(原版):4.13秒
@42(经过@Frank 的修改):0.05 秒
Frank 和 42 的解决方案都比我的快得多(所以我的必须是复制)。
我转载了下面最快的解法(Frank修改后的42):
recode.multi <- function(datacol, oldval, newval) {
trans <- setNames(newval, oldval)
trans[ match(datacol, names(trans)) ]
}
我有一个 data.table,其中包含多个二进制列,这些列具有相同的值,我想在一次操作中重新编码。我已经修改了一个最初为 data.frames 编写的函数,但我不确定我是否真的以我修改它的方式利用了 data.table 的速度:具体来说,我怀疑该函数可能仍然是复制值。
如何确保函数通过引用替换值?
这是一个玩具数据集:
# Example data:
id <- c(1,2,3,4,5)
fruit <- c("apple", "orange", "banana", "strawbery", "rasberry")
mydate <- c("2015-09-01", "2015-09-02", "2015-11-15", "2016-02-24", "2016-03-08")
eaten <- c("y", "y", "n", "y", "u")
present <- c("n", "n", "y", "y", "y")
dt <- data.table(id, fruit, mydate, eaten, present)
dt[, mydate := as.Date(mydate, format = "%Y-%m-%d")]
dt[, sex := c("m", "f", "f", "m", "f")]
# Columns to update:
bincols <- c("eaten", "present")
重新编码之前,数据如下所示:
> dt
id fruit mydate eaten present sex
1: 1 apple 2015-09-01 y n m
2: 2 orange 2015-09-02 y n f
3: 3 banana 2015-11-15 n y f
4: 4 strawbery 2016-02-24 y y m
5: 5 rasberry 2016-03-08 u y f
函数如下:
recode.multi <- function(datacols, oldval, newval) {
for (i in 1:length(datacols)) {
datacols[datacols == oldval[i]] = newval[i]
}
datacols
}
...应用于数据:
dt[, (bincols) := lapply(.SD, recode.multi, oldval = c("u", "n", "y"), newval = c(NA_real_, 0, 1)), .SDcols = bincols]
...和输出,它根据需要更新值但不确定在此过程中是否正在复制列?
> dt
id fruit mydate eaten present sex
1: 1 apple 2015-09-01 1 0 m
2: 2 orange 2015-09-02 1 0 f
3: 3 banana 2015-11-15 0 1 f
4: 4 strawbery 2016-02-24 1 1 m
5: 5 rasberry 2016-03-08 NA 1 f
我尝试将函数中的最后一个“=”更改为“:=”,但在检查 'datacols' 是否为 data.table 时出现错误。在函数中添加一个子句来检查是否是。data.table == TRUE 没有解决问题(返回相同的错误)。
任何关于实现此功能的最data.table 适当方法的想法将不胜感激。
我会做...
recodeDT = data.table(old = c("u", "n", "y"), new = c(NA_integer_, 0L, 1L), key = "old")
dt[, (bincols) := lapply(.SD, function(x) recodeDT[.(x), new]), .SDcols = bincols]
为了清楚起见,我认为最好将任何有限重映射存储在 table 中,但我不知道这样是否更有效率。如果您将变量存储为因子,您可以简单地调整水平,这应该非常快。你可以使用 setattr(x, "levels", z)
,也许。
旁注:您可能希望将它们编码为整数而不是浮点数。
这与 Frank 的类似,但将参数传递给构建翻译向量和 returns 翻译的函数。您不需要在函数内部进行循环,因为 lapply
、 :=
和 .SDcols 函数正在 [.data.table
.
recode_dt <- function(datacol, oldval, newval)
{ trans <- setNames(newval, oldval)
trans[ datacol ] }
dt[, (bincols) := lapply(.SD, recode_dt, oldval = c("u", "n", "y"),
newval = c(NA_real_, 0, 1)),
.SDcols = bincols]
dt
#===============
id fruit mydate eaten present sex
1: 1 apple 2015-09-01 1 0 m
2: 2 orange 2015-09-02 1 0 f
3: 3 banana 2015-11-15 0 1 f
4: 4 strawbery 2016-02-24 1 1 m
5: 5 rasberry 2016-03-08 NA 1 f
请注意,您的列实际上并不是您从其中一条评论中想到的那样的因素。如果您构建了一个 data.frame 作为中间步骤,它们可能已经存在。
为了确定我的原始解决方案是否在复制值,我将我的、Frank 和 42 的解决方案应用于我的真实数据集,该数据集有 8933 个观察值和 150 列要使用函数更新。以下 system.time
的结果:
@Amy M: 332.82 秒
@Frank: 0.15 秒
@42(原版):4.13秒
@42(经过@Frank 的修改):0.05 秒
Frank 和 42 的解决方案都比我的快得多(所以我的必须是复制)。
我转载了下面最快的解法(Frank修改后的42):
recode.multi <- function(datacol, oldval, newval) {
trans <- setNames(newval, oldval)
trans[ match(datacol, names(trans)) ]
}