我怎么能告诉dplyr中的select()它看到的字符串是数据框中的列名

问题描述 投票:36回答:8

我试过搜索,但没有找到这个问题的答案。

我正在尝试在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中的所有字符串都是数据框中的列名。

谢谢。

r
8个回答
45
投票

在更新版本的dplyr中,这可能在select中使用one_of,如

my_cols <- c('mpg', 'disp')
mtcars %>% select(one_of(my_cols))

32
投票

Select似乎与列索引(dplyr 0.2)一起使用,因此只需将您想要的名称与其索引匹配,并使用它们来选择列。

myCols <- c("mpg","disp")
colNums <- match(myCols,names(mtcars))
mtcars %>% select(colNums)

10
投票

您可以使用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

9
投票

我碰到了这个,我想我应该提到这已经在较新版本的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)

8
投票

[编辑 - 下面的一些内容现已过时,发布了dplyr 0.7 - 请参阅here]

问题是标准评估和非标准评估之间的区别。

tl; dnr:你可以使用dplyr::select的'标准评估'对应物,即dplyr::select_

这允许您提供列名作为包含字符串的变量:

dplyr::select_(df.main.scaled, names.gens[i,1], names.gens[i,2])

这里有更多细节试图解释它是如何工作的:

Non standard evaluation and the select function in dplyr

非标准评估是以非标准方式评估代码。通常,这意味着在评估表达式之前捕获它们,并在不同的环境(上下文/范围)中将它们评估为正常。当您为dplyr::select提供不带引号的列名时,dplyr使用非标准评估将它们解释为列。

使用dplyr :: select的例子

假设我们有以下数据框:

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$adf$b

R中的函数参数是promises,它们不会立即求值。它们可以作为表达式捕获,然后在不同的环境中运行。

如果我们知道我们想要提前选择列ab,这很好。但是,如果这些列事先未知,并保存在变量中,该怎么办呢?

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,因为它旨在以交互方式使用。


1
投票

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

1
投票

对于字符串,你可以做

my_cols <- c("mpg", "disp")
mtcars %>% select(!!!my_cols)

虽然,我认为避免字符串可能是更好的做法

my_cols <- quos(mpg, disp)
mtcars %>% select(!!!my_cols)

-6
投票

我通过反复试验弄明白了。如果有人好奇,做了这样的事情:

lapply(names.gens, as.name)
select(df.main.scaled, eval(names.gens[[i]]), eval(names.gens[[i+someindex]]))
© www.soinside.com 2019 - 2024. All rights reserved.