【问题标题】:Pass a vector of variable names to arrange() in dplyr将变量名称的向量传递给 dplyr 中的arrange()
【发布时间】:2014-12-17 07:55:38
【问题描述】:

我想传递arrange() {dplyr} 一个变量名向量来进行排序。通常我只是输入我想要的变量,但我正在尝试创建一个函数,其中排序变量可以作为函数参数输入。

df <- structure(list(var1 = c(1L, 2L, 2L, 3L, 1L, 1L, 3L, 2L, 4L, 4L
  ), var2 = structure(c(10L, 1L, 8L, 3L, 5L, 4L, 7L, 9L, 2L, 6L
  ), .Label = c("b", "c", "f", "h", "i", "o", "s", "t", "w", "x"
  ), class = "factor"), var3 = c(7L, 5L, 5L, 8L, 5L, 8L, 6L, 7L, 
  5L, 8L), var4 = structure(c(8L, 5L, 1L, 4L, 7L, 4L, 3L, 6L, 9L, 
  2L), .Label = c("b", "c", "d", "e", "f", "h", "i", "w", "y"), 
  class = "factor")), .Names = c("var1", "var2", "var3", "var4"), 
  row.names = c(NA, -10L), class = "data.frame")

# this is the normal way to arrange df with dplyr
df %>% arrange(var3, var4)

# but none of these (below) work for passing a vector of variables
vector_of_vars <- c("var3", "var4")
df %>% arrange(vector_of_vars)
df %>% arrange(get(vector_of_vars))
df %>% arrange(eval(parse(text = paste(vector_of_vars, collapse = ", "))))

