How to use tidyeval on a column to mutate?

后端 未结 2 1038
天涯浪人
天涯浪人 2020-12-19 23:34

I\'m sorry for the confusion but eventually, the first example I posted (at the bottom of the page), did not help me to figure out how tidyeval works with mutate, so I\'m ad

相关标签:
2条回答
  • 2020-12-19 23:46

    As the error says "Error: LHS must be a name or string" where LHS means left-hand side and it's specifically referring to the !!col := 1 part. You need to turn the quosure you made into a string or symbol. It's probably easiest to get the string version of the quosure with quo_name

    colTo1 <- function(dt, ...) {
        col <- quo_name(quo(...))
        mutate(mtcars, !!col := 1)
    }
    
    0 讨论(0)
  • 2020-12-19 23:47

    @MrFlick's solution works for the one column case, but since OP used ... as an argument, I assume OP would also want the function to be able to take in multiple columns. For example, the following would not work:

    colTo1 <- function(dt, ...) {
      col <- quo_name(quo(...))
      mutate(dt, !!col := 1)
    }
    
    colTo1(mtcars, mpg, cyl)
    

    Error in inherits(x, "quosure") : object 'cyl' not found

    What we can do is to use quos instead of quo and mutate_at instead of mutate:

    colTo1 <- function(dt, ...) {
      cols <- quos(...)
      mutate_at(dt, vars(!!!cols), function(x) x=1)
    }
    

    quos converts each argument from ... into vector of quosures. Using mutate_at's vars syntax and explicit splicing with !!! from rlang, we can unquote each quosure in cols, and mutate on those specified columns.

    Now this works as intended:

    colTo1(mtcars, mpg, cyl)
    

    Result:

       mpg cyl  disp  hp drat    wt  qsec vs am gear carb
    1    1   1 160.0 110 3.90 2.620 16.46  0  1    4    4
    2    1   1 160.0 110 3.90 2.875 17.02  0  1    4    4
    3    1   1 108.0  93 3.85 2.320 18.61  1  1    4    1
    4    1   1 258.0 110 3.08 3.215 19.44  1  0    3    1
    5    1   1 360.0 175 3.15 3.440 17.02  0  0    3    2
    6    1   1 225.0 105 2.76 3.460 20.22  1  0    3    1
    7    1   1 360.0 245 3.21 3.570 15.84  0  0    3    4
    8    1   1 146.7  62 3.69 3.190 20.00  1  0    4    2
    9    1   1 140.8  95 3.92 3.150 22.90  1  0    4    2
    10   1   1 167.6 123 3.92 3.440 18.30  1  0    4    4
    ...
    

    It's also easy enough to let "1" be another argument to be passed into the function:

    colToX <- function(dt, ..., X) {
      cols <- quos(...)
      mutate_at(dt, vars(!!!cols), function(x) x=X)
    }
    
    colToX(mtcars, mpg, cyl, X = 2)
    

    Edit: OP changed the question to require that X should be another column in the same dataframe. Below is my new solution:

    colToX <- function(dt, ..., X) {
      cols <- quos(...)
      X_quo <- enquo(X)
      mutate_at(dt, vars(!!!cols), funs(.data$`!!`(X_quo)))
    }
    
    colToX(mtcars, mpg, cyl, X = hp)
    

    Here, I am using the funs function to create a list of function calls to each column referenced from vars. .data refers to the input dataframe into mutate_at (in this case dt). I used enquo to convert what's called from X into a quosure and unquote it using !!.

    Result:

       mpg cyl  disp  hp drat    wt  qsec vs am gear carb
    1  110 110 160.0 110 3.90 2.620 16.46  0  1    4    4
    2  110 110 160.0 110 3.90 2.875 17.02  0  1    4    4
    3   93  93 108.0  93 3.85 2.320 18.61  1  1    4    1
    4  110 110 258.0 110 3.08 3.215 19.44  1  0    3    1
    5  175 175 360.0 175 3.15 3.440 17.02  0  0    3    2
    6  105 105 225.0 105 2.76 3.460 20.22  1  0    3    1
    7  245 245 360.0 245 3.21 3.570 15.84  0  0    3    4
    8   62  62 146.7  62 3.69 3.190 20.00  1  0    4    2
    9   95  95 140.8  95 3.92 3.150 22.90  1  0    4    2
    10 123 123 167.6 123 3.92 3.440 18.30  1  0    4    4
    ...
    
    0 讨论(0)
提交回复
热议问题