我知道可以使用eval(parse())和as.names()函数将字符串作为变量名传递。但我的问题有点不同。
我有一个包含数据和列名称的字符串,例如字符串:data1$column2。当我尝试上述命令时,我收到变量的变量未找到错误 data1$column2。变量本身当然是被调用的data1,因此无法找到,因为 R 将整个字符串解释为变量名。
如何让 $ 符号作为列引用?某种粘贴为文本命令也很棒。也就是说,如果我可以将字符串作为控制台输入的文字部分传递。
例子
attach(iris)
col_names <- cbind("iris$Sepal.Length", "iris$Sepal.Width")
col_names
Run Code Online (Sandbox Code Playgroud)
现在我想做:
"as.data.frame(parse(col_names))"
Run Code Online (Sandbox Code Playgroud)
也就是说,可以解释为:
as.data.frame(cbind(iris$Sepal.Length, iris$Sepal.Width))
Run Code Online (Sandbox Code Playgroud)
鉴于问题细节的各种变化,以下是该问题的两种解决方案,可以表述为:
给定
col_names <- c("Obj1$Var1", "Obj2$Var2")
Run Code Online (Sandbox Code Playgroud)
如何返回相当于以下内容的数据框
cbind(Obj1$Var1, Obj2$Var2)
Run Code Online (Sandbox Code Playgroud)
?
最简单的解决方案是
as.data.frame(sapply(col_names, function(x) eval(parse(text = x))))
Run Code Online (Sandbox Code Playgroud)
但这种用途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)
Run Code Online (Sandbox Code Playgroud)
尽管第二个解决方案有点长,但它也有优点,因为它
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
Run Code Online (Sandbox Code Playgroud)
正如 @texb 提到的,这可以通过(修改为返回数据帧)轻松扩展以处理字符串向量
col_names <- c("iris$Sepal.Length", "iris$Sepal.Width")
as.data.frame(sapply(col_names, function(x) eval(parse(text = x))))
Run Code Online (Sandbox Code Playgroud)
使用它可能更容易被接受,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
Run Code Online (Sandbox Code Playgroud)
irisOP问题的例子正如 @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
....
Run Code Online (Sandbox Code Playgroud)
还可以进行修改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
Run Code Online (Sandbox Code Playgroud)
如果您引用了多个对象,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
Run Code Online (Sandbox Code Playgroud)