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]