我知道可以使用eval(parse())
和as.names()
函数将字符串作为变量名传递。但我的问题有点不同。
我有一个包含数据和列名的字符串,例如字符串:data1$column2
。当我尝试上述命令时,我得到变量 data1$column2
的变量未找到错误。变量本身当然称为data1
,因此无法找到,因为R将整个字符串解释为变量名。
如何让$ -sign作为列引用?某种类型的paste-as-text-command也很棒。也就是说,如果我只是将字符串作为控制台输入的文字部分传递。
实施例
attach(iris)
col_names <- cbind("iris$Sepal.Length", "iris$Sepal.Width")
col_names
现在我想做:
"as.data.frame(parse(col_names))"
即,被解释为:
as.data.frame(cbind(iris$Sepal.Length, iris$Sepal.Width))
答案 0 :(得分:4)
鉴于问题细节的各种变化,这里有两个解决问题的方法,可以表达为:
鉴于
col_names <- c("Obj1$Var1", "Obj2$Var2")
如何返回相当于
的数据框cbind(Obj1$Var1, Obj2$Var2)
最简单的解决方案是
as.data.frame(sapply(col_names, function(x) eval(parse(text = x))))
但是使用parse()
这样的事情不应该依赖它。
get4 <- function(x, ...) {
fun <- function(text, ...) {
obj <- get(text[1], ...)
obj[[text[2]]]
}
sx <- strsplit(x, "\\$")
lx <- lapply(sx, fun, ...)
out <- do.call(cbind.data.frame, lx)
names(out) <- x
out
}
get4(col_names)
第二种解决方案虽然有点长,却具有优势,因为它
eval(parse(text = ....))
解决方案首先简化为数组。使用lapply()
代替sapply()
是一个可以解决此问题的选项,但需要额外的工作来更改生成的对象的names
。get()
来获取具有声明名称和基本子集语法的对象。parse
; - )更详细的原始答案在下面继续:
eval(parse(....))
将有效
data1 <- data.frame(column1 = 1:10, column2 = letters[1:10])
txt <- "data1$column2"
> eval(parse(text = txt))
[1] a b c d e f g h i j
Levels: a b c d e f g h i j
正如@texb所提到的,这可以简单地扩展到处理字符串向量(修改为返回数据帧)
col_names <- c("iris$Sepal.Length", "iris$Sepal.Width")
as.data.frame(sapply(col_names, function(x) eval(parse(text = x))))
使用get
可能更容易接受,但你必须做一些进动,这与
get2 <- function(x, ...) {
sx <- strsplit(x, "\\$")[[1]]
obj <- get(sx[1], ...)
obj[[sx[2]]]
}
> get2(txt)
[1] a b c d e f g h i j
Levels: a b c d e f g h i j
OP的问题中的iris
示例
正如@texb所提到的,eval(parse(text = ....))
版本可以简单地扩展为处理字符串向量(修改为返回数据框)
col_names <- c("iris$Sepal.Length", "iris$Sepal.Width")
as.data.frame(sapply(col_names, function(x) eval(parse(text = x))))
iris$Sepal.Length iris$Sepal.Width
1 5.1 3.5
2 4.9 3.0
3 4.7 3.2
4 4.6 3.1
5 5.0 3.6
6 5.4 3.9
....
修改get2()
也可以允许它处理字符串向量,例如col_names
。在这里,我循环遍历sx
的第一个元素以提取对象字符串(检查只有一个唯一的对象名称),然后我get
该对象,然后使用变量名称对其进行子集化(使用sapply(sx, `[`, 2)
)
get3 <- function(x, ...) {
sx <- strsplit(x, "\\$")
obj <- unique(sapply(sx, `[`, 1))
stopifnot(length(obj) == 1L)
obj <- get(obj, ...)
obj[sapply(sx, `[`, 2)]
}
col_names <- c("iris$Sepal.Length", "iris$Sepal.Width")
head(get3(col_names))
> head(get3(col_names))
Sepal.Length Sepal.Width
1 5.1 3.5
2 4.9 3.0
3 4.7 3.2
4 4.6 3.1
5 5.0 3.6
6 5.4 3.9
如果您在col_names
中引用了多个对象,那么您将需要一个不同的解决方案,
get4 <- function(x, ...) {
fun <- function(text, ...) {
obj <- get(text[1], ...)
obj[[text[2]]]
}
sx <- strsplit(x, "\\$")
lx <- lapply(sx, fun, ...)
out <- do.call(cbind.data.frame, lx)
names(out) <- x
out
}
col_names2 <- c("iris$Sepal.Length", "iris2$Sepal.Length")
get4(col_names2)
> head(get4(col_names2))
iris$Sepal.Length iris2$Sepal.Length
1 5.1 5.1
2 4.9 4.9
3 4.7 4.7
4 4.6 4.6
5 5.0 5.0
6 5.4 5.4
答案 1 :(得分:3)
如果您的变量只包含列名称作为字符串,那么您不需要eval
任何内容 - 只需通过foo[[var]]
访问该列(其中) var <- 'colname'
)代替foo$colname
。
另一方面,如果整个名称是一个字符串(这很奇怪,应该让你停下来:改变你的设计,它可能会被打破!)你仍然可以相当直接地解析出不同的部分: / p>
manipulate <- function (vars) {
parts <- strsplit(vars, '\\$')
# This gets a list of variables (c('iris', 'iris') in our case)
data <- lapply(parts, function (part) get(part[1], envir = parent.frame()))
# This selects the matching column for every variable.
cols <- mapply(function (d, part) d[part[2]], data, parts)
# This just `cbind`s the columns.
do.call(cbind.data.frame, cols)
}
cols <- c('iris$Sepal.Length', 'iris$Sepal.Width')
foo <- manipulate(cols)
也就是说,如果你只是想从数据框中选择一些给定的列,那么有一种更简单的方法:
cols <- c('Sepal.Length', 'Sepal.Width')
result <- iris[, cols]