Search code examples
rdplyrrlang

How to specify sorting variable with desc() for dplyr::arrange using string?


The following code should sort a column, hp, in a descending order, but it fails.

Could someone please point out the problem?

data(mtcars)
result = dplyr::arrange( mtcars, !! rlang::expr("desc(hp)") )
head(result)
                   mpg cyl disp  hp drat    wt  qsec vs am gear carb
Mazda RX4         21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
Mazda RX4 Wag     21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
Datsun 710        22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
Hornet 4 Drive    21.4   6  258 110 3.08 3.215 19.44  1  0    3    1
Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2
Valiant           18.1   6  225 105 2.76 3.460 20.22  1  0    3    1

Additional note

Using str2lang() instead of rlang::expr() works fine. Could someone explain the reason?

data(mtcars)
result = dplyr::arrange( mtcars, !! str2lang("desc(hp)") )
                     mpg cyl disp  hp drat    wt  qsec vs am gear carb
Maserati Bora       15.0   8  301 335 3.54 3.570 14.60  0  1    5    8
Ford Pantera L      15.8   8  351 264 4.22 3.170 14.50  0  1    5    4
Duster 360          14.3   8  360 245 3.21 3.570 15.84  0  0    3    4
Camaro Z28          13.3   8  350 245 3.73 3.840 15.41  0  0    3    4
Chrysler Imperial   14.7   8  440 230 3.23 5.345 17.42  0  0    3    4
Lincoln Continental 10.4   8  460 215 3.00 5.424 17.82  0  0    3    4

Solution

  • To explain the problem, we need to look at what rlang::expr does. It captures an expression without evaluating it. So when you write rlang::expr("desc(hp)") the result is actually a string:

    # setup:
    mtcars <- dplyr::as_tibble(mtcars)
    
    eval(rlang::expr("desc(hp)"))
    #> [1] "desc(hp)"
    

    and by this no different than:

    dplyr::arrange(mtcars, "desc(hp)")
    #> # A tibble: 32 x 11
    #>      mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb
    #>    <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
    #>  1  21       6  160    110  3.9   2.62  16.5     0     1     4     4
    #>  2  21       6  160    110  3.9   2.88  17.0     0     1     4     4
    #>  3  22.8     4  108     93  3.85  2.32  18.6     1     1     4     1
    #>  4  21.4     6  258    110  3.08  3.22  19.4     1     0     3     1
    #>  5  18.7     8  360    175  3.15  3.44  17.0     0     0     3     2
    #>  6  18.1     6  225    105  2.76  3.46  20.2     1     0     3     1
    #>  7  14.3     8  360    245  3.21  3.57  15.8     0     0     3     4
    #>  8  24.4     4  147.    62  3.69  3.19  20       1     0     4     2
    #>  9  22.8     4  141.    95  3.92  3.15  22.9     1     0     4     2
    #> 10  19.2     6  168.   123  3.92  3.44  18.3     1     0     4     4
    #> # ... with 22 more rows
    

    to actually make it work with rlang::expr we need to use an actual expression and not a string as argument:

    dplyr::arrange(mtcars, !! rlang::expr(desc(hp)))
    #> # A tibble: 32 x 11
    #>      mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb
    #>    <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
    #>  1  15       8  301    335  3.54  3.57  14.6     0     1     5     8
    #>  2  15.8     8  351    264  4.22  3.17  14.5     0     1     5     4
    #>  3  14.3     8  360    245  3.21  3.57  15.8     0     0     3     4
    #>  4  13.3     8  350    245  3.73  3.84  15.4     0     0     3     4
    #>  5  14.7     8  440    230  3.23  5.34  17.4     0     0     3     4
    #>  6  10.4     8  460    215  3     5.42  17.8     0     0     3     4
    #>  7  10.4     8  472    205  2.93  5.25  18.0     0     0     3     4
    #>  8  16.4     8  276.   180  3.07  4.07  17.4     0     0     3     3
    #>  9  17.3     8  276.   180  3.07  3.73  17.6     0     0     3     3
    #> 10  15.2     8  276.   180  3.07  3.78  18       0     0     3     3
    #> # ... with 22 more rows
    

    Since you want to use a string we could use rlang::parse_expr as you already posted in your own answer:

    dplyr::arrange( mtcars, !! rlang::parse_expr("desc(hp)"))
    #> # A tibble: 32 x 11
    #>      mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb
    #>    <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
    #>  1  15       8  301    335  3.54  3.57  14.6     0     1     5     8
    #>  2  15.8     8  351    264  4.22  3.17  14.5     0     1     5     4
    #>  3  14.3     8  360    245  3.21  3.57  15.8     0     0     3     4
    #>  4  13.3     8  350    245  3.73  3.84  15.4     0     0     3     4
    #>  5  14.7     8  440    230  3.23  5.34  17.4     0     0     3     4
    #>  6  10.4     8  460    215  3     5.42  17.8     0     0     3     4
    #>  7  10.4     8  472    205  2.93  5.25  18.0     0     0     3     4
    #>  8  16.4     8  276.   180  3.07  4.07  17.4     0     0     3     3
    #>  9  17.3     8  276.   180  3.07  3.73  17.6     0     0     3     3
    #> 10  15.2     8  276.   180  3.07  3.78  18       0     0     3     3
    #> # ... with 22 more rows
    

    Created on 2021-11-03 by the reprex package (v2.0.1)