Home > OS >  Adding functions to a pipeline based on an argument
Adding functions to a pipeline based on an argument

Time:11-29

I am trying to write a function that calls another function multiple times in its body. I am hoping to control the number of such function calls and their respective target by using an argument, but this becomes tricky due to the structure of pipelines. Imagine this simple example of mutating columns. I am fully aware that this is not the greatest example as you wouldn't call mutate multiple times for different targets, but bear with me. This is just a standin example, so it is important that each mutate call corresponds to a string supplied via the cols argument.

library(dplyr)

scale_cols <- function(data, cols = c("mpg", "cyl")) {
  
  processed_data <- data |> 
    mutate("mpg" = scale(mpg)) |> 
    mutate("cyl" = scale(cyl))
  
  return(processed_data)
}

scale_cols(mtcars)
#>                             mpg        cyl  disp  hp drat    wt  qsec vs am
#> Mazda RX4            0.15088482 -0.1049878 160.0 110 3.90 2.620 16.46  0  1
#> Mazda RX4 Wag        0.15088482 -0.1049878 160.0 110 3.90 2.875 17.02  0  1
#> Datsun 710           0.44954345 -1.2248578 108.0  93 3.85 2.320 18.61  1  1
#> Hornet 4 Drive       0.21725341 -0.1049878 258.0 110 3.08 3.215 19.44  1  0
#> Hornet Sportabout   -0.23073453  1.0148821 360.0 175 3.15 3.440 17.02  0  0
#> Valiant             -0.33028740 -0.1049878 225.0 105 2.76 3.460 20.22  1  0
#> Duster 360          -0.96078893  1.0148821 360.0 245 3.21 3.570 15.84  0  0
#> Merc 240D            0.71501778 -1.2248578 146.7  62 3.69 3.190 20.00  1  0
#> Merc 230             0.44954345 -1.2248578 140.8  95 3.92 3.150 22.90  1  0

Created on 2022-11-28 with reprex v2.0.2

Currently which columns are to be transformed is hardcoded, but I would prefer being able to choose the columnsfor transformation by using the cols argument. Is it possible to map or apply the mutate function over the cols elements so that in the end a fully functional pipeline is created? Thank you for your time.

CodePudding user response:

It sounds to me as though you want to iteratively apply a function once for each element of a passed vector, and for the result to include the changes cumulatively. The non-standard evaluation involved in mutate, as well as there being better ways to carry out this particular operation probably makes it a suboptimal example.

However, the basic concept of what you are trying to do is performed by the base R function Reduce, which will take a starting argument (e.g. the input data frame), and iteratively apply a function of two variables (the output of the last iteration and the next member of the cols vector)

scale_cols <- function(data, cols = c("mpg", "cyl")) {
  
  Reduce(function(x, y) { 
    x[[y]] <- scale(x[[y]]) 
    return(x)
    }, x = cols, init = data)
}

Resulting in:

