From 24ec872f64adf9e36a854f60e33602b773ff2a8f Mon Sep 17 00:00:00 2001 From: Alexandre Courtiol Date: Thu, 26 Oct 2023 13:50:55 +0200 Subject: [PATCH] Refresh --- .github/pics/README-bench_run1-1.png | Bin 23276 -> 23084 bytes README.md | 237 ++++++++++++++------------- man/lay.Rd | 58 ++++--- 3 files changed, 162 insertions(+), 133 deletions(-) diff --git a/.github/pics/README-bench_run1-1.png b/.github/pics/README-bench_run1-1.png index dfacef4c4a832c1c9da8c203aa64d451a5845e2a..f3363170f6faaca12458994d271cbc6c74919948 100644 GIT binary patch literal 23084 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 Xh&@`Ge)Ta41RMxujmLPH`Ro4zuVJQ} literal 23276 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

dP(p1ARl}O$QvnC;$DY8*R}%sh77dvjSKr0 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 diff --git a/README.md b/README.md index 8109ae2..3c2a96e 100644 --- a/README.md +++ b/README.md @@ -9,13 +9,12 @@ experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://www.tidyverse.org/lifecycle/#experimental) [![CRAN status](https://www.r-pkg.org/badges/version/lay)](https://CRAN.R-project.org/package=lay) -[![R build -status](https://github.com/courtiol/lay/workflows/R-CMD-check/badge.svg)](https://github.com/courtiol/lay) +[![R-CMD-check](https://github.com/courtiol/lay/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/courtiol/lay/actions/workflows/R-CMD-check.yaml) ## Why **{lay}**? -Doing rowwise operation is notoriously awkward in R. +Doing rowwise operations are notoriously awkward in R. Many options have been proposed, but they tend to be complicated, inefficient, or both. @@ -48,7 +47,7 @@ of pain relievers for non medical purpose. ``` r library(lay) ## requires to have installed {lay} drugs -#> # A tibble: 100 x 8 +#> # A tibble: 100 × 8 #> caseid hydrocd oxycodp codeine tramadl morphin methdon vicolor #> #> 1 1 0 0 0 0 0 0 0 @@ -61,7 +60,7 @@ drugs #> 8 8 0 0 0 0 0 0 0 #> 9 9 0 0 0 0 0 0 1 #> 10 10 0 0 0 0 0 0 0 -#> # … with 90 more rows +#> # ℹ 90 more rows ``` The dataset is [tidy](https://vita.had.co.nz/papers/tidy-data.pdf): each @@ -79,9 +78,9 @@ This is how you would achieve our goal using **lay()**: ``` r library(dplyr, warn.conflicts = FALSE) ## requires to have installed {dplyr} -drugs_full %>% - mutate(everused = lay(across(-caseid), any)) -#> # A tibble: 55,271 x 9 +drugs_full |> + mutate(everused = lay(pick(-caseid), any)) +#> # A tibble: 55,271 × 9 #> caseid hydrocd oxycodp codeine tramadl morphin methdon vicolor everused #> #> 1 1 0 0 0 0 0 0 0 FALSE @@ -94,17 +93,17 @@ drugs_full %>% #> 8 8 0 0 0 0 0 0 0 FALSE #> 9 9 0 0 0 0 0 0 1 TRUE #> 10 10 0 0 0 0 0 0 0 FALSE -#> # … with 55,261 more rows +#> # ℹ 55,261 more rows ``` We used **mutate()** from **{dplyr}** to create a new column called -*everused*, and we use **across()** from that same package to remove the +*everused*, and we used **pick()** from that same package to remove the column *caseid* when laying down each row of the data and applying the function **any()**. When combining **lay()** and **{dplyr}**, you should always use -**across()**. The function **across()** lets you pick among many -[selection +**pick()** or **across()**. The functions **pick()** and **across()** +let you pick among many [selection helpers](https://tidyselect.r-lib.org/reference/language.html) from the package **{tidyselect}**, which makes it easy to specify which columns to consider. @@ -116,18 +115,18 @@ argument(s) of the function you wish to apply rowwise (here **any()**): drugs_with_NA <- drugs ## create a copy of the dataset drugs_with_NA[1, 2] <- NA ## introduce a missing value -drugs_with_NA %>% - mutate(everused = lay(across(-caseid), any)) %>% ## without additional argument +drugs_with_NA |> + mutate(everused = lay(pick(-caseid), any)) |> ## without additional argument slice(1) ## keep first row only -#> # A tibble: 1 x 9 +#> # A tibble: 1 × 9 #> caseid hydrocd oxycodp codeine tramadl morphin methdon vicolor everused #> #> 1 1 NA 0 0 0 0 0 0 NA -drugs_with_NA %>% - mutate(everused = lay(across(-caseid), any, na.rm = TRUE)) %>% ## with additional argument +drugs_with_NA |> + mutate(everused = lay(pick(-caseid), any, na.rm = TRUE)) |> ## with additional argument slice(1) -#> # A tibble: 1 x 9 +#> # A tibble: 1 × 9 #> caseid hydrocd oxycodp codeine tramadl morphin methdon vicolor everused #> #> 1 1 NA 0 0 0 0 0 0 FALSE @@ -139,9 +138,9 @@ Since one of the backbones of **lay()** is define anonymous functions on the fly: ``` r -drugs_with_NA %>% - mutate(everused = lay(across(-caseid), ~ any(.x, na.rm = TRUE))) ## same as above, different syntax -#> # A tibble: 100 x 9 +drugs_with_NA |> + mutate(everused = lay(pick(-caseid), ~ any(.x, na.rm = TRUE))) ## same as above, different syntax +#> # A tibble: 100 × 9 #> caseid hydrocd oxycodp codeine tramadl morphin methdon vicolor everused #> #> 1 1 NA 0 0 0 0 0 0 FALSE @@ -154,7 +153,7 @@ drugs_with_NA %>% #> 8 8 0 0 0 0 0 0 0 FALSE #> 9 9 0 0 0 0 0 0 1 TRUE #> 10 10 0 0 0 0 0 0 0 FALSE -#> # … with 90 more rows +#> # ℹ 90 more rows ``` We can also apply many functions at once, as exemplified with another @@ -163,27 +162,27 @@ dataset: ``` r data("world_bank_pop", package = "tidyr") ## requires to have installed {tidyr} -world_bank_pop %>% - filter(indicator == "SP.POP.TOTL") %>% - mutate(lay(across(matches("\\d")), +world_bank_pop |> + filter(indicator == "SP.POP.TOTL") |> + mutate(lay(pick(matches("\\d")), ~ tibble(min = min(.x), mean = mean(.x), max = max(.x))), .after = indicator) -#> # A tibble: 264 x 23 -#> country indicator min mean max `2000` `2001` `2002` `2003` `2004` -#> -#> 1 ABW SP.POP.T… 9.09e4 1.00e5 1.05e5 9.09e4 9.29e4 9.50e4 9.70e4 9.87e4 -#> 2 AFG SP.POP.T… 2.01e7 2.78e7 3.55e7 2.01e7 2.10e7 2.20e7 2.31e7 2.41e7 -#> 3 AGO SP.POP.T… 1.64e7 2.25e7 2.98e7 1.64e7 1.70e7 1.76e7 1.82e7 1.89e7 -#> 4 ALB SP.POP.T… 2.87e6 2.96e6 3.09e6 3.09e6 3.06e6 3.05e6 3.04e6 3.03e6 -#> 5 AND SP.POP.T… 6.54e4 7.81e4 8.45e4 6.54e4 6.73e4 7.00e4 7.32e4 7.62e4 -#> 6 ARB SP.POP.T… 2.84e8 3.46e8 4.14e8 2.84e8 2.90e8 2.96e8 3.02e8 3.09e8 -#> 7 ARE SP.POP.T… 3.15e6 6.67e6 9.40e6 3.15e6 3.33e6 3.51e6 3.74e6 4.09e6 -#> 8 ARG SP.POP.T… 3.71e7 4.06e7 4.43e7 3.71e7 3.75e7 3.79e7 3.83e7 3.87e7 -#> 9 ARM SP.POP.T… 2.88e6 2.95e6 3.07e6 3.07e6 3.05e6 3.03e6 3.02e6 3.00e6 -#> 10 ASM SP.POP.T… 5.52e4 5.70e4 5.93e4 5.75e4 5.82e4 5.87e4 5.91e4 5.93e4 -#> # … with 254 more rows, and 13 more variables: `2005` , `2006` , -#> # `2007` , `2008` , `2009` , `2010` , `2011` , -#> # `2012` , `2013` , `2014` , `2015` , `2016` , -#> # `2017` +#> # A tibble: 266 × 23 +#> country indicator min mean max `2000` `2001` `2002` `2003` `2004` +#> +#> 1 ABW SP.POP.TOTL 8.91e4 9.81e4 1.05e5 8.91e4 9.07e4 9.18e4 9.27e4 9.35e4 +#> 2 AFE SP.POP.TOTL 4.02e8 5.08e8 6.33e8 4.02e8 4.12e8 4.23e8 4.34e8 4.45e8 +#> 3 AFG SP.POP.TOTL 1.95e7 2.73e7 3.56e7 1.95e7 1.97e7 2.10e7 2.26e7 2.36e7 +#> 4 AFW SP.POP.TOTL 2.70e8 3.45e8 4.31e8 2.70e8 2.77e8 2.85e8 2.93e8 3.01e8 +#> 5 AGO SP.POP.TOTL 1.64e7 2.26e7 3.02e7 1.64e7 1.69e7 1.75e7 1.81e7 1.88e7 +#> 6 ALB SP.POP.TOTL 2.87e6 2.96e6 3.09e6 3.09e6 3.06e6 3.05e6 3.04e6 3.03e6 +#> 7 AND SP.POP.TOTL 6.61e4 7.32e4 8.02e4 6.61e4 6.78e4 7.08e4 7.39e4 7.69e4 +#> 8 ARB SP.POP.TOTL 2.87e8 3.52e8 4.24e8 2.87e8 2.94e8 3.00e8 3.07e8 3.13e8 +#> 9 ARE SP.POP.TOTL 3.28e6 6.58e6 9.07e6 3.28e6 3.45e6 3.63e6 3.81e6 3.99e6 +#> 10 ARG SP.POP.TOTL 3.71e7 4.05e7 4.40e7 3.71e7 3.75e7 3.79e7 3.83e7 3.87e7 +#> # ℹ 256 more rows +#> # ℹ 13 more variables: `2005` , `2006` , `2007` , `2008` , +#> # `2009` , `2010` , `2011` , `2012` , `2013` , +#> # `2014` , `2015` , `2016` , `2017` ``` Since the other backbone of **lay()** is @@ -196,32 +195,33 @@ column). This is why, in the last chunk of code, three different columns rowwise job must output a scalar (vector of length 1), or a tibble or data frame with a single row. -We can apply a function that returns a vector of length > 1 by -turning such vector into a tibble using **as\_tibble\_row()** from +We can apply a function that returns a vector of length \> 1 by turning +such vector into a tibble using **as_tibble_row()** from [**{tibble}**](https://tibble.tidyverse.org/): ``` r -world_bank_pop %>% - filter(indicator == "SP.POP.TOTL") %>% - mutate(lay(across(matches("\\d")), +world_bank_pop |> + filter(indicator == "SP.POP.TOTL") |> + mutate(lay(pick(matches("\\d")), ~ as_tibble_row(quantile(.x, na.rm = TRUE))), .after = indicator) -#> # A tibble: 264 x 25 -#> country indicator `0%` `25%` `50%` `75%` `100%` `2000` `2001` `2002` -#> -#> 1 ABW SP.POP.T… 9.09e4 9.91e4 1.01e5 1.03e5 1.05e5 9.09e4 9.29e4 9.50e4 -#> 2 AFG SP.POP.T… 2.01e7 2.44e7 2.76e7 3.15e7 3.55e7 2.01e7 2.10e7 2.20e7 -#> 3 AGO SP.POP.T… 1.64e7 1.90e7 2.22e7 2.58e7 2.98e7 1.64e7 1.70e7 1.76e7 -#> 4 ALB SP.POP.T… 2.87e6 2.90e6 2.94e6 3.02e6 3.09e6 3.09e6 3.06e6 3.05e6 -#> 5 AND SP.POP.T… 6.54e4 7.64e4 7.90e4 8.26e4 8.45e4 6.54e4 6.73e4 7.00e4 -#> 6 ARB SP.POP.T… 2.84e8 3.11e8 3.44e8 3.80e8 4.14e8 2.84e8 2.90e8 2.96e8 -#> 7 ARE SP.POP.T… 3.15e6 4.21e6 7.28e6 8.98e6 9.40e6 3.15e6 3.33e6 3.51e6 -#> 8 ARG SP.POP.T… 3.71e7 3.88e7 4.06e7 4.24e7 4.43e7 3.71e7 3.75e7 3.79e7 -#> 9 ARM SP.POP.T… 2.88e6 2.90e6 2.93e6 3.00e6 3.07e6 3.07e6 3.05e6 3.03e6 -#> 10 ASM SP.POP.T… 5.52e4 5.56e4 5.66e4 5.85e4 5.93e4 5.75e4 5.82e4 5.87e4 -#> # … with 254 more rows, and 15 more variables: `2003` , `2004` , -#> # `2005` , `2006` , `2007` , `2008` , `2009` , -#> # `2010` , `2011` , `2012` , `2013` , `2014` , -#> # `2015` , `2016` , `2017` +#> # A tibble: 266 × 25 +#> country indicator `0%` `25%` `50%` `75%` `100%` `2000` `2001` `2002` +#> +#> 1 ABW SP.POP.TOTL 8.91e4 9.38e4 9.86e4 1.03e5 1.05e5 8.91e4 9.07e4 9.18e4 +#> 2 AFE SP.POP.TOTL 4.02e8 4.48e8 5.03e8 5.64e8 6.33e8 4.02e8 4.12e8 4.23e8 +#> 3 AFG SP.POP.TOTL 1.95e7 2.38e7 2.69e7 3.13e7 3.56e7 1.95e7 1.97e7 2.10e7 +#> 4 AFW SP.POP.TOTL 2.70e8 3.03e8 3.42e8 3.85e8 4.31e8 2.70e8 2.77e8 2.85e8 +#> 5 AGO SP.POP.TOTL 1.64e7 1.89e7 2.21e7 2.59e7 3.02e7 1.64e7 1.69e7 1.75e7 +#> 6 ALB SP.POP.TOTL 2.87e6 2.90e6 2.94e6 3.02e6 3.09e6 3.09e6 3.06e6 3.05e6 +#> 7 AND SP.POP.TOTL 6.61e4 7.11e4 7.21e4 7.55e4 8.02e4 6.61e4 6.78e4 7.08e4 +#> 8 ARB SP.POP.TOTL 2.87e8 3.15e8 3.51e8 3.87e8 4.24e8 2.87e8 2.94e8 3.00e8 +#> 9 ARE SP.POP.TOTL 3.28e6 4.07e6 7.49e6 8.73e6 9.07e6 3.28e6 3.45e6 3.63e6 +#> 10 ARG SP.POP.TOTL 3.71e7 3.88e7 4.05e7 4.21e7 4.40e7 3.71e7 3.75e7 3.79e7 +#> # ℹ 256 more rows +#> # ℹ 15 more variables: `2003` , `2004` , `2005` , `2006` , +#> # `2007` , `2008` , `2009` , `2010` , `2011` , +#> # `2012` , `2013` , `2014` , `2015` , `2016` , +#> # `2017` ``` ## Alternatives to **lay()** @@ -236,9 +236,9 @@ example about drugs usage. One solution is to simply do the following: ``` r -drugs_full %>% +drugs_full |> mutate(everused = codeine | hydrocd | methdon | morphin | oxycodp | tramadl | vicolor) -#> # A tibble: 55,271 x 9 +#> # A tibble: 55,271 × 9 #> caseid hydrocd oxycodp codeine tramadl morphin methdon vicolor everused #> #> 1 1 0 0 0 0 0 0 0 FALSE @@ -251,25 +251,25 @@ drugs_full %>% #> 8 8 0 0 0 0 0 0 0 FALSE #> 9 9 0 0 0 0 0 0 1 TRUE #> 10 10 0 0 0 0 0 0 0 FALSE -#> # … with 55,261 more rows +#> # ℹ 55,261 more rows ``` It is certainly very efficient from a computational point of view, but coding this way presents two main limitations: -- you need to name all columns explicitly, which can be problematic - when dealing with many columns -- you are stuck with expressing your task with logical and arithmetic - operators, which is not always sufficient +- you need to name all columns explicitly, which can be problematic when + dealing with many columns +- you are stuck with expressing your task with logical and arithmetic + operators, which is not always sufficient ### Alternative 2: 100% **{dplyr}** ``` r -drugs%>% - rowwise() %>% - mutate(everused = any(c_across(-caseid))) %>% +drugs |> + rowwise() |> + mutate(everused = any(c_across(-caseid))) |> ungroup() -#> # A tibble: 100 x 9 +#> # A tibble: 100 × 9 #> caseid hydrocd oxycodp codeine tramadl morphin methdon vicolor everused #> #> 1 1 0 0 0 0 0 0 0 FALSE @@ -282,10 +282,10 @@ drugs%>% #> 8 8 0 0 0 0 0 0 0 FALSE #> 9 9 0 0 0 0 0 0 1 TRUE #> 10 10 0 0 0 0 0 0 0 FALSE -#> # … with 90 more rows +#> # ℹ 90 more rows ``` -It is easy to use as **c\_across()** turns its input into a vector and +It is easy to use as **c_across()** turns its input into a vector and **rowwise()** implies that the vector only represents one row at a time. Yet, for now it remains quite slow on large datasets (see **Efficiency** below). @@ -295,14 +295,14 @@ below). ``` r library(tidyr) ## requires to have installed {tidyr} -drugs %>% - pivot_longer(-caseid) %>% - group_by(caseid) %>% - mutate(everused = any(value)) %>% - ungroup() %>% - pivot_wider() %>% +drugs |> + pivot_longer(-caseid) |> + group_by(caseid) |> + mutate(everused = any(value)) |> + ungroup() |> + pivot_wider() |> relocate(everused, .after = last_col()) -#> # A tibble: 100 x 9 +#> # A tibble: 100 × 9 #> caseid hydrocd oxycodp codeine tramadl morphin methdon vicolor everused #> #> 1 1 0 0 0 0 0 0 0 FALSE @@ -315,7 +315,7 @@ drugs %>% #> 8 8 0 0 0 0 0 0 0 FALSE #> 9 9 0 0 0 0 0 0 1 TRUE #> 10 10 0 0 0 0 0 0 0 FALSE -#> # … with 90 more rows +#> # ℹ 90 more rows ``` Here the trick is to turn the rowwise problem into a column problem by @@ -329,9 +329,9 @@ time and memory required to pivot the tables. ``` r library(purrr) ## requires to have installed {purrr} -drugs %>% - mutate(everused = pmap_lgl(across(-caseid), ~ any(...))) -#> # A tibble: 100 x 9 +drugs |> + mutate(everused = pmap_lgl(pick(-caseid), ~ any(...))) +#> # A tibble: 100 × 9 #> caseid hydrocd oxycodp codeine tramadl morphin methdon vicolor everused #> #> 1 1 0 0 0 0 0 0 0 FALSE @@ -344,7 +344,7 @@ drugs %>% #> 8 8 0 0 0 0 0 0 0 FALSE #> 9 9 0 0 0 0 0 0 1 TRUE #> 10 10 0 0 0 0 0 0 0 FALSE -#> # … with 90 more rows +#> # ℹ 90 more rows ``` This is a perfectly fine solution and actually part of what one @@ -356,9 +356,9 @@ a user perspective it is a little too geeky-scary. ``` r library(slider) ## requires to have installed {slider} -drugs %>% - mutate(everused = slide_vec(across(-caseid), any)) -#> # A tibble: 100 x 9 +drugs |> + mutate(everused = slide_vec(pick(-caseid), any)) +#> # A tibble: 100 × 9 #> caseid hydrocd oxycodp codeine tramadl morphin methdon vicolor everused #> #> 1 1 0 0 0 0 0 0 0 FALSE @@ -371,7 +371,7 @@ drugs %>% #> 8 8 0 0 0 0 0 0 0 FALSE #> 9 9 0 0 0 0 0 0 1 TRUE #> 10 10 0 0 0 0 0 0 0 FALSE -#> # … with 90 more rows +#> # ℹ 90 more rows ``` The package [**{slider}**](https://davisvaughan.github.io/slider/) is a @@ -396,7 +396,23 @@ library(data.table) ## requires to have installed {data.table} drugs_dt <- data.table(drugs) drugs_dt[, ..I := .I] - drugs_dt[, everused := any(.SD), by = ..I, .SDcols = -"caseid"] +drugs_dt[, everused := any(.SD), by = ..I, .SDcols = -"caseid"] +drugs_dt[, ..I := NULL] +as_tibble(drugs_dt) +#> # A tibble: 100 × 9 +#> caseid hydrocd oxycodp codeine tramadl morphin methdon vicolor everused +#> +#> 1 1 0 0 0 0 0 0 0 FALSE +#> 2 2 0 0 0 0 0 0 0 FALSE +#> 3 3 0 0 0 0 0 0 0 FALSE +#> 4 4 0 0 0 0 0 0 0 FALSE +#> 5 5 0 0 0 0 0 0 0 FALSE +#> 6 6 0 0 0 0 0 0 0 FALSE +#> 7 7 0 0 0 0 0 0 0 FALSE +#> 8 8 0 0 0 0 0 0 0 FALSE +#> 9 9 0 0 0 0 0 0 1 TRUE +#> 10 10 0 0 0 0 0 0 0 FALSE +#> # ℹ 90 more rows ``` This is a solution for those using **{data.table}**. It is not @@ -406,9 +422,9 @@ do not program frequently using **{data.table}**. ### Alternative 7: **apply()** ``` r -drugs %>% - mutate(everused = apply(across(-caseid), 1L, any)) -#> # A tibble: 100 x 9 +drugs |> + mutate(everused = apply(pick(-caseid), 1L, any)) +#> # A tibble: 100 × 9 #> caseid hydrocd oxycodp codeine tramadl morphin methdon vicolor everused #> #> 1 1 0 0 0 0 0 0 0 FALSE @@ -421,7 +437,7 @@ drugs %>% #> 8 8 0 0 0 0 0 0 0 FALSE #> 9 9 0 0 0 0 0 0 1 TRUE #> 10 10 0 0 0 0 0 0 0 FALSE -#> # … with 90 more rows +#> # ℹ 90 more rows ``` This is the base R solution. Very efficient and actually part of the @@ -441,7 +457,7 @@ for (i in seq_len(nrow(drugs))) { } drugs -#> # A tibble: 100 x 9 +#> # A tibble: 100 × 9 #> caseid hydrocd oxycodp codeine tramadl morphin methdon vicolor everused #> #> 1 1 0 0 0 0 0 0 0 FALSE @@ -454,7 +470,7 @@ drugs #> 8 8 0 0 0 0 0 0 0 FALSE #> 9 9 0 0 0 0 0 0 1 TRUE #> 10 10 0 0 0 0 0 0 0 FALSE -#> # … with 90 more rows +#> # ℹ 90 more rows ``` This is another base R solution, which does not involve any external @@ -463,7 +479,7 @@ package. It is not very pretty, nor particularly efficient. ### Other alternatives? There are probably other ways. If you think of a nice one, please leave -an issue! +an issue and we will add it here! ## Efficiency @@ -483,18 +499,19 @@ quite efficient! lay_history -This package has been created by **@romainfrancois** as a reply to a -tweet I posted under **@rdataberlin** in February 2020. At the time I -was exploring different ways to perform rowwise jobs in R and I was -experimenting with various ideas on how to exploit the fact that the -newly introduced function **across()** from **{dplyr}** creates tibbles -on which on can easily apply a function. Romain came up with **lay()** -as the better solution making good use of **{rlang}** & **{vctrs}**. +The first draft of this package has been created by **@romainfrancois** +as a reply to a tweet I posted under **@rdataberlin** in February 2020. +At the time I was exploring different ways to perform rowwise jobs in R +and I was experimenting with various ideas on how to exploit the fact +that the newly introduced function **across()** from **{dplyr}** creates +tibbles on which on can easily apply a function. Romain came up with +**lay()** as the better solution making good use of **{rlang}** & +**{vctrs}**. The verb **lay()** never made it to be integrated within **{dplyr}** and, so far, I still find **lay()** superior than most alternatives, -which is why I decided to revive this package in November 2020. +which is why I decided to maintain this package. In short, I deserve little credit and instead you should feel free to buy Romain a coffee [here](https://ko-fi.com/romain) or to sponsor his -[github profile](https://github.com/romainfrancois) as I do. +[github profile](https://github.com/romainfrancois). diff --git a/man/lay.Rd b/man/lay.Rd index 82187e8..1fba62a 100644 --- a/man/lay.Rd +++ b/man/lay.Rd @@ -4,7 +4,7 @@ \alias{lay} \title{Apply a function within each row.} \usage{ -lay(.data, .fn, ..., .method = "apply") +lay(.data, .fn, ..., .method = c("apply", "tidy")) } \arguments{ \item{.data}{A data frame or tibble (or other data frame extensions).} @@ -13,6 +13,8 @@ lay(.data, .fn, ..., .method = "apply") Possible values are: \itemize{ \item A function, e.g. \code{mean} +\item An anonymous function, .e.g. \code{function(x) mean(x, na.rm = TRUE)} +\item An anonymous function with shorthand, .e.g. \verb{\\(x) mean(x, na.rm = TRUE)} \item A purrr-style lambda, e.g. \code{~ mean(.x, na.rm = TRUE)} (wrap the output in a data frame to apply several functions at once, e.g. @@ -25,7 +27,7 @@ Possible values are: is used to apply the rowwise job: \itemize{ \item "apply", the default internally uses the function \code{\link[=apply]{apply()}}. -\item "tidy", internally uses \code{\link[purrr:map2]{purrr::pmap()}} and is stricter with respect to class coercion +\item "tidy", internally uses \code{\link[purrr:pmap]{purrr::pmap()}} and is stricter with respect to class coercion across columns. } @@ -53,12 +55,12 @@ The function \code{lay()} should work in a wide range of situations, provided th \item The input \code{.data} should be a data frame (including tibble) with columns of same class, or of classes similar enough to be easily coerced into a single class. Note that \code{.method = "apply"} also allows for the input to be a matrix and is more permissive in terms of data coercion. -\item The output of \code{.fn} should be a scalar (i.e. vector of length 1) or a 1 row data frame (or +\item The output of \code{.fn} should be a scalar (i.e., vector of length 1) or a 1 row data frame (or tibble). } If you use \code{lay()} within \code{\link[dplyr:mutate]{dplyr::mutate()}}, make sure that the data used by \code{\link[dplyr:mutate]{dplyr::mutate()}} -contain no row-grouping, i.e what is passed to \code{.data} in \code{\link[dplyr:mutate]{dplyr::mutate()}} should not be of +contain no row-grouping, i.e., what is passed to \code{.data} in \code{\link[dplyr:mutate]{dplyr::mutate()}} should not be of class \code{grouped_df} or \code{rowwise_df}. If it is, \code{lay()} will be called multiple times, which will slow down the computation despite not influencing the output. } @@ -69,7 +71,12 @@ slow down the computation despite not influencing the output. # lay can return a vector lay(drugs[1:10, -1], any) -# lay can return a data frame (note the use of the rlang lambda syntax ~ fn(.x)) +# lay can return a data frame +## using the shorthand function syntax \(x) .fn(x) +lay(drugs[1:10, -1], + \(x) data.frame(drugs_taken = sum(x), drugs_not_taken = sum(x == 0))) + +## using the rlang lambda syntax ~ fn(.x) lay(drugs[1:10, -1], ~ data.frame(drugs_taken = sum(.x), drugs_not_taken = sum(.x == 0))) @@ -84,45 +91,50 @@ cbind(drugs[1:10, ], if (require("dplyr")) { # apply any() to each row - drugs \%>\% - mutate(everused = lay(across(-caseid), any)) + drugs |> + mutate(everused = lay(pick(-caseid), any)) + + # apply any() to each row using all columns + drugs |> + select(-caseid) |> + mutate(everused = lay(pick(everything()), any)) # a workaround would be to use `rowSums` - drugs \%>\% - mutate(everused = rowSums(across(-caseid)) > 0) + drugs |> + mutate(everused = rowSums(pick(-caseid)) > 0) # but we can lay any function taking a vector as input, e.g. median - drugs \%>\% - mutate(used_median = lay(across(-caseid), median)) + drugs |> + mutate(used_median = lay(pick(-caseid), median)) # you can pass arguments to the function drugs_with_NA <- drugs drugs_with_NA[1, 2] <- NA - drugs_with_NA \%>\% - mutate(everused = lay(across(-caseid), any)) - drugs_with_NA \%>\% - mutate(everused = lay(across(-caseid), any, na.rm = TRUE)) + drugs_with_NA |> + mutate(everused = lay(pick(-caseid), any)) + drugs_with_NA |> + mutate(everused = lay(pick(-caseid), any, na.rm = TRUE)) # you can lay the output into a 1-row tibble (or data.frame) # if you want to apply multiple functions - drugs \%>\% - mutate(lay(across(-caseid), + drugs |> + mutate(lay(pick(-caseid), ~ tibble(drugs_taken = sum(.x), drugs_not_taken = sum(.x == 0)))) # note that naming the output prevent the automatic splicing and you obtain a df-column - drugs \%>\% - mutate(usage = lay(across(-caseid), + drugs |> + mutate(usage = lay(pick(-caseid), ~ tibble(drugs_taken = sum(.x), drugs_not_taken = sum(.x == 0)))) # if your function returns a vector longer than a scalar, you should turn the output # into a tibble, which is the job of as_tibble_row() - drugs \%>\% - mutate(lay(across(-caseid), ~ as_tibble_row(quantile(.x)))) + drugs |> + mutate(lay(pick(-caseid), ~ as_tibble_row(quantile(.x)))) # note that you could also wrap the output in a list and name it to obtain a list-column - drugs \%>\% - mutate(usage_quantiles = lay(across(-caseid), ~ list(quantile(.x)))) + drugs |> + mutate(usage_quantiles = lay(pick(-caseid), ~ list(quantile(.x)))) } }