我认为元编程在这里是正确的术语.
我希望能够使用data.table,就像在webapp中使用MySQL一样.也就是说,Web用户使用一些Web前端(例如Shiny服务器)来选择数据库,选择要过滤的列,选择要分组的列,选择要聚合的列和聚合函数.我想使用R和data.table作为查询,聚合等的后端.假设前端存在,R将这些变量作为字符串,并验证它们等.
我编写了以下函数来构建data.table表达式并使用R的parse/eval元编程功能来运行它.这是一种合理的方法吗?
我包括所有相关代码来测试这个.获取此代码(在读取安全性之后!)并运行test_agg_meta()来测试它.这只是一个开始.我可以添加更多功能.
但我的主要问题是我是否过度思考这一点.有没有更直接的方法来使用data.table,所有的输入都是事先确定的,而不需要求助于parse/eval元编程?
我也知道"with"声明和一些其他无糖功能方法,但不知道他们是否可以处理所有情况.
require(data.table)
fake_data<-function(num=12){
#make some fake data
x=1:num
lets=letters[1:num]
data=data.table(
u=rep(c("A","B","C"),floor(num/3)),
v=x %%2, w=lets, x=x, y=x^2, z=1-x)
return(data)
}
data_table_meta<-function(
#aggregate a data.table meta-programmatically
data_in=fake_data(),
filter_cols=NULL,
filter_min=NULL,
filter_max=NULL,
groupby_cols=NULL,
agg_cols=setdiff(names(data_in),groupby_cols),
agg_funcs=NULL,
verbose=F,
validate=T,
jsep="_"
){
all_cols=names(data_in)
if (validate) {
stopifnot(length(filter_cols) == length(filter_min))
stopifnot(length(filter_cols) == length(filter_max))
stopifnot(filter_cols %in% all_cols)
stopifnot(groupby_cols %in% all_cols)
stopifnot(length(intersect(agg_cols,groupby_cols)) == 0)
stopifnot((length(agg_cols) == length(agg_funcs)) | (length(agg_funcs)==1) | (length(agg_funcs)==0))
}
#build the command
#defaults
i_filter=""
j_select=""
n_agg_funcs=length(agg_funcs)
n_agg_cols=length(agg_cols)
n_groupby_cols=length(groupby_cols)
if (n_agg_funcs == 0) …Run Code Online (Sandbox Code Playgroud) 我在这里错过了什么?
d = data.table(a = 1:5)
d[, a] # 1 2 3 4 5
d[, sum(a)] # 15
d[, eval(quote(a))] # 1 2 3 4 5
d[, sum(eval(quote(a)))] # 15
quoted_a = quote(a)
d[, eval(quoted_a)] # 1 2 3 4 5
d[, sum(eval(quoted_a))] # Error in eval(expr, envir, enclos) : object 'a' not found
Run Code Online (Sandbox Code Playgroud)
到底是怎么回事?我跑R 2.15.0和data.table 1.8.9.
我的聚合需求因列/ data.frames而异.我想动态地将"list"参数传递给data.table.
作为一个最小的例子:
require(data.table)
type <- c(rep("hello", 3), rep("bye", 3), rep("ok",3))
a <- (rep(1:3, 3))
b <- runif(9)
c <- runif(9)
df <- data.frame(cbind(type, a, b, c), stringsAsFactors=F)
DT <-data.table(df)
Run Code Online (Sandbox Code Playgroud)
这个电话:
DT[, list(suma = sum(as.numeric(a)), meanb = mean(as.numeric(b)), minc = min(as.numeric(c))), by= type]
Run Code Online (Sandbox Code Playgroud)
会有类似的结果:
type suma meanb minc
1: hello 6 0.1332210 0.4265579
2: bye 6 0.5680839 0.2993667
3: ok 6 0.5694532 0.2069026
Run Code Online (Sandbox Code Playgroud)
未来的data.frames将有更多的列,我想要以不同的方式进行总结.但是为了使用这个小例子:有没有办法以编程方式传递列表?
我天真地尝试过:
# create a different list
mylist <- "list(lengtha = length(as.numeric(a)), maxb = max(as.numeric(b)), meanc = …Run Code Online (Sandbox Code Playgroud) 我有以下几点 data.table
structure(list(val1 = c(1, 2, 1, 3, 4, 5, 3), val2 = c(4, 5, 6, 4, 2, 4, 5)), .Names = c("val1", "val2"), row.names = c(NA, -7L), class = c("data.table", "data.frame"), .internal.selfref = <pointer: 0xedae28>)
Run Code Online (Sandbox Code Playgroud)
我想要做的是根据其他变量定义的标准过滤其中的行.例如,我可能想要所有的行val1 >= 1.这很容易做到
x[val1 > 1,]
Run Code Online (Sandbox Code Playgroud)
但是,我希望能够将列名称(即,val1或者val2)指定为变量,将过滤器值指定为变量.我试过了
cname = 'val1'
cutoff = 1
x[(cname >= cutoff),] # Try 1
x[(cname) >= (cutoff),] # Try 2
x[eval(cname > cutoff),] # Try 3
Run Code Online (Sandbox Code Playgroud)
但它只是给了原版data.table.