## Excluding rows

4 min read
``## Warning: package 'purrr' was built under R version 3.4.1``

This is another post about tidy evaluation, the new cool thing that appeared in the `dplyr` 0.7 series and is likely to be used everywhere in the tidyverse. In this post, we want to create a new verb `exclude` that kind of does the opposite of `filter`, i.e. the call would extract the tibble on the right.

``exclude( data, a == 1, b == 2 )`` So the condition we give to `exclude` control what we don’t want to see in the result, this is equivalent to these.

``````# more likely we would type this
filter( data, a != 1, b != 2 )

# but this will be easier to generate programmatically
filter( data, ! (a == 1), !(b == 2) )

# ... and using not instead spares us on bang
filter( data, not(a == 1), not(b == 2) )``````

We can do something similar to this previous post and use `Reduce` and `!!!` :

``````exclude1 <- function(data, ...){
dots <- quos(...)
filter( data, Reduce("&", map(list(!!!dots), ~not(.))) )
}``````
• First we get a logical vector for each condition by splicing the dots: `list(!!!dots)`
• We use `purrr::map` to negate them. (see how this uses `not` instaed of `!`) because we have enough bangs in the expression
• Then we iteratively reduce them into a single logical vector with `Reduce("&")`.
``exclude1(  data, a == 1, b == 2 )``
``````##   a b
## 1 2 1
## 2 2 3``````

This works fine, but it’s kind of hacky and asks `dplyr` to evaluate this complicated expression:

``filter( data, Reduce("&", map(list(a==1, b==2), ~not(.))) )``
``````##   a b
## 1 2 1
## 2 2 3``````

tidy eval lets us manipulate the expressions before we splice them. Let’s have a lot at the mysterious `dots` object that `quos` gives us:

``````curious <- function(...) quos(...)
curious( a == 1, b == 2)``````
``````## []
## <quosure: global>
## ~a == 1
##
## []
## <quosure: global>
## ~b == 2
##
## attr(,"class")
##  "quosures"``````

We get a list of `quosure`, so we can manipulate each of them with `purrr::map`. We just need a function that takes a quosure, and return a new quosure that wraps the previous expression in a `not` call and uses the same environment. This is what I came up with, perhaps there is a better way:

``````negate_quosure <- function(q){
set_env( quo(not(!!get_expr(q))), get_env(q))
}
q <- quo(a==1)
negate_quosure( q )``````
``````## <quosure: global>
## ~not(a == 1)``````
``````dots <- curious(a == 1, b == 2)
map( dots, negate_quosure )``````
``````## []
## <quosure: global>
## ~not(a == 1)
##
## []
## <quosure: global>
## ~not(b == 2)``````

Finally we can splice those modified quosures into a filter call:

``````exclude <- function(data, ...) {
ndots <- map( quos(...), negate_quosure )
filter( data, !!!ndots )
}
exclude( data, a == 1, b == 2 )``````
``````##   a b
## 1 2 1
## 2 2 3``````

And celebrate our `!!!` (aka bang bang bang) skills:

Lionel came to the rescue on twitter for a better implementation of `negate_quosure`

``````negate_quosure <- function(q){
quo(not(UQ(q)))
}
exclude <- function(data, ...) {
ndots <- map( quos(...), negate_quosure )
filter( data, !!!ndots )
}
exclude( data, a == 1, b == 2 )``````
``````##   a b
## 1 2 1
## 2 2 3``````

Actually while we are here, we can make this all thing `purrr`:

``````exclude <- function(data, ...) {
ndots <- map( quos(...), ~ quo(not(!!.x)) )
filter( data, !!!ndots )
}
exclude( data, a == 1, b == 2 )``````
``````##   a b
## 1 2 1
## 2 2 3``````