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

时间:2013-07-22 16:39:48

标签: r

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

2 个答案:

答案 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)

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

  1. 将适用于不同类型的数据,因为它与列表一起使用并将其转换为数据框。 eval(parse(text = ....))解决方案首先简化为数组。使用lapply()代替sapply()是一个可以解决此问题的选项,但需要额外的工作来更改生成的对象的names
  2. 使用公共函数get()来获取具有声明名称和基本子集语法的对象。
  3. 不使用parse; - )
  4. 原始答案

    更详细的原始答案在下面继续:

    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]