scale_cols(mtcars)
#>                             mpg        cyl  disp  hp drat    wt  qsec vs am
#> Mazda RX4            0.15088482 -0.1049878 160.0 110 3.90 2.620 16.46  0  1
#> Mazda RX4 Wag        0.15088482 -0.1049878 160.0 110 3.90 2.875 17.02  0  1
#> Datsun 710           0.44954345 -1.2248578 108.0  93 3.85 2.320 18.61  1  1
#> Hornet 4 Drive       0.21725341 -0.1049878 258.0 110 3.08 3.215 19.44  1  0
#> Hornet Sportabout   -0.23073453  1.0148821 360.0 175 3.15 3.440 17.02  0  0
#> Valiant             -0.33028740 -0.1049878 225.0 105 2.76 3.460 20.22  1  0
#> Duster 360          -0.96078893  1.0148821 360.0 245 3.21 3.570 15.84  0  0
#> Merc 240D            0.71501778 -1.2248578 146.7  62 3.69 3.190 20.00  1  0
#> Merc 230             0.44954345 -1.2248578 140.8  95 3.92 3.150 22.90  1  0
#> Merc 280            -0.14777380 -0.1049878 167.6 123 3.92 3.440 18.30  1  0
#> Merc 280C           -0.38006384 -0.1049878 167.6 123 3.92 3.440 18.90  1  0
#> Merc 450SE          -0.61235388  1.0148821 275.8 180 3.07 4.070 17.40  0  0
#> Merc 450SL          -0.46302456  1.0148821 275.8 180 3.07 3.730 17.60  0  0
#> Merc 450SLC         -0.81145962  1.0148821 275.8 180 3.07 3.780 18.00  0  0
#> Cadillac Fleetwood  -1.60788262  1.0148821 472.0 205 2.93 5.250 17.98  0  0
#> Lincoln Continental -1.60788262  1.0148821 460.0 215 3.00 5.424 17.82  0  0
#> Chrysler Imperial   -0.89442035  1.0148821 440.0 230 3.23 5.345 17.42  0  0
#> Fiat 128             2.04238943 -1.2248578  78.7  66 4.08 2.200 19.47  1  1
#> Honda Civic          1.71054652 -1.2248578  75.7  52 4.93 1.615 18.52  1  1
#> Toyota Corolla       2.29127162 -1.2248578  71.1  65 4.22 1.835 19.90  1  1
#> Toyota Corona        0.23384555 -1.2248578 120.1  97 3.70 2.465 20.01  1  0
#> Dodge Challenger    -0.76168319  1.0148821 318.0 150 2.76 3.520 16.87  0  0
#> AMC Javelin         -0.81145962  1.0148821 304.0 150 3.15 3.435 17.30  0  0
#> Camaro Z28          -1.12671039  1.0148821 350.0 245 3.73 3.840 15.41  0  0
#> Pontiac Firebird    -0.14777380  1.0148821 400.0 175 3.08 3.845 17.05  0  0
#> Fiat X1-9            1.19619000 -1.2248578  79.0  66 4.08 1.935 18.90  1  1
#> Porsche 914-2        0.98049211 -1.2248578 120.3  91 4.43 2.140 16.70  0  1
#> Lotus Europa         1.71054652 -1.2248578  95.1 113 3.77 1.513 16.90  1  1
#> Ford Pantera L      -0.71190675  1.0148821 351.0 264 4.22 3.170 14.50  0  1
#> Ferrari Dino        -0.06481307 -0.1049878 145.0 175 3.62 2.770 15.50  0  1
#> Maserati Bora       -0.84464392  1.0148821 301.0 335 3.54 3.570 14.60  0  1
#> Volvo 142E           0.21725341 -1.2248578 121.0 109 4.11 2.780 18.60  1  1
#>                     gear carb
#> Mazda RX4              4    4
#> Mazda RX4 Wag          4    4
#> Datsun 710             4    1
#> Hornet 4 Drive         3    1
#> Hornet Sportabout      3    2
#> Valiant                3    1
#> Duster 360             3    4
#> Merc 240D              4    2
#> Merc 230               4    2
#> Merc 280               4    4
#> Merc 280C              4    4
#> Merc 450SE             3    3
#> Merc 450SL             3    3
#> Merc 450SLC            3    3
#> Cadillac Fleetwood     3    4
#> Lincoln Continental    3    4
#> Chrysler Imperial      3    4
#> Fiat 128               4    1
#> Honda Civic            4    2
#> Toyota Corolla         4    1
#> Toyota Corona          3    1
#> Dodge Challenger       3    2
#> AMC Javelin            3    2
#> Camaro Z28             3    4
#> Pontiac Firebird       3    2
#> Fiat X1-9              4    1
#> Porsche 914-2          5    2
#> Lotus Europa           5    2
#> Ford Pantera L         5    4
#> Ferrari Dino           5    6
#> Maserati Bora          5    8
#> Volvo 142E             4    2

Created on 2022-11-28 with reprex v2.0.2

CodePudding user response:

I agree with @GregorThomas that across is the obvious solution to your question as posted and with @MrFlick that you should modify your post to make your intent clear. That said, and based on your comments, I believe this gives you what you want (and is more in keeping with the tidyverse's style than your example).

scale_cols <- function(data, cols) {
  for (c in cols) {
    data <- data %>% mutate({{c}} := scale({{c}}))
  }
  data
}

mtcars %>% scale_cols(vars(mpg, cyl))
                            mpg        cyl  disp  hp drat    wt  qsec vs am gear carb
Mazda RX4            0.15088482 -0.1049878 160.0 110 3.90 2.620 16.46  0  1    4    4
Mazda RX4 Wag        0.15088482 -0.1049878 160.0 110 3.90 2.875 17.02  0  1    4    4
Datsun 710           0.44954345 -1.2248578 108.0  93 3.85 2.320 18.61  1  1    4    1
Hornet 4 Drive       0.21725341 -0.1049878 258.0 110 3.08 3.215 19.44  1  0    3    1
Hornet Sportabout   -0.23073453  1.0148821 360.0 175 3.15 3.440 17.02  0  0    3    2
Valiant             -0.33028740 -0.1049878 225.0 105 2.76 3.460 20.22  1  0    3    1
<output truncated for brevity>
  • Related