3

我知道可以使用eval(parse())andas.names()函数将字符串作为变量名传递。但我的问题有点不同。

我有一个包含数据列名的字符串,例如字符串:data1$column2。当我尝试上述命令时,我得到一个 variable not found 变量的错误 data1$column2。变量 is 本身当然被调用data1,因此无法找到,因为 R 将整个字符串解释为变量名。

如何让 $-sign 用作列参考?某种粘贴为文本的命令也会很棒。也就是说,如果我可以将字符串作为控制台输入的文字部分传递。

例子

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))
4

2 回答 2

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)

第二种解决方案虽然有点长,但具有优势,因为它

  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

正如@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

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
....

修改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
于 2013-07-22T16:51:41.593 回答
3

如果你有一个变量只包含列名作为字符串,那么你不需要eval任何东西——你只需通过foo[[var]](where var <- 'colname') 而不是foo$colname.

另一方面,如果将整个名称作为字符串给出(这很奇怪,应该让你停下来:改变你的设计,它可能已经坏了!)你仍然可以相当直接地解析出不同的部分:

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]
于 2013-07-22T17:37:58.017 回答