传递字符串作为附加数据列名称的名称

Jos*_*hua 4 r

我知道可以使用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)

Rei*_*son 5

概括

鉴于问题细节的各种变化,以下是该问题的两种解决方案,可以表述为:

给定

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)

尽管第二个解决方案有点长,但它也有优点,因为它

  1. 将适用于不同类型的数据,因为它使用列表并将其转换为数据框。该eval(parse(text = ....))解决方案首先简化为数组。使用lapply()代替sapply()是解决这个问题的一个选项,但需要额外的工作来更改names结果对象的。
  2. 使用通用函数get()来获取具有指定名称和基本子集语法的对象。
  3. 不使用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)