我想编写一个函数,该函数将使用data.table
包选择数据集的一部分。函数的声明参数为:
- 输入数据集 (
dset
), - 变量,数据集将在其上子集 (
seg.var
), - 上面声明的变量的值 (
value
)。
我能够在基础 R 中编写工作函数:
# Function without data.table
data.select <- function(dset, seg.var, value){
dset.out <- dset[dset[[seg.var]] == value,]
return(dset.out)
}
data.select(iris, "Species", "setosa")
但是,我无法使用data.table
包重写它:下面的功能不起作用。
# Function with data.table
data.select.dt <- function(dset, seg.var, value){
dset <- as.data.table(dset)
dset.out <- dset[seg.var == value,]
return(dset.out)
}
data.select.dt(iris, Species, "setosa")
eval(expr, envir, enclos) 中的错误:找不到对象"物种">
data.select.dt(iris, "Species", "setosa")
空数据表(0 行)的 5 列:
萼片长度,萼片宽度,花瓣长度,花瓣宽度,物种
输入数据集采用data.frame
格式。重写给定函数的目标是性能改进。任何帮助将不胜感激。
评论有点太长了,所以单独回答:
首先,我提到的get()
函数作为对@R.S.回答的评论:
data.select.dt.V1 <- function(dset, seg.var, value){
if(!data.table::is.data.table(dset)) dset <- data.table::as.data.table(dset) # To convert only if needed
dset[get(seg.var) == value,]
}
data.select.dt.V1(iris, 'Species', 'setosa')
然后是更data.table
函数,您可以在其中将第二个参数作为表达式传递(好吧,以data.table
的方式),而不是字符串:
data.select.dt.V2 <- function(dset, seg.var, value){
if(!data.table::is.data.table(dset)) dset <- data.table::as.data.table(dset) # To convert only if needed
sv <- substitute(seg.var)
dset[eval(sv) == value,]
}
data.select.dt.V2(iris, Species, 'setosa')
编辑:功能简化和转换测试(感谢@David Arenburg评论)。
第 2 次编辑:在 515 MBdata.table
和data.frame
上对上述两个函数进行了基准测试@David Arenburg 的一个函数(分别带有和不带is.data.table
检查、V3
和V4
):
data.select.dt.V3 <- function(dset, seg.var, value) data.table::as.data.table(dset)[.(value), on = seg.var]
data.select.dt.V4 <- function(dset, seg.var, value) {
if(!data.table::is.data.table(dset)) dset <- data.table::as.data.table(dset) # To convert only if needed
dset[.(value), on = seg.var]
}
expr min lq mean median uq max neval cld
1 res <- data.select.dt.V1(iris_df, "Species", "setosa") 3.804995 4.585763 4.150130 4.688093 5.320362 3.166503 10 c
2 res <- data.select.dt.V2(iris_df, Species, "setosa") 3.713275 3.827180 3.865347 4.544968 4.753045 3.218075 10 c
3 res <- data.select.dt.V3(iris_df, "Species", "setosa") 1.927947 1.942868 2.167127 2.328364 2.595420 2.159664 10 b
4 res <- data.select.dt.V4(iris_df, "Species", "setosa") 1.987710 2.004497 2.011502 2.280117 2.856847 1.594249 10 b
5 res <- data.select.dt.V1(iris_dt, "Species", "setosa") 2.771223 2.792428 2.501362 2.805796 3.056144 1.883520 10 b
6 res <- data.select.dt.V2(iris_dt, Species, "setosa") 2.830161 2.970071 2.593192 3.123812 3.170884 1.752576 10 b
7 res <- data.select.dt.V3(iris_dt, "Species", "setosa") 1.963530 2.116116 2.059718 2.203265 2.740949 1.768817 10 b
8 res <- data.select.dt.V4(iris_dt, "Species", "setosa") 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 10 a
显然,如果您同时期待data.frame
和data.table
,那么V4
是要去的,如果只有df
可能的话,最快的功能将是V3
。
检查你的代码:data.table 版本中的dset.out <- dset[seg.var == value,]
应该是dset.out <- dset[dset[[seg.var]] == value,]
这是你原来拥有的。
Species
缺少引号,应在调用函数时"Species"
引号。这就是错误消息指出工作区中没有对象Species
的原因。
这行得通。
data.select.dt <- function(dset, seg.var, value){
dset <- as.data.table(dset)
dset.out <- dset[dset[[seg.var]] == value,]
return(dset.out)
}
data.select.dt(iris, "Species", "setosa")
编辑以添加提示,在函数外部调试,以便您看到出现问题的地方。
这里有一些微妙之处,我的建议是将其分解为几个步骤。先直接编写代码,然后转换为函数:
您的原始代码在函数之外工作:
require(data.table)
dset <- as.data.table(iris)
dset.out <- dset[Species == "setosa",]
dset.out
> dset <- as.data.table(iris)
> dset.out <- dset[Species == "setosa",]
> dset.out
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1: 5.1 3.5 1.4 0.2 setosa
2: 4.9 3.0 1.4 0.2 setosa
...
但是,当您将其包装在函数中时会失败...
> require(data.table)
> data.select.dt <- function(dset, seg.var, value){
+ dset <- as.data.table(dset)
+ dset.out <- dset[ seg.var == eval(value) ]
+ return(dset.out)
+ }
> data.select.dt(iris, Species, "setosa")
Show Traceback
Rerun with Debug
Error in eval(expr, envir, enclos) : object 'Species' not found
好吧,有趣,为什么它在函数调用中失败?您需要正确引用"物种"变量。这需要您在调用中取消引用:
dset[dset[["Species"]] == "setosa",]
> dset[dset[["Species"]] == "setosa",]
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1: 5.1 3.5 1.4 0.2 setosa
2: 4.9 3.0 1.4 0.2 setosa
...
铌。我发现真正有趣的是,调试中的 RStudio 允许函数工作,但在非调试模式下触发错误。
现在你可以把它包装在一个函数中:
data.select.dt <- function(dset, seg.var, value){
dset <- as.data.table(dset)
dset.out <- dset[dset[[seg.var]] == value,]
return(dset.out)
}
请注意,物种用引号括起来"Species"
...
data.select.dt(iris, "Species", "setosa")
data.select.dt(iris, "Species", "setosa")
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1: 5.1 3.5 1.4 0.2 setosa
2: 4.9 3.0 1.4 0.2 setosa
3: 4.7 3.2 1.3 0.2 setosa