From 584fd458f5755946468defee47258d57ef861b56 Mon Sep 17 00:00:00 2001 From: John Mount Date: Wed, 25 Aug 2021 12:43:45 -0700 Subject: [PATCH] update docs --- DESCRIPTION | 6 +- NEWS.md | 5 + README.md | 8 +- docs/404.html | 2 +- docs/LICENSE-text.html | 2 +- docs/articles/MutatePartitioner.html | 4 +- docs/articles/index.html | 2 +- docs/articles/named_map_builder.html | 4 +- docs/articles/rename_se.html | 4 +- docs/articles/seplyr.html | 34 +-- docs/articles/using_seplyr.html | 267 ++-------------------- docs/authors.html | 2 +- docs/index.html | 2 +- docs/news/index.html | 16 +- docs/pkgdown.yml | 2 +- docs/reference/add_count_se.html | 2 +- docs/reference/add_group_indices.html | 2 +- docs/reference/add_group_sub_indices.html | 2 +- docs/reference/add_group_summaries.html | 2 +- docs/reference/add_rank_indices.html | 2 +- docs/reference/add_tally_se.html | 2 +- docs/reference/arrange_se.html | 2 +- docs/reference/complete_se.html | 2 +- docs/reference/count_se.html | 2 +- docs/reference/deselect.html | 2 +- docs/reference/distinct_se.html | 2 +- docs/reference/factor_mutate.html | 2 +- docs/reference/filter_nse.html | 2 +- docs/reference/filter_se.html | 2 +- docs/reference/gather_se.html | 2 +- docs/reference/group_by_se.html | 2 +- docs/reference/group_indices_se.html | 2 +- docs/reference/group_mutate.html | 2 +- docs/reference/group_summarize.html | 2 +- docs/reference/group_transmute.html | 2 +- docs/reference/if_else_device.html | 2 +- docs/reference/index.html | 2 +- docs/reference/mutate_nse.html | 2 +- docs/reference/mutate_se.html | 2 +- docs/reference/mutate_seb.html | 2 +- docs/reference/novelName.html | 2 +- docs/reference/partition_mutate_qt.html | 2 +- docs/reference/partition_mutate_se.html | 2 +- docs/reference/quote_mutate.html | 2 +- docs/reference/reexports.html | 2 +- docs/reference/rename_se.html | 2 +- docs/reference/select_nse.html | 2 +- docs/reference/select_se.html | 2 +- docs/reference/seplyr.html | 2 +- docs/reference/spread_se.html | 2 +- docs/reference/summarize_nse.html | 2 +- docs/reference/summarize_se.html | 2 +- docs/reference/tally_se.html | 2 +- docs/reference/transmute_nse.html | 2 +- docs/reference/transmute_se.html | 2 +- vignettes/seplyr.Rmd | 15 +- vignettes/using_seplyr.Rmd | 166 +------------- 57 files changed, 109 insertions(+), 512 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 59a57dd..ec5b467 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,8 +1,8 @@ Package: seplyr Type: Package Title: Improved Standard Evaluation Interfaces for Common Data Manipulation Tasks -Version: 1.0.3 -Date: 2021-06-12 +Version: 1.0.4 +Date: 2021-08-25 Authors@R: c( person("John", "Mount", email = "jmount@win-vector.com", role = c("aut", "cre")), person(family = "Win-Vector LLC", role = c("cph")) @@ -24,7 +24,7 @@ License: GPL-2 | GPL-3 Encoding: UTF-8 Depends: R (>= 3.4.0), - wrapr (>= 2.0.7) + wrapr (>= 2.0.8) Imports: dplyr (>= 0.8.5), rlang, diff --git a/NEWS.md b/NEWS.md index 94ae3dd..1ab9b1b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,9 @@ + +# seplyr 1.0.4 2021-08-25 + + * Documentation fixes (catch up with rlang fixes). + # seplyr 1.0.3 2021-06-12 * Remove LazyData decl diff --git a/README.md b/README.md index 7594c3f..0043a6d 100644 --- a/README.md +++ b/README.md @@ -60,18 +60,18 @@ To install this package please either install from install.packages('seplyr') ``` -Please see [`help("%.>%", -package="wrapr")`](https://winvector.github.io/wrapr/reference/dot_arrow.html) +Please see +[`help("%.>%", package="wrapr")`](https://winvector.github.io/wrapr/reference/dot_arrow.html) for details on “dot pipe.” In addition to standard interface adapters `seplyr` supplies some non-trivial statement transforms: - - [`partition_mutate_se()`](https://winvector.github.io/seplyr/reference/partition_mutate_se.html): +- [`partition_mutate_se()`](https://winvector.github.io/seplyr/reference/partition_mutate_se.html): [vignette](https://winvector.github.io/seplyr/articles/MutatePartitioner.html), and [article](https://winvector.github.io/FluidData/partition_mutate.html). - - [`if_else_device()`](https://winvector.github.io/seplyr/reference/if_else_device.html): +- [`if_else_device()`](https://winvector.github.io/seplyr/reference/if_else_device.html): [article](https://winvector.github.io/FluidData/partition_mutate_ex2.html). ## Note diff --git a/docs/404.html b/docs/404.html index 3741f1c..1c3cdd6 100644 --- a/docs/404.html +++ b/docs/404.html @@ -71,7 +71,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/LICENSE-text.html b/docs/LICENSE-text.html index a0fca34..fffd335 100644 --- a/docs/LICENSE-text.html +++ b/docs/LICENSE-text.html @@ -71,7 +71,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/articles/MutatePartitioner.html b/docs/articles/MutatePartitioner.html index 5dd4035..6c2d467 100644 --- a/docs/articles/MutatePartitioner.html +++ b/docs/articles/MutatePartitioner.html @@ -31,7 +31,7 @@ seplyr - 1.0.3 + 1.0.4 @@ -94,7 +94,7 @@

Mutate Partitioner

John Mount, Win-Vector LLC

-

2021-06-12

+

2021-08-25

Source: vignettes/MutatePartitioner.Rmd diff --git a/docs/articles/index.html b/docs/articles/index.html index d75e6b4..0ccea69 100644 --- a/docs/articles/index.html +++ b/docs/articles/index.html @@ -71,7 +71,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/articles/named_map_builder.html b/docs/articles/named_map_builder.html index 6003d6e..37e22e9 100644 --- a/docs/articles/named_map_builder.html +++ b/docs/articles/named_map_builder.html @@ -31,7 +31,7 @@ seplyr - 1.0.3 + 1.0.4 @@ -94,7 +94,7 @@

Named Map Builder

John Mount

-

2021-06-12

+

2021-08-25

Source: vignettes/named_map_builder.Rmd diff --git a/docs/articles/rename_se.html b/docs/articles/rename_se.html index 68f998b..f0b399d 100644 --- a/docs/articles/rename_se.html +++ b/docs/articles/rename_se.html @@ -31,7 +31,7 @@ seplyr - 1.0.3 + 1.0.4 @@ -94,7 +94,7 @@

rename_se

John Mount

-

2021-06-12

+

2021-08-25

Source: vignettes/rename_se.Rmd diff --git a/docs/articles/seplyr.html b/docs/articles/seplyr.html index e9e3719..108cc68 100644 --- a/docs/articles/seplyr.html +++ b/docs/articles/seplyr.html @@ -31,7 +31,7 @@ seplyr - 1.0.3 + 1.0.4 @@ -94,7 +94,7 @@

seplyr

John Mount

-

2021-06-12

+

2021-08-25

Source: vignettes/seplyr.Rmd @@ -126,33 +126,13 @@

2021-06-12

## Merc 240D 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2 ## Merc 230 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2 ## Fiat 128 32.4 4 78.7 66 4.08 2.200 19.47 1 1 4 1 -

In dplyr 0.7.* if the names of the columns are coming from a variable set elsewhere you would to need to use a tool to substitute those names in. One such tool is rlang/tidyeval (though we strongly prefer seplyr and wrapr::let())). rlang/tidyeval works as follows (for comparison only, this is not our suggested workflow).

