dplyr 0.7 in shiny apps: Will "_" versions of verbs really be removed from dplyr?

75 views
Skip to first unread message

Vincent

unread,
Jun 23, 2017, 10:34:40 PM6/23/17
to manipulatr
In shiny apps user input is often provided in string/character form. The new _at and _if versions of select are useful, e.g., 

select_at(mtcars, .vars = c("mpg", "cyl"))

But what if "mpg:cyl" is provided by the user as a string? The following works in 0.5 but I'm not sure about the translation to the new programming model:

select_(mtcars, .dots = "mpg:cyl")

Similarly, what about:

filter_(mtcars, .dots = "mpg > 30")

or 

mutate_(mtcars, .dots = c("mpg2 = mpg^2", "cyl2 = cyl^2"))

or

group_by_(mtcars, .dots = c("vs","cyl"))

or

arrange_(mtcars, .dots = c("desc(vs)", "cyl"))

etc.

where the user of the shiny app provides the string or character vector either through a textInput, selectInput, etc.

Hadley Wickham

unread,
Jun 24, 2017, 10:02:53 AM6/24/17
to Vincent, manipulatr
Parse the string then use unquoting — but note that taking arbitrary user input in a shiny app is potentially dangerous. 

Hadley
--
You received this message because you are subscribed to the Google Groups "manipulatr" group.
To unsubscribe from this group and stop receiving emails from it, send an email to manipulatr+unsubscribe@googlegroups.com.
To post to this group, send email to manip...@googlegroups.com.
Visit this group at https://groups.google.com/group/manipulatr.
For more options, visit https://groups.google.com/d/optout.


--
http://hadley.nz

Vincent

unread,
Jun 24, 2017, 7:20:28 PM6/24/17
to manipulatr, vincen...@gmail.com
Thanks for the quick response Hadley. I see how the following works:

group_by(mtcars, !! quo(cyl))
group_by(mtcars, !!! c(quo(cyl), quo(vs)))
arrange(mtcars, !! quo(desc(cyl)))

but that is not the input provided from, for example, a selectInput in Shiny. The following does not work so I assume I'm missing something.

group_by(mtcars, !! "cyl")
group_by(mtcars, !!! c("cyl", "vs") )
arrange(mtcars, !! "desc(cyl)")

Also, I'm not sure what you mean by "parse the string". Assuming the user provides the string "mpg:cyl" or "mpg > 30 & vs == 1" how should that be parsed, unquoted, and passed to dplyr::select and dplyr::filter in the new programming model?

select_(mtcars, "mpg:cyl")
filter_(mtcars, "mpg > 30 & vs == 1")

Thanks

Vincent

unread,
Jun 24, 2017, 10:13:55 PM6/24/17
to manipulatr, vincen...@gmail.com
Based on this answer https://stackoverflow.com/a/44593617/1974918 I came up with the following:
  
group_by_at(mtcars, .vars = "cyl")
group_by_at(mtcars, .vars = c("cyl", "vs"))

arr_ <- function(dat, ord) {
  ord <- lapply(ord, rlang::parse_expr)
  arrange(dat, !!! ord)
}

arr_(mtcars, "cyl")
arr_(mtcars, c("desc(cyl)", "vs"))

sel_ <- function(dat, sel) {
  sel <- lapply(sel, rlang::parse_expr)
  select(dat, !!! sel)
}

sel_(mtcars, c("cyl", "vs"))
sel_(mtcars, "cyl:vs")

filt_ <- function(dat, filt) {
  filt <- rlang::parse_expr(gsub(",", "&", filt))
  filter(dat, !! filt)
}

filt_(mtcars, "mpg > 30 & vs == 1")
filt_(mtcars, "mpg > 30, vs == 1")

I think these will be sufficiently general for my application but basically these are just less robust versions of of select_, arrange_, and filter_ so for people using shiny I really hope that the _ functions in dplyr do not go away

Hadley Wickham

unread,
Jun 25, 2017, 10:05:59 AM6/25/17
to Vincent, manipulatr
They're not less robust - they're doing exactly the same thing except that now you have to be more explicit about whether the strings are expressions or names. 

I would advise against adding the helpers you have below. Instead, create reactive expressions that coerce the inputs from shiny to the correct type (with sym() or parse_expr()) and then call the dplyr functions directly.

Hadley
--
You received this message because you are subscribed to the Google Groups "manipulatr" group.
To unsubscribe from this group and stop receiving emails from it, send an email to manipulatr+unsubscribe@googlegroups.com.
To post to this group, send email to manip...@googlegroups.com.
Visit this group at https://groups.google.com/group/manipulatr.
For more options, visit https://groups.google.com/d/optout.