【问题讨论】:

  • Imo,应该保存使用 %>% 以进行链接,因为它非常丑陋...(对于单个操作

标签: r sorting parameter-passing dplyr


【解决方案1】:

Hadley 没有在帮助文件中明确说明这一点——仅在他的 NSE 小插图中。后跟下划线的函数版本使用标准评估,因此您将它们传递给字符串向量等。

如果我正确理解您的问题,您只需将arrange() 替换为arrange_() 即可。

具体来说,在执行此操作时将字符串向量作为.dots 参数传递。

> df %>% arrange_(.dots=c("var1","var3"))
   var1 var2 var3 var4
1     1    i    5    i
2     1    x    7    w
3     1    h    8    e
4     2    b    5    f
5     2    t    5    b
6     2    w    7    h
7     3    s    6    d
8     3    f    8    e
9     4    c    5    y
10    4    o    8    c

========== 2018 年 3 月更新 ==============

在 dplyr 中使用标准评估版本,正如我在此处显示的那样现在被认为已弃用。您可以阅读Hadley's programming vignette 了解新方法。基本上,您将使用!! 取消引用一个变量或使用!!! 取消引用arrange() 内的变量向量。

当您传递这些列时,如果它们是空的,请使用 quo() 引用它们作为一个变量或使用 quos() 引用它们作为向量。不要使用引号。请参阅 Akrun 的答案。

如果您的列已经是字符串,则使用rlang::sym() 为单个列命名,或使用rlang::syms() 为向量命名。请参阅克里斯托斯的答案。您还可以将as.name() 用于单个列。不幸的是,在撰写本文时,有关如何使用 rlang::sym() 的信息尚未包含在我上面链接到的小插图中(根据他的草稿,最终它将在“可变参数准引用”部分中)。

【讨论】:

  • 我也是这么想的,但是如果你这样做df %&gt;% arrange_(vector_of_vars),它似乎会忽略第二个元素并且只对第一个元素进行排序。但是,如果您执行df %&gt;% arrange_(vector_of_vars[1], vector_of_vars[2]),那么它会根据这两个值进行排序。我认为有一种比第二种方法更优雅的方法,但我不确定它是什么。
  • arrange_() 似乎确实忽略了第二列。 @eipi10 你的解决方案可以工作,但问题是vector_of_vars 中可以有任意数量的元素。
  • 啊,这行得通:df %&gt;% arrange_(.dots = vector_of_vars)。 farnsy,如果你做出这个改变,我会给你答案
  • @farnsy 如果你想按降序排序怎么办?如何传递 desc 参数?我还没想通!
  • vector_of_vars &lt;- c("desc(var3)", "var4");df %&gt;% arrange_(.dots=vector_of_vars)
【解决方案2】:

在新版本中(即将发布0.6.0dplyr)我们可以使用quosures

library(dplyr)
vector_of_vars <- quos(var1, var3)
df %>%
    arrange(!!! vector_of_vars)
#   var1 var2 var3 var4
#1     1    i    5    i
#2     1    x    7    w
#3     1    h    8    e
#4     2    b    5    f
#5     2    t    5    b
#6     2    w    7    h
#7     3    s    6    d
#8     3    f    8    e
#9     4    c    5    y
#10    4    o    8    c

当有多个变量时,我们使用quos,对于单个变量,我们使用quoquos 将返回引用变量的 list,在 arrange 内部,我们使用 !!! 取消引用 list 进行评估

【讨论】:

  • ...现在又被弃用了...1: Unquoting language objects with '!!!' is soft-deprecated as of rlang 0.3.0. Please use '!!' instead. 令人兴奋(保持礼貌)在 tidyverse 中有多少功能不断被弃用...我会回到 Base R对于我的长期代码,我认为...
【解决方案3】:

本着 quosures 精神:

df %>% arrange(!!! rlang::syms(c("var1", "var3")))

对于单个变量,它看起来像:

df %>% arrange(!! rlang::sym(c("var1")))

【讨论】:

    【解决方案4】:

    我认为现在你可以使用dplyr::arrange_at()

    library(dplyr)
    
    ### original
    head(iris)
    #   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
    # 1          5.1         3.5          1.4         0.2  setosa
    # 2          4.9         3.0          1.4         0.2  setosa
    # 3          4.7         3.2          1.3         0.2  setosa
    # 4          4.6         3.1          1.5         0.2  setosa
    # 5          5.0         3.6          1.4         0.2  setosa
    # 6          5.4         3.9          1.7         0.4  setosa
    
    ### arranged
    iris %>% 
      arrange_at(c("Sepal.Length", "Sepal.Width")) %>% 
      head()
    #   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
    # 1          4.3         3.0          1.1         0.1  setosa
    # 2          4.4         2.9          1.4         0.2  setosa
    # 3          4.4         3.0          1.3         0.2  setosa
    # 4          4.4         3.2          1.3         0.2  setosa
    # 5          4.5         2.3          1.3         0.3  setosa
    # 6          4.6         3.1          1.5         0.2  setosa
    

    【讨论】:

      【解决方案5】:

      试试这个:

      df %>% do(do.call(arrange_, . %>% list(.dots = vector_of_vars)))
      

      实际上这可以更简单地写成:

      df %>% arrange_(.dots = vector_of_vars)
      

      虽然在这一点上我认为它与 farnsy 的隐含解决方案相同。

      【讨论】:

      • 这对我不起作用,请参阅my post
      • arrange_ 已弃用,quosures 方式似乎是要走的路
      【解决方案6】:

      它有点密集,但我认为现在最好的方法是使用 across() 以及 tidyselect 函数,例如all_of():

      df <- structure(list(var1 = c(1L, 2L, 2L, 3L, 1L, 1L, 3L, 2L, 4L, 4L
        ), var2 = structure(c(10L, 1L, 8L, 3L, 5L, 4L, 7L, 9L, 2L, 6L
        ), .Label = c("b", "c", "f", "h", "i", "o", "s", "t", "w", "x"
        ), class = "factor"), var3 = c(7L, 5L, 5L, 8L, 5L, 8L, 6L, 7L, 
        5L, 8L), var4 = structure(c(8L, 5L, 1L, 4L, 7L, 4L, 3L, 6L, 9L, 
        2L), .Label = c("b", "c", "d", "e", "f", "h", "i", "w", "y"), 
        class = "factor")), .Names = c("var1", "var2", "var3", "var4"), 
        row.names = c(NA, -10L), class = "data.frame")
      
      vector_of_vars <- c("var3", "var4")
      
      df %>% arrange(across(all_of(vector_of_vars)))
      

      【讨论】:

        猜你喜欢
        • 2019-07-30
        • 1970-01-01
        • 1970-01-01
        • 2015-11-21
        • 2017-07-21
        • 2014-12-31
        • 1970-01-01
        • 2014-08-13
        • 1970-01-01
        相关资源
        最近更新 更多