+

In dplyr 0.7.* if the names of the columns are coming from a variable set elsewhere you would to need to use a tool to substitute those names in. One such tool is rlang/tidyeval (though we strongly prefer seplyr.

 # Assume this is set elsewhere,
 # supplied by a user, function argument, or control file.
-orderTerms <- c('cyl', 'desc(gear)')
-
-# Now convert into splice-able types, the idea is the user
-# supplies variable names that we later convert to "quosures"
-# for use in `dplyr` 0.7.* generic code.
-# This code is near the pipe under the rule:
-# "If you are close enough to form a quosure, 
-#  you are close enough to re-code the analysis"
-orderQs <- lapply(orderTerms,
-                  function(si) { rlang::parse_expr(si) })
-# pipe
-datasets::mtcars %>% 
-  arrange(!!!orderQs) %>% 
-  head()
-
##                mpg cyl  disp  hp drat    wt  qsec vs am gear carb
-## Porsche 914-2 26.0   4 120.3  91 4.43 2.140 16.70  0  1    5    2
-## Lotus Europa  30.4   4  95.1 113 3.77 1.513 16.90  1  1    5    2
-## Datsun 710    22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1
-## Merc 240D     24.4   4 146.7  62 3.69 3.190 20.00  1  0    4    2
-## Merc 230      22.8   4 140.8  95 3.92 3.150 22.90  1  0    4    2
-## Fiat 128      32.4   4  78.7  66 4.08 2.200 19.47  1  1    4    1
+orderTerms <- c('cyl', 'desc(gear)')

If you don’t want to try and digest entire theory of quasi-quoting and splicing (the !!! operator) then you can use seplyr which conveniently and legibly wraps the operations as follows:

-
+
 
## Loading required package: wrapr
## 
@@ -160,7 +140,7 @@ 

2021-06-12

## The following object is masked from 'package:dplyr':
 ## 
 ##     coalesce
-
+
 datasets::mtcars %.>% 
   arrange_se(., orderTerms) %>% 
   head(.)
@@ -198,7 +178,7 @@

2021-06-12

  • rename_mp()
  • Here is a example using seplyr::summarize_se().

    -
    +
     datasets::iris %.>%
       group_by_se(., "Species") %.>%
       summarize_se(., c("Mean.Sepal.Length" := "mean(Sepal.Length)", 
    diff --git a/docs/articles/using_seplyr.html b/docs/articles/using_seplyr.html
    index ed10da0..7b058a4 100644
    --- a/docs/articles/using_seplyr.html
    +++ b/docs/articles/using_seplyr.html
    @@ -31,7 +31,7 @@
           
           
             seplyr
    -        1.0.3
    +        1.0.4
           
         
    @@ -94,7 +94,7 @@

    Using seplyr to Program Over dplyr

    John Mount

    -

    2021-06-12

    +

    2021-08-25

    Source: vignettes/using_seplyr.Rmd @@ -106,42 +106,7 @@

    2021-06-12

    Introduction

    -

    seplyr is an R package that makes it easy to program over dplyr 0.7.*.

    -

    To illustrate this we will work an example.

    -

    Suppose you had worked out a dplyr pipeline that performed an analysis you were interested in. For an example we could take something similar to one of the examples from the dplyr 0.7.0 announcement.

    - -
    ## [1] '1.0.5'
    -
    -colnames(starwars)
    -
    ##  [1] "name"       "height"     "mass"       "hair_color" "skin_color"
    -##  [6] "eye_color"  "birth_year" "sex"        "gender"     "homeworld" 
    -## [11] "species"    "films"      "vehicles"   "starships"
    -
    -starwars %>%
    -  group_by(homeworld) %>%
    -  summarise(mean_height = mean(height, na.rm = TRUE),
    -            mean_mass = mean(mass, na.rm = TRUE),
    -            count = n()) 
    -
    ## # A tibble: 49 x 4
    -##    homeworld      mean_height mean_mass count
    -##    <chr>                <dbl>     <dbl> <int>
    -##  1 Alderaan              176.      64       3
    -##  2 Aleen Minor            79       15       1
    -##  3 Bespin                175       79       1
    -##  4 Bestine IV            180      110       1
    -##  5 Cato Neimoidia        191       90       1
    -##  6 Cerea                 198       82       1
    -##  7 Champala              196      NaN       1
    -##  8 Chandrila             150      NaN       1
    -##  9 Concord Dawn          183       79       1
    -## 10 Corellia              175       78.5     2
    -## # … with 39 more rows
    -

    The above is colloquially called “an interactive script.” The name comes from the fact that we directly use names of variables (such as “homeworld”, which would only be known from looking at the data directly) in our analysis code. Only somebody interacting with the data could write such a script (hence the name).

    -

    It has long been considered a point of discomfort to convert such an interactive dplyr pipeline into a re-usable script or function. That is a script or function that specifies column names in some parametric or re-usable fashion. Roughly it means the names of the data columns are not yet known when we are writing the code (and this is what makes the code re-usable).

    -

    This inessential (or conquerable) difficulty is largely a due to the strong preference for non-standard evaluation interfaces (that is interfaces that capture and inspect un-evaluated expressions from their calling interface) in the design dplyr. The common tooling to work around dplyr’s non-standard interfaces is a system called alternately rlang (a name which is perhaps a bit overly ambitious as rlang is not the R language) or tidyeval (a name which is perhaps a bit derogatory, incorrectly labeling R itself as the “non-tidy” dialect relative to tidyeval). We, on the other hand, recommend using either the superior tooling offered by wrapr::let() (which actual pre-dates rlang and can be used with any package, not just those that are designed around it) or to avoid non-standard interface foibles by using a standard evaluation adapter for dplyr called seplyr (which we will discuss here).

    -

    Our contention is a dialect (or adaption) of dplyr itself (which we call seplyr for “standard evaluation plyr”) that emphasizes standard (or value carrying) interfaces is easy to use and does not require the complexities of rlang (as seplyr fails to introduce the difficulty rlang is designed to feast on).

    +

    seplyr is an R package that makes it easy to program over dplyr 0.7.* + without needing to directly use rlang notation.

    @@ -158,7 +123,17 @@

  • Replace “=” in expressions with “:=”.
  • Our converted code looks like the following.

    -
    +
    +
    ## 
    +## Attaching package: 'dplyr'
    +
    ## The following objects are masked from 'package:stats':
    +## 
    +##     filter, lag
    +
    ## The following objects are masked from 'package:base':
    +## 
    +##     intersect, setdiff, setequal, union
    +
    ## Loading required package: wrapr
    ## 
    @@ -166,7 +141,7 @@ 

    ## The following object is masked from 'package:dplyr':
     ## 
     ##     coalesce
    -
    +
     starwars %>%
       group_by_se("homeworld") %>%
       summarize_se(c("mean_height" := "mean(height, na.rm = TRUE)",
    @@ -189,7 +164,7 @@ 

    This code works the same as the original dplyr code. Also the translation could be performed by following the small set of explicit re-coding rules that we gave above.

    Obviously at this point all we have done is: worked to make the code a bit less pleasant looking. We have yet to see any benefit from this conversion (though we can turn this on its head and say all the original dplyr notation is saving us is from having to write a few quote marks).

    The benefit is: this new code can very easily be parameterized and wrapped in a re-usable function. In fact it is now simpler to do than to describe.

    -
    +
     grouped_mean <- function(data, 
                              grouping_variables, 
                              value_variables,
    @@ -231,219 +206,11 @@ 

    To be sure: there are some clunky details of using to build up the expressions, but the conversion process is very regular and easy. In seplyr parametric programming is intentionally easy (just replace values with variables).

    -
    -

    -rlang/tidyeval

    -

    The above may all seem academic as “dplyr supplies its own parametric abstraction layer: rlang/tidyeval.” However, in our opinion rlang abstraction is irregular and full of corner cases (even for simple column names). When you are programming of rlang you are programming over a language that is not quite R, and a language that is not in fact superior to R. Take for example the following example from the dplyr 0.7.0 announcement:

    -

    -

    The above code appears to work, unless you take the extra steps of actually running it and examining the results.

    -
    -packageVersion("dplyr")
    -
    ## [1] '1.0.5'
    -
    -packageVersion("rlang")
    -
    ## [1] '0.4.10'
    -
    -packageVersion("tidyselect")
    -
    ## [1] '1.1.0'
    -
    -my_var <- "homeworld"
    -
    -starwars %>%
    -  group_by(.data[[my_var]]) %>%
    -  summarise_at(vars(height:mass), mean, na.rm = TRUE)
    -
    ## # A tibble: 49 x 3
    -##    homeworld      height  mass
    -##    <chr>           <dbl> <dbl>
    -##  1 Alderaan         176.  64  
    -##  2 Aleen Minor       79   15  
    -##  3 Bespin           175   79  
    -##  4 Bestine IV       180  110  
    -##  5 Cato Neimoidia   191   90  
    -##  6 Cerea            198   82  
    -##  7 Champala         196  NaN  
    -##  8 Chandrila        150  NaN  
    -##  9 Concord Dawn     183   79  
    -## 10 Corellia         175   78.5
    -## # … with 39 more rows
    -

    Notice the created grouping column is called “my_var”, and nothomeworld”. This may seem like a small thing, but it will cause any downstream processing to fail. One can try variations and get many different results, some of which are correct and some of which are not.

    -
    -# wrong
    -starwars %>%
    -  group_by(!!my_var) %>%
    -  summarise_at(vars(height:mass), mean, na.rm = TRUE)
    -
    ## # A tibble: 1 x 3
    -##   `"homeworld"` height  mass
    -##   <chr>          <dbl> <dbl>
    -## 1 homeworld       174.  97.3
    -
    -# correct, or at least appears to work
    -starwars %>%
    -  group_by(!!rlang::sym(my_var)) %>%
    -  summarise_at(vars(height:mass), mean, na.rm = TRUE)
    -
    ## # A tibble: 49 x 3
    -##    homeworld      height  mass
    -##    <chr>           <dbl> <dbl>
    -##  1 Alderaan         176.  64  
    -##  2 Aleen Minor       79   15  
    -##  3 Bespin           175   79  
    -##  4 Bestine IV       180  110  
    -##  5 Cato Neimoidia   191   90  
    -##  6 Cerea            198   82  
    -##  7 Champala         196  NaN  
    -##  8 Chandrila        150  NaN  
    -##  9 Concord Dawn     183   79  
    -## 10 Corellia         175   78.5
    -## # … with 39 more rows
    -
    -# correct, or at least appears to work
    -starwars %>%
    -  group_by(.data[[!!my_var]]) %>%
    -  summarise_at(vars(height:mass), mean, na.rm = TRUE)
    -
    ## # A tibble: 49 x 3
    -##    homeworld      height  mass
    -##    <chr>           <dbl> <dbl>
    -##  1 Alderaan         176.  64  
    -##  2 Aleen Minor       79   15  
    -##  3 Bespin           175   79  
    -##  4 Bestine IV       180  110  
    -##  5 Cato Neimoidia   191   90  
    -##  6 Cerea            198   82  
    -##  7 Champala         196  NaN  
    -##  8 Chandrila        150  NaN  
    -##  9 Concord Dawn     183   79  
    -## 10 Corellia         175   78.5
    -## # … with 39 more rows
    -
    -# errors-out
    -starwars %>%
    -  group_by(.data[[!!rlang::sym(my_var)]]) %>%
    -  summarise_at(vars(height:mass), mean, na.rm = TRUE)
    -
    ## # A tibble: 49 x 3
    -##    homeworld      height  mass
    -##    <chr>           <dbl> <dbl>
    -##  1 Alderaan         176.  64  
    -##  2 Aleen Minor       79   15  
    -##  3 Bespin           175   79  
    -##  4 Bestine IV       180  110  
    -##  5 Cato Neimoidia   191   90  
    -##  6 Cerea            198   82  
    -##  7 Champala         196  NaN  
    -##  8 Chandrila        150  NaN  
    -##  9 Concord Dawn     183   79  
    -## 10 Corellia         175   78.5
    -## # … with 39 more rows
    -

    Notice above in some places strings are required and in others names or symbols are required. In some contexts one may substitute for the other, and in some contexts they can not. In some situations “!!” must be used, and in others it must not be used. In some situations “rlang::sym()” must be used, and in some situations it must not be used. In some situations errors are signaled, and in others a bad result is quietly returned.

    -

    The details above are possibly teachable (in the sense one can rote memorize them, once APIs stabilize and stop changing), but they are not friendly to exploration or discoverable. There are a needlessly large number of moving pieces (strings, names, symbols, quoting, quosures, de-quoting, “data pronouns”, select semantics, non-select semantics, = versus :=, and so on) and one is expected to know which combinations of these items work together in which context.

    -

    For a further example consider the following.

    -
    -# correct, or at least appears to work
    -starwars %>%
    -  select(my_var) %>%
    -  head(n=2)
    -
    ## Note: Using an external vector in selections is ambiguous.
    -## ℹ Use `all_of(my_var)` instead of `my_var` to silence this message.
    -## ℹ See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
    -## This message is displayed once per session.
    -
    ## # A tibble: 2 x 1
    -##   homeworld
    -##   <chr>    
    -## 1 Tatooine 
    -## 2 Tatooine
    -
    -# errors-out
    -starwars %>%
    -  group_by(my_var) %>%
    -  head(n=2)
    -
    ## Error: Must group by variables found in `.data`.
    -## * Column `my_var` is not found.
    -

    The “use the value unless the variable name is a column” semantics of select can make code hard to read and potentially unsafe.
    -The point is the user does not know what column a select() actually chooses unless that know all of:

    -
      -
    • The name of the variable being used (in this case “my_var”).
    • -
    • The value in the variable.
    • -
    • If the data.frame does or does not have a column matching the variable name.
    • -
    -

    Column-name coincidences are actually likely in re-usable code. Often when generalizing a sequence of operations, one often chooses an existing column name as the name of a column control variable.

    -

    Let’s look at examples.

    -
    -my_var <- "homeworld"
    -# selects height column
    -starwars %>%
    -  select(my_var) %>%
    -  head(n=2)
    -
    ## # A tibble: 2 x 1
    -##   homeworld
    -##   <chr>    
    -## 1 Tatooine 
    -## 2 Tatooine
    -
    -# perhaps different data with an extra column
    -starwars_plus <- starwars %>%
    -  mutate(my_var = seq_len(nrow(starwars)))
    -
    -# same code now selects my_var column
    -starwars_plus %>%
    -  select(my_var) %>%
    -  head(n=2)
    -
    ## # A tibble: 2 x 1
    -##   my_var
    -##    <int>
    -## 1      1
    -## 2      2
    -
    -# original official example code now errors-out
    -starwars_plus %>%
    -  group_by(.data[[my_var]]) %>%
    -  summarise_at(vars(height:mass), mean, na.rm = TRUE)
    -
    ## # A tibble: 49 x 3
    -##    homeworld      height  mass
    -##    <chr>           <dbl> <dbl>
    -##  1 Alderaan         176.  64  
    -##  2 Aleen Minor       79   15  
    -##  3 Bespin           175   79  
    -##  4 Bestine IV       180  110  
    -##  5 Cato Neimoidia   191   90  
    -##  6 Cerea            198   82  
    -##  7 Champala         196  NaN  
    -##  8 Chandrila        150  NaN  
    -##  9 Concord Dawn     183   79  
    -## 10 Corellia         175   78.5
    -## # … with 39 more rows
    -

    The above might be acceptable in interactive work, if we assume the user knows the names of all of the columns in the data.frame and therefore can be held responsible for avoiding column name to variable coincidences. Or more realistically: the user is working interactively in the sense they are there to alter and re-run the code if they happen to notice the failure. However, this is not a safe state of affairs in re-usable functions or packages: which should work even if their are naming coincidences in thought to be irrelevant extra columns.

    -

    rlang does have methods to write unambiguous code such as in:

    -
    -starwars %>%
    -  select(!!my_var) %>%
    -  head(n=2)
    -
    ## # A tibble: 2 x 1
    -##   homeworld
    -##   <chr>    
    -## 1 Tatooine 
    -## 2 Tatooine
    -

    However, because the dplyr interface does not insist on the “!!” notation, the “!!” notation will not always be remembered. rlang/tidyeval interfaces tend to be complex and irregular.

    -

    The seplyr::select_se() interface is intentionally simple and regular: it always looks for columns using the strings (not variable names) given.

    -
    -print(my_var)
    -
    ## [1] "homeworld"
    -
    -# selects homeworld column (the value specified my_var) independent of
    -# any coincidence between variable name and column names.
    -starwars_plus %>% 
    -  select_se(my_var) %>%
    -  head(n=2)
    -
    ## # A tibble: 2 x 1
    -##   homeworld
    -##   <chr>    
    -## 1 Tatooine 
    -## 2 Tatooine
    -

    We have found when a programing notation is irregular it discourages exploration and leads to learned helplessness (rote appeals to authority and manuals instead of rapid acquisition of patterns and skills).

    -

    Conclusion

    The seplyr methodology is simple, easy to teach, and powerful.

    -

    The methodologies we discussed differ in philosophy.

    +

    There are alternatives that differ in philosophy.

    • rlang/tidyeval is often used to build new non-standard interfaces on top of existing non-standard interfaces. This is needed because non-standard interfaces do not naturally compose. The tidy/non-tidy analogy is: it works around a mess by introducing a new mess.
    • diff --git a/docs/authors.html b/docs/authors.html index a2e6fc1..6c3beef 100644 --- a/docs/authors.html +++ b/docs/authors.html @@ -71,7 +71,7 @@ seplyr - 1.0.3 + 1.0.4
    diff --git a/docs/index.html b/docs/index.html index 2270e9b..e44c0af 100644 --- a/docs/index.html +++ b/docs/index.html @@ -40,7 +40,7 @@ seplyr - 1.0.3 + 1.0.4
    diff --git a/docs/news/index.html b/docs/news/index.html index ade859d..ac36d87 100644 --- a/docs/news/index.html +++ b/docs/news/index.html @@ -71,7 +71,7 @@ seplyr - 1.0.3 + 1.0.4

    @@ -135,9 +135,17 @@

    Changelog

    Source: NEWS.md
    -
    -

    -seplyr 1.0.2 2021-06-12 Unreleased +
    +

    +seplyr 1.0.4 2021-08-25 Unreleased +

    +
      +
    • Documentation fixes (catch up with rlang fixes).
    • +
    +
    +
    +

    +seplyr 1.0.3 2021-06-12 2021-06-12

    • Remove LazyData decl
    • diff --git a/docs/pkgdown.yml b/docs/pkgdown.yml index 3e5ffc9..5593ed1 100644 --- a/docs/pkgdown.yml +++ b/docs/pkgdown.yml @@ -7,5 +7,5 @@ articles: rename_se: rename_se.html seplyr: seplyr.html using_seplyr: using_seplyr.html -last_built: 2021-06-12T13:44Z +last_built: 2021-08-25T19:42Z diff --git a/docs/reference/add_count_se.html b/docs/reference/add_count_se.html index ddbe6ca..0c2c445 100644 --- a/docs/reference/add_count_se.html +++ b/docs/reference/add_count_se.html @@ -72,7 +72,7 @@ seplyr - 1.0.3 + 1.0.4
    diff --git a/docs/reference/add_group_indices.html b/docs/reference/add_group_indices.html index 82cf7ac..07ad8e5 100644 --- a/docs/reference/add_group_indices.html +++ b/docs/reference/add_group_indices.html @@ -72,7 +72,7 @@ seplyr - 1.0.3 + 1.0.4

    diff --git a/docs/reference/add_group_sub_indices.html b/docs/reference/add_group_sub_indices.html index f673db4..6c7e63d 100644 --- a/docs/reference/add_group_sub_indices.html +++ b/docs/reference/add_group_sub_indices.html @@ -72,7 +72,7 @@ seplyr - 1.0.3 + 1.0.4

    diff --git a/docs/reference/add_group_summaries.html b/docs/reference/add_group_summaries.html index 9fef6ff..d710e83 100644 --- a/docs/reference/add_group_summaries.html +++ b/docs/reference/add_group_summaries.html @@ -75,7 +75,7 @@ seplyr - 1.0.3 + 1.0.4

    diff --git a/docs/reference/add_rank_indices.html b/docs/reference/add_rank_indices.html index 9e9c399..7e5e576 100644 --- a/docs/reference/add_rank_indices.html +++ b/docs/reference/add_rank_indices.html @@ -72,7 +72,7 @@ seplyr - 1.0.3 + 1.0.4
    diff --git a/docs/reference/add_tally_se.html b/docs/reference/add_tally_se.html index 0be209d..a190355 100644 --- a/docs/reference/add_tally_se.html +++ b/docs/reference/add_tally_se.html @@ -72,7 +72,7 @@ seplyr - 1.0.3 + 1.0.4
    diff --git a/docs/reference/arrange_se.html b/docs/reference/arrange_se.html index 0ce639c..473e116 100644 --- a/docs/reference/arrange_se.html +++ b/docs/reference/arrange_se.html @@ -76,7 +76,7 @@ seplyr - 1.0.3 + 1.0.4
    diff --git a/docs/reference/complete_se.html b/docs/reference/complete_se.html index eb13e83..4ce3537 100644 --- a/docs/reference/complete_se.html +++ b/docs/reference/complete_se.html @@ -72,7 +72,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/reference/count_se.html b/docs/reference/count_se.html index 4e5b9dd..9a1a247 100644 --- a/docs/reference/count_se.html +++ b/docs/reference/count_se.html @@ -72,7 +72,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/reference/deselect.html b/docs/reference/deselect.html index 27e7c0f..de8b558 100644 --- a/docs/reference/deselect.html +++ b/docs/reference/deselect.html @@ -72,7 +72,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/reference/distinct_se.html b/docs/reference/distinct_se.html index 18d5cc8..4f7c16f 100644 --- a/docs/reference/distinct_se.html +++ b/docs/reference/distinct_se.html @@ -72,7 +72,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/reference/factor_mutate.html b/docs/reference/factor_mutate.html index 2282ee2..49c3b1d 100644 --- a/docs/reference/factor_mutate.html +++ b/docs/reference/factor_mutate.html @@ -73,7 +73,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/reference/filter_nse.html b/docs/reference/filter_nse.html index 6a7df9f..502b64c 100644 --- a/docs/reference/filter_nse.html +++ b/docs/reference/filter_nse.html @@ -72,7 +72,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/reference/filter_se.html b/docs/reference/filter_se.html index ec3cd96..aeece68 100644 --- a/docs/reference/filter_se.html +++ b/docs/reference/filter_se.html @@ -73,7 +73,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/reference/gather_se.html b/docs/reference/gather_se.html index 1ecc955..642908a 100644 --- a/docs/reference/gather_se.html +++ b/docs/reference/gather_se.html @@ -75,7 +75,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/reference/group_by_se.html b/docs/reference/group_by_se.html index 2fa8a00..5304bf8 100644 --- a/docs/reference/group_by_se.html +++ b/docs/reference/group_by_se.html @@ -73,7 +73,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/reference/group_indices_se.html b/docs/reference/group_indices_se.html index 00c4272..e6113f7 100644 --- a/docs/reference/group_indices_se.html +++ b/docs/reference/group_indices_se.html @@ -72,7 +72,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/reference/group_mutate.html b/docs/reference/group_mutate.html index b4be7b1..46a45c5 100644 --- a/docs/reference/group_mutate.html +++ b/docs/reference/group_mutate.html @@ -76,7 +76,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/reference/group_summarize.html b/docs/reference/group_summarize.html index 67b139d..21b299c 100644 --- a/docs/reference/group_summarize.html +++ b/docs/reference/group_summarize.html @@ -76,7 +76,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/reference/group_transmute.html b/docs/reference/group_transmute.html index d0d61c4..9b0f001 100644 --- a/docs/reference/group_transmute.html +++ b/docs/reference/group_transmute.html @@ -76,7 +76,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/reference/if_else_device.html b/docs/reference/if_else_device.html index e1716ca..4cd98b6 100644 --- a/docs/reference/if_else_device.html +++ b/docs/reference/if_else_device.html @@ -84,7 +84,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/reference/index.html b/docs/reference/index.html index 559e9aa..4baa44e 100644 --- a/docs/reference/index.html +++ b/docs/reference/index.html @@ -71,7 +71,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/reference/mutate_nse.html b/docs/reference/mutate_nse.html index 466efe1..57f3eec 100644 --- a/docs/reference/mutate_nse.html +++ b/docs/reference/mutate_nse.html @@ -72,7 +72,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/reference/mutate_se.html b/docs/reference/mutate_se.html index e7b4e5a..ed799d3 100644 --- a/docs/reference/mutate_se.html +++ b/docs/reference/mutate_se.html @@ -75,7 +75,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/reference/mutate_seb.html b/docs/reference/mutate_seb.html index 377f59e..dd29384 100644 --- a/docs/reference/mutate_seb.html +++ b/docs/reference/mutate_seb.html @@ -72,7 +72,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/reference/novelName.html b/docs/reference/novelName.html index c3c773e..59147ac 100644 --- a/docs/reference/novelName.html +++ b/docs/reference/novelName.html @@ -72,7 +72,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/reference/partition_mutate_qt.html b/docs/reference/partition_mutate_qt.html index 2842986..8b47d51 100644 --- a/docs/reference/partition_mutate_qt.html +++ b/docs/reference/partition_mutate_qt.html @@ -77,7 +77,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/reference/partition_mutate_se.html b/docs/reference/partition_mutate_se.html index 7078aa2..e377fed 100644 --- a/docs/reference/partition_mutate_se.html +++ b/docs/reference/partition_mutate_se.html @@ -77,7 +77,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/reference/quote_mutate.html b/docs/reference/quote_mutate.html index f318e9a..bf222d1 100644 --- a/docs/reference/quote_mutate.html +++ b/docs/reference/quote_mutate.html @@ -72,7 +72,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/reference/reexports.html b/docs/reference/reexports.html index 0923cf0..02538aa 100644 --- a/docs/reference/reexports.html +++ b/docs/reference/reexports.html @@ -77,7 +77,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/reference/rename_se.html b/docs/reference/rename_se.html index 71ae11c..f40cd29 100644 --- a/docs/reference/rename_se.html +++ b/docs/reference/rename_se.html @@ -74,7 +74,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/reference/select_nse.html b/docs/reference/select_nse.html index 592b3dc..7eab216 100644 --- a/docs/reference/select_nse.html +++ b/docs/reference/select_nse.html @@ -77,7 +77,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/reference/select_se.html b/docs/reference/select_se.html index 4e611b2..2ac8d89 100644 --- a/docs/reference/select_se.html +++ b/docs/reference/select_se.html @@ -72,7 +72,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/reference/seplyr.html b/docs/reference/seplyr.html index c71e924..00e0834 100644 --- a/docs/reference/seplyr.html +++ b/docs/reference/seplyr.html @@ -73,7 +73,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/reference/spread_se.html b/docs/reference/spread_se.html index 0e2cd12..9011c0c 100644 --- a/docs/reference/spread_se.html +++ b/docs/reference/spread_se.html @@ -75,7 +75,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/reference/summarize_nse.html b/docs/reference/summarize_nse.html index ffe9198..0b5d580 100644 --- a/docs/reference/summarize_nse.html +++ b/docs/reference/summarize_nse.html @@ -72,7 +72,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/reference/summarize_se.html b/docs/reference/summarize_se.html index a9a09d2..3d1dc04 100644 --- a/docs/reference/summarize_se.html +++ b/docs/reference/summarize_se.html @@ -73,7 +73,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/reference/tally_se.html b/docs/reference/tally_se.html index 6d38a7f..b893aca 100644 --- a/docs/reference/tally_se.html +++ b/docs/reference/tally_se.html @@ -72,7 +72,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/reference/transmute_nse.html b/docs/reference/transmute_nse.html index 500d2a3..ac45020 100644 --- a/docs/reference/transmute_nse.html +++ b/docs/reference/transmute_nse.html @@ -72,7 +72,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/docs/reference/transmute_se.html b/docs/reference/transmute_se.html index 667a95c..4f6f332 100644 --- a/docs/reference/transmute_se.html +++ b/docs/reference/transmute_se.html @@ -73,7 +73,7 @@ seplyr - 1.0.3 + 1.0.4 diff --git a/vignettes/seplyr.Rmd b/vignettes/seplyr.Rmd index 8dc9a4e..748e54f 100644 --- a/vignettes/seplyr.Rmd +++ b/vignettes/seplyr.Rmd @@ -34,25 +34,12 @@ datasets::mtcars %>% head() ``` -In `dplyr` `0.7.*` if the names of the columns are coming from a variable set elsewhere you would to need to use a tool to substitute those names in. One such tool is `rlang`/`tidyeval` (though we strongly prefer [`seplyr`](https://github.com/WinVector/seplyr/blob/master/README.md) and [`wrapr::let()`](https://github.com/WinVector/wrapr/blob/master/README.md))). `rlang`/`tidyeval` works as follows (for comparison only, this is *not* our suggested workflow). +In `dplyr` `0.7.*` if the names of the columns are coming from a variable set elsewhere you would to need to use a tool to substitute those names in. One such tool is `rlang`/`tidyeval` (though we strongly prefer [`seplyr`](https://github.com/WinVector/seplyr/blob/master/README.md). ```{r ex1b} # Assume this is set elsewhere, # supplied by a user, function argument, or control file. orderTerms <- c('cyl', 'desc(gear)') - -# Now convert into splice-able types, the idea is the user -# supplies variable names that we later convert to "quosures" -# for use in `dplyr` 0.7.* generic code. -# This code is near the pipe under the rule: -# "If you are close enough to form a quosure, -# you are close enough to re-code the analysis" -orderQs <- lapply(orderTerms, - function(si) { rlang::parse_expr(si) }) -# pipe -datasets::mtcars %>% - arrange(!!!orderQs) %>% - head() ``` If you don't want to try and digest entire theory of quasi-quoting and splicing (the `!!!` operator) then you can use `seplyr` which conveniently and legibly wraps the operations as follows: diff --git a/vignettes/using_seplyr.Rmd b/vignettes/using_seplyr.Rmd index a0f4fa4..bc6b774 100644 --- a/vignettes/using_seplyr.Rmd +++ b/vignettes/using_seplyr.Rmd @@ -11,32 +11,8 @@ vignette: > ## Introduction -[`seplyr`](https://github.com/WinVector/seplyr) is an [`R`](https://www.r-project.org) package that makes it easy to program over [`dplyr` `0.7.*`]( https://CRAN.R-project.org/package=dplyr). +[`seplyr`](https://github.com/WinVector/seplyr) is an [`R`](https://www.r-project.org) package that makes it easy to program over [`dplyr` `0.7.*` +]( https://CRAN.R-project.org/package=dplyr) without needing to directly use `rlang` notation. -To illustrate this we will work an example. - -Suppose you had worked out a `dplyr` pipeline that performed an analysis you were interested in. For an example we could take something similar to one of the examples from the [`dplyr` `0.7.0` announcement](https://blog.rstudio.com/2017/06/13/dplyr-0-7-0/). - -```{r exi} -suppressPackageStartupMessages(library("dplyr")) -packageVersion("dplyr") - -colnames(starwars) - -starwars %>% - group_by(homeworld) %>% - summarise(mean_height = mean(height, na.rm = TRUE), - mean_mass = mean(mass, na.rm = TRUE), - count = n()) -``` - -The above is colloquially called "an interactive script." The name comes from the fact that we directly use names of variables (such as "`homeworld`", which would only be known from looking at the data directly) in our analysis code. Only somebody interacting with the data could write such a script (hence the name). - -It has long been considered a point of discomfort to convert such an interactive `dplyr` pipeline into a re-usable script or function. That is a script or function that specifies column names in some [parametric](https://win-vector.com/2017/04/21/programming-over-r/) or re-usable fashion. Roughly it means the names of the data columns are not yet known when we are writing the code (and this is what makes the code re-usable). - -This inessential (or conquerable) difficulty is largely a due to the strong preference for [non-standard evaluation interfaces](http://adv-r.had.co.nz/Computing-on-the-language.html) (that is interfaces that capture and inspect un-evaluated expressions from their calling interface) in the design `dplyr`. The common tooling to work around `dplyr`'s non-standard interfaces is a system called alternately `rlang` (a name which is perhaps a bit overly ambitious as `rlang` is not the `R` language) or tidyeval (a name which is perhaps a bit derogatory, incorrectly labeling `R` itself as the "non-tidy" dialect relative to tidyeval). We, on the other hand, recommend using either the superior tooling offered by [`wrapr::let()`](https://winvector.github.io/wrapr/reference/let.html) (which actual pre-dates `rlang` and can be used with any package, not just those that are designed around it) or to avoid non-standard interface foibles by using a standard evaluation adapter for `dplyr` called [`seplyr`]( https://CRAN.R-project.org/package=seplyr) (which we will discuss here). - -Our contention is a dialect (or adaption) of `dplyr` itself (which we call `seplyr` for "standard evaluation plyr") that emphasizes standard (or value carrying) interfaces is easy to use and does not require the complexities of `rlang` (as `seplyr` fails to introduce the difficulty `rlang` is designed to feast on). ## `seplyr` @@ -57,8 +33,14 @@ Our converted code looks like the following. ```{r exc} +library("dplyr") +``` + +```{r} library("seplyr") +``` +```{r} starwars %>% group_by_se("homeworld") %>% summarize_se(c("mean_height" := "mean(height, na.rm = TRUE)", @@ -104,144 +86,12 @@ We have translated our original interactive or ad-hoc calculation into a paramet To be sure: there are some clunky details of using \code{paste0()} to build up the expressions, but the conversion process is very regular and easy. In `seplyr` parametric programming is intentionally easy (just replace values with variables). -## `rlang`/tidyeval - -The above may all seem academic as "`dplyr` supplies its own parametric abstraction layer: `rlang`/tidyeval." However, in our opinion `rlang` abstraction is irregular and full of corner cases (even for simple column names). When you are programming of `rlang` you are programming over a language that is not quite `R`, and a language that is not in fact superior to `R`. Take for example the following example from the [`dplyr` `0.7.0` announcement](https://blog.rstudio.com/2017/06/13/dplyr-0-7-0/): - -![](dplyr_rlan_example.png) - -The above code appears to work, unless you take the extra steps of actually running it and examining the results. - -```{r rlde1} -packageVersion("dplyr") -packageVersion("rlang") -packageVersion("tidyselect") - -my_var <- "homeworld" - -starwars %>% - group_by(.data[[my_var]]) %>% - summarise_at(vars(height:mass), mean, na.rm = TRUE) -``` - -Notice the created grouping column is called "`my_var`", and *not* "`homeworld`". This may *seem* like a small thing, but it will cause any downstream processing to fail. One can try variations and get many different results, some of which are correct and some of which are not. - -```{r rlde2} -# wrong -starwars %>% - group_by(!!my_var) %>% - summarise_at(vars(height:mass), mean, na.rm = TRUE) -``` - - -```{r rlde3} -# correct, or at least appears to work -starwars %>% - group_by(!!rlang::sym(my_var)) %>% - summarise_at(vars(height:mass), mean, na.rm = TRUE) -``` - -```{r rlde4} -# correct, or at least appears to work -starwars %>% - group_by(.data[[!!my_var]]) %>% - summarise_at(vars(height:mass), mean, na.rm = TRUE) -``` - -```{r rlde5, error=TRUE} -# errors-out -starwars %>% - group_by(.data[[!!rlang::sym(my_var)]]) %>% - summarise_at(vars(height:mass), mean, na.rm = TRUE) -``` - -Notice above in some places strings are required and in others names or symbols are required. In some contexts one may substitute for the other, and in some contexts they can not. In some situations "`!!`" must be used, and in others it must not be used. In some situations "`rlang::sym()`" must be used, and in some situations it must not be used. In some situations errors are signaled, and in others a bad result is quietly returned. - -The details above are [possibly teachable](https://dplyr.tidyverse.org/articles/programming.html) (in the sense one can rote memorize them, once APIs stabilize and stop changing), but they are not friendly to exploration or discoverable. There are a needlessly large number of moving pieces (strings, names, symbols, quoting, quosures, de-quoting, "data pronouns", select semantics, non-select semantics, `=` versus `:=`, and so on) and one is expected to know which combinations of these items work together in which context. - -For a further example consider the following. - -```{r rdse1, error=TRUE} -# correct, or at least appears to work -starwars %>% - select(my_var) %>% - head(n=2) - -# errors-out -starwars %>% - group_by(my_var) %>% - head(n=2) -``` - -The "use the value unless the variable name is a column" semantics of select can make code hard to read and potentially unsafe. -The point is the user does not know what column a `select()` actually chooses unless that know all of: - - * The name of the variable being used (in this case "`my_var`"). - * The value in the variable. - * If the `data.frame` does or does not have a column matching the variable name. - -Column-name coincidences are actually likely in re-usable code. Often when generalizing a sequence of operations, -one often chooses an existing column name as the name of a column control variable. - -Let's look at examples. - -```{r rdsel2, error=TRUE} -my_var <- "homeworld" -# selects height column -starwars %>% - select(my_var) %>% - head(n=2) - -# perhaps different data with an extra column -starwars_plus <- starwars %>% - mutate(my_var = seq_len(nrow(starwars))) - -# same code now selects my_var column -starwars_plus %>% - select(my_var) %>% - head(n=2) - -# original official example code now errors-out -starwars_plus %>% - group_by(.data[[my_var]]) %>% - summarise_at(vars(height:mass), mean, na.rm = TRUE) -``` - - -The above *might* be acceptable in interactive work, if we assume the user knows the names of all of the columns in the `data.frame` and -therefore can be held responsible for avoiding column name to variable coincidences. Or more realistically: the user is working -interactively in the sense they are there to alter and re-run the code if they happen to notice the failure. However, this is not a safe state of -affairs in re-usable functions or packages: which should work even if their are naming coincidences in thought to be irrelevant extra columns. - -`rlang` does have methods to write unambiguous code such as in: - -```{r rlangu} -starwars %>% - select(!!my_var) %>% - head(n=2) -``` - -However, because the `dplyr` interface does not insist on the "`!!`" notation, the "`!!`" notation will not always be remembered. `rlang`/tidyeval interfaces tend to be complex and irregular. - -The `seplyr::select_se()` interface is intentionally simple and regular: it always looks for columns using the strings (not variable names) given. - -```{r sesel} -print(my_var) - -# selects homeworld column (the value specified my_var) independent of -# any coincidence between variable name and column names. -starwars_plus %>% - select_se(my_var) %>% - head(n=2) -``` - -We have found when a programing notation is irregular it discourages exploration and leads to learned helplessness (rote appeals to authority and manuals instead of rapid acquisition of patterns and skills). ## Conclusion The `seplyr` methodology is simple, easy to teach, and powerful. -The methodologies we discussed differ in philosophy. +There are alternatives that differ in philosophy. * `rlang`/tidyeval is often used to build new non-standard interfaces on top of existing non-standard interfaces. This is needed because non-standard interfaces do not naturally compose. The tidy/non-tidy analogy is: it works around a mess by introducing a new mess. * `wrapr::let()` converts non-standard interfaces into standard [referentially transparent](https://en.wikipedia.org/wiki/Referential_transparency) or value-oriented interfaces. It tries to help clean up messes.