--
http://hadley.nz

Vincent

unread,
Jun 26, 2017, 10:02:47 AM6/26/17
to manipulatr, vincen...@gmail.com
Thanks for the follow-up and suggestions Hadley. The app has a lot of inputs so I will need to add a quite a few reactives. I do also need the character/string representation of the inputs in most of the non-dplyr code and functions. I'll see if I can make this work

Hadley Wickham

unread,
Jun 26, 2017, 6:54:39 PM6/26/17
to Vincent, manipulatr
I assume you could also make a custom version of an inputTextbox that
returned the parsed expression.

Hadley
> --
> You received this message because you are subscribed to the Google Groups
> "manipulatr" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to manipulatr+...@googlegroups.com.

Vincent

unread,
Jul 7, 2017, 4:07:55 PM7/7/17
to manipulatr, vincen...@gmail.com
Nice suggestion about creating a custom textInput 

Not sure if this a good place to put this but it took me a while to figure how to use rename with strings in 0.7.1 and perhaps this can help someone else:

dplyr 0.5.0:

rename_(iris, .dots = setNames("Petal.Length", "petal_length"))

dplyr 0.7.1:

rename(iris, !!! setNames("Petal.Length", "petal_length"))

Hadley Wickham

unread,
Jul 7, 2017, 6:13:05 PM7/7/17
to Vincent, manipulatr


On Friday, July 7, 2017, Vincent <vincen...@gmail.com> wrote:
Nice suggestion about creating a custom textInput 

Not sure if this a good place to put this but it took me a while to figure how to use rename with strings in 0.7.1 and perhaps this can help someone else:

dplyr 0.5.0:

rename_(iris, .dots = setNames("Petal.Length", "petal_length"))

dplyr 0.7.1:

rename(iris, !!! setNames("Petal.Length", "petal_length"))

Or

rename(iris, !!"Petal.Length" := !!"petal_length")

 

On Monday, June 26, 2017 at 3:54:39 PM UTC-7, Hadley Wickham wrote:
I assume you could also make a custom version of an inputTextbox that
returned the parsed expression.

Hadley

On Mon, Jun 26, 2017 at 9:02 AM, Vincent <vincen...@gmail.com> wrote:
> Thanks for the follow-up and suggestions Hadley. The app has a lot of inputs
> so I will need to add a quite a few reactives. I do also need the
> character/string representation of the inputs in most of the non-dplyr code
> and functions. I'll see if I can make this work
>
> --
> You received this message because you are subscribed to the Google Groups
> "manipulatr" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to manipulatr+...@googlegroups.com.
> To post to this group, send email to manip...@googlegroups.com.
> Visit this group at https://groups.google.com/group/manipulatr.
> For more options, visit https://groups.google.com/d/optout.



--
http://hadley.nz


 

--
You received this message because you are subscribed to the Google Groups "manipulatr" group.
To unsubscribe from this group and stop receiving emails from it, send an email to manipulatr+unsubscribe@googlegroups.com.

To post to this group, send email to manip...@googlegroups.com.
Visit this group at https://groups.google.com/group/manipulatr.
For more options, visit https://groups.google.com/d/optout.


--
http://hadley.nz

Vincent

unread,
Jul 7, 2017, 8:24:18 PM7/7/17
to manipulatr, vincen...@gmail.com
Thanks Hadley! That is convenient when there is one variable to rename. Needed to flip the order of the strings for it to work with the iris dataset:

rename(iris, !!"petal_length" := !!"Petal.Length")

I was hoping the same approach would work with filter. However, the following doesn't work:

filter(iris, !! "Species" == "setosa")

However, these do work:

filter(iris, .data[["Species"]] == "setosa")

filter(iris, (!! sym("Species")) == "setosa")

Hadley Wickham

unread,
Jul 8, 2017, 11:50:46 AM7/8/17
to Vincent, manipulatr
On Fri, Jul 7, 2017 at 7:24 PM, Vincent <vincen...@gmail.com> wrote:
> Thanks Hadley! That is convenient when there is one variable to rename.
> Needed to flip the order of the strings for it to work with the iris
> dataset:
>
> rename(iris, !!"petal_length" := !!"Petal.Length")
>
> I was hoping the same approach would work with filter. However, the
> following doesn't work:
>
> filter(iris, !! "Species" == "setosa")

This doesn't work for two reasons:

* !! on the LHS of := has special behaviour because the LHS of an =
must be a name (so we can safely coerce a string)

* !! on the RHS works in select (but not elsewhere) because select()
can take column names as strings.

Hadley

--
http://hadley.nz
Reply all
Reply to author
Forward
0 new messages