我试过搜索,但没有找到这个问题的答案。
我正在尝试在dplyr中使用select语句,但在尝试发送字符串时遇到问题。我的问题是,我怎么告诉select()它看到的字符串是数据框中的列名?
例如这很好用
select(df.main.scaled, var1, var3)
select(df.main.scaled, var2, var4)
但这不起作用:
select(df.main.scaled, names.gens[i,1], names.gens[i,2])
哪里
> names.genx <- c("var1","var2")
> names.geny <- c("var3","var4")
> names.gens <- cbind(names.genx, names.geny)
> names.gens
names.genx names.geny
[1,] "var1" "var3"
[2,] "var2" "var4"
要清楚,names.gens中的所有字符串都是数据框中的列名。
谢谢。
在更新版本的dplyr中,这可能在select
中使用one_of
,如
my_cols <- c('mpg', 'disp')
mtcars %>% select(one_of(my_cols))
Select似乎与列索引(dplyr 0.2)一起使用,因此只需将您想要的名称与其索引匹配,并使用它们来选择列。
myCols <- c("mpg","disp")
colNums <- match(myCols,names(mtcars))
mtcars %>% select(colNums)
您可以使用get()
来获取当前环境中字符串命名的对象。所以:
R> iris %>% select(Species, Petal.Length) %>% head(3)
Species Petal.Length
1 setosa 1.4
2 setosa 1.4
3 setosa 1.3
R> iris %>% select('Species', 'Petal.Length') %>% head(3)
Error in abs(ind[ind < 0]) :
non-numeric argument to mathematical function
R> iris %>% select(get('Species'), get('Petal.Length')) %>% head(3)
Species Petal.Length
1 setosa 1.4
2 setosa 1.4
3 setosa 1.3
R> s <- 'Species'
R> p <- 'Petal.Length'
R> iris %>% select(get(s), get(p)) %>% head(3)
Species Petal.Length
1 setosa 1.4
2 setosa 1.4
3 setosa 1.3
我碰到了这个,我想我应该提到这已经在较新版本的dplyr中解决了。
myTest = data_frame(
var1 = 1,
var2 = 2,
var3 = 3,
var4 = 4)
i = 1
myTest %>%
select_(.dots =
c(names.gens[i,1], names.gens[i,2]) %>% unname)
[编辑 - 下面的一些内容现已过时,发布了dplyr 0.7 - 请参阅here]
问题是标准评估和非标准评估之间的区别。
tl; dnr:你可以使用dplyr::select
的'标准评估'对应物,即dplyr::select_
。
这允许您提供列名作为包含字符串的变量:
dplyr::select_(df.main.scaled, names.gens[i,1], names.gens[i,2])
这里有更多细节试图解释它是如何工作的:
非标准评估是以非标准方式评估代码。通常,这意味着在评估表达式之前捕获它们,并在不同的环境(上下文/范围)中将它们评估为正常。当您为dplyr::select
提供不带引号的列名时,dplyr
使用非标准评估将它们解释为列。
假设我们有以下数据框:
df <- tibble::data_frame(a = 1:5, b = 6:10, c = 11:15, d = 16:20)
select语句的一个简单示例如下:
r <- dplyr::select(df, a, b)
这是NSE的一个示例,因为a和b不是全局环境中存在的变量。 dplyr::select
不是在全局命名空间中搜索a和b,而是指示R在dataframe df
的上下文中搜索变量a和b。您可以将环境视为列表,将a和b视为键。所以以下有点像告诉R查找df$a
和df$b
R中的函数参数是promises,它们不会立即求值。它们可以作为表达式捕获,然后在不同的环境中运行。
如果我们知道我们想要提前选择列a
和b
,这很好。但是,如果这些列事先未知,并保存在变量中,该怎么办呢?
columns_to_select <- c("a", "b")
以下不起作用:
dplyr::select(df, columns_to_select)
此错误告诉我们数据框中没有名为“columns_to_select”的列。参数columns_to_select
已在数据框的上下文中进行了评估,因此R尝试执行类似df$columns_to_select
的操作,并发现该列不存在。
我们如何解决这个问题?
Tidyverse功能总是提供'escape hatch',让您可以绕过这个限制。 dplyr vignette说'使用NSE的dplyr中的每个函数也有一个使用SE的版本。 SE版本的名称始终是NSE名称,末尾带有_。
这是什么意思?
我们可能会尝试以下方法,但我们发现它不起作用:
# Does not work
r <-dplyr::select_(df, columns_to_select)
与将参数columns_to_select
捕获到select_函数并将其解释为列名称相反,columns_to_select
以标准方式进行评估,解析为c("a", "b")
。
这就是我们想要的,除了select_
的每个参数都是一个列,我们刚刚提供了一个长度为2的字符向量来表示单个列。
因此,上面的代码返回一个包含单列a的tibble,这不是我们想要的。 (只使用第一个元素 - 字符向量中的"a"
,忽略其他所有元素)。
这个问题的一个解决方案如下,但它假设columns_to_select
包含两个元素:
col1 <- columns_to_select[1]
col2 <- columns_to_select[2]
r <- dplyr::select_(df,col1, col2)
我们如何将这概括为columns_to_select
可能具有任意数量的元素的情况?
解决方案是使用可选的.dots
参数。
dplyr::select_(df, .dots=columns_to_select)
这有一些解释
在R中,...
construct允许创建具有可变(任意)数量的参数的函数。 ...
在函数中可用,并允许函数体访问所有参数。另见here。
一个非常简单的例子如下:
addition <- function(...) {
args <- list(...)
sum(unlist(args))
}
r <- addition(1,2,3)
但是,这并没有立即帮助我们。它实际上已经在select_
函数中实现,并且只允许我们提供任意数量的列名作为参数,例如select_(df, "a", "b", "c", "d")
。
我们需要的是一种类似于...
的机制,但允许我们将...
之类的东西作为单个参数传递给函数。这正是.dots
所做的。
请注意,.dots
不提供select
,因为它旨在以交互方式使用。
dplyr 0.1.2中有一个使用正则表达式和匹配的解决方法(有关未来版本中直接支持的信息,请参阅下面的hadley评论)。像^(x1|x2|x3)$
这样的正则表达式匹配精确的变量名称,所以我们只需要从带有变量名称的向量中构造这样的表达式。这是代码
# load libraries
library(dplyr)
library(stringr)
# create data.frame
df = data.frame(
x = rep(0,5),
y = 1,
var = 2,
another_var = 5,
var.4 = 6
)
# function to construct reg exp from vector with variable names
varlist = function(x) {
x = str_c('^(',paste(x, collapse='|'),')$')
x = str_replace_all(x,'\\.','\\\\.')
return(x)
}
# select variables based on vector of variable names
vars = c('y','another_var','var.4')
df %>%
select(matches(varlist(vars)))
对于字符串,你可以做
my_cols <- c("mpg", "disp")
mtcars %>% select(!!!my_cols)
虽然,我认为避免字符串可能是更好的做法
my_cols <- quos(mpg, disp)
mtcars %>% select(!!!my_cols)
我通过反复试验弄明白了。如果有人好奇,做了这样的事情:
lapply(names.gens, as.name)
select(df.main.scaled, eval(names.gens[[i]]), eval(names.gens[[i+someindex]]))