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

R 传递字符串作为附加数据列名的名称,r,R,我知道可以使用eval(parse())和as.names()函数将字符串作为变量名传递。但我的问题有点不同 我有一个包含数据和列名称的字符串,例如字符串:data1$column2。当我尝试上述命令时,我得到变量data1$column2的变量not found错误。变量本身当然被称为data1,因此无法找到,因为R将整个字符串解释为变量名 如何使$-符号用作列引用?某种“粘贴为文本”命令也不错。也就是说,如果我可以将字符串作为控制台输入的文本部分传递 范例 attach(iris) col_

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

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

如何使$-符号用作列引用?某种“粘贴为文本”命令也不错。也就是说,如果我可以将字符串作为控制台输入的文本部分传递

范例

attach(iris)
col_names <- cbind("iris$Sepal.Length", "iris$Sepal.Width")
col_names
也就是说,可以解释为:

as.data.frame(cbind(iris$Sepal.Length, iris$Sepal.Width))
总结 根据问题细节的不同变化,这里有两种解决问题的方法,可以用以下措辞来表达:

给定

?

最简单的解决办法是

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)
正如@texb所提到的,这可以通过(修改为返回数据帧)扩展到处理字符串向量

iris
OP问题示例 正如@texb提到的,
eval(parse(text=..)
版本可以简单地扩展,通过(修改以返回数据帧)处理字符串向量

如果在
col\u names
中引用了多个对象,则需要不同的解决方案,如下所示:

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

get4如果您有一个只包含列名作为字符串的变量,那么您不需要
eval
任何东西–您只需通过
foo[[var]]
访问列(其中
var请提供一个。您的示例做得不对;您只需要将对象子集,当然不需要
as.data.frame(cbind))(..)
这样做。我会更新我的答案。我之所以将列名作为字符串,是因为它们是通过一个较长的过程派生出来的。所以关键是要将它们真正解释为列名。我很感激,但您不会将
作为.data.frame(cbind(iris$Sepal.Length,iris$Sepal.Width))
当您真正想要
iris[,c(“Sepal.Length”,“Sepal.Width”)]
时。换句话说,将问题想象为i)从输入字符串中获取对象名(
iris
),然后ii)提取变量名,然后iii)从i)使用从ii)派生的东西对对象进行子集。同样,我知道这一点,但您要求代码生成
as.data.frame(cbind(iris$Sepal.Length,iris$Sepal.Width))
您应该考虑在哪里生成
iris[,c(“Sepal.Length”,“Sepal.Width”)
。这将使问题变得更容易,因为您只需要提取对象名和变量名。当然,由于您再次更改了目标帖子,您确实希望代码的计算结果为
cbind(obj1$Var1,obj2$Var2)
,这也是我现在对
get4()
的回答。谢谢!这是可行的,但是如果我在txt中有多个列(多个元素),该怎么办?它似乎没有将它们作为单独的列附加到新数据中。@Joshua我更新了我的答案。如果你在写原始问题时能更充分地解释这一点,那会有所帮助。在原著中,没有需要使用字符串向量的例子。提问时尽量具体,举一个可重复的例子总是有帮助的。我的编辑应该可以解决您的实际问题。这种奇怪的编程看起来很有趣-sapply(col_names,function(cname)eval(parse(text=cname)))
(尽管这似乎分配了详细的列名版本)?:)@texb是的,当然,我认为这是微不足道的,因为我的原始答案的开头部分。然而,
eval(parse(…)
通常被认为是糟糕的形式。例如,请参见《财富》图书馆(“财富”)
,然后是《财富》(106);-)@约书亚在
sapply周围(列名称,函数(x)eval(解析(text=x)))
?不,我已经在编辑后的答案中添加了这个选项。如果您的数据是原子的,这是可以的,但是您必须小心在其他地方使用该习惯用法。我上面的
get4()
对于不同的数据类型应该可以正常工作,因为它使用列表并将其直接转换为数据帧。“访问列的正确方法不是通过
foo$colname
”,这显然是不正确的
$
是访问列表的一种非常有效的方法,列表就是数据帧。你过分简化了吗?完全同意存储由
$
分隔的对象和列名是次优的。注意,您的最后一个示例与
iris[,cols]
相同,不需要任何其他内容。最后的评论-请注意,OP现在通知我们,他引用了不同的对象,例如,
cols@Gavin“如果您有……作为字符串的列名…”-此外,我的代码可以处理不同的对象。我不知道我的上一个解决方案出了什么问题,我一定是在睡觉什么的。
mapply
的+1,因为这很好。请注意,您需要
do.call(cbind.data.frame,cols)
因为在运行该行时,
cols
是一个包含两个向量的列表,
cbind
将它们作为向量处理,因此它们必须是原子的。请注意,如果执行
操作(c('iris$Sepal.Length','iris$Species'))
-将
物种
变量转换为原始因子的基本数字表示形式。您可以通过直接调用数据帧方法来避免这种情况。然后最好说“如果您有……列名为字符串……那么是更好的访问方式……”或类似的话。(请注意,
iris$“Species”
完全有效
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)
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
col_names <- c("iris$Sepal.Length", "iris$Sepal.Width")
as.data.frame(sapply(col_names, function(x) eval(parse(text = x))))
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
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
....
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
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
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]