error when using NSE (in dplyr) : object 'value' not found











up vote
0
down vote

favorite












I'm trying to get familiar with using NSE in my code where warranted. Let's say I have pairs of columns and want to generate a new string variable for each pair indicating whether the values in that pair are the same.



library(tidyverse)
library(magrittr)

df <- tibble(one.x = c(1,2,3,4),
one.y = c(2,2,4,3),
two.x = c(5,6,7,8),
two.y = c(6,7,7,9),
# not used but also in df
extra = c(5,5,5,5))


I'm trying to write code that would accomplish the same thing as the following code:



df.mod <- df %>%
# is one.x the same as one.y?
mutate(one.x_suffix = case_when(
one.x == one.y ~ "same",
TRUE ~ "different")) %>%
# is two.x the same as two.y?
mutate(two.x_suffix = case_when(
two.x == two.y ~ "same",
TRUE ~ "different"))

df.mod
#> # A tibble: 4 x 6
#> one.x one.y two.x two.y one.x_suffix two.x_suffix
#> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
#> 1 1. 2. 5. 6. different different
#> 2 2. 2. 6. 7. same different
#> 3 3. 4. 7. 7. different same
#> 4 4. 3. 8. 9. different different


In my actual data I have an arbitrary number of such pairs (e.g. three.x and three.y, . . .) so I want to write a more generalized procedure using mutate_at.



My strategy is to pass in the ".x" variables as the .vars and then gsub the "x" for "y" on one side of the equality test inside the case_when, like so:



df.mod <- df %>%
mutate_at(vars(one.x, two.x),
funs(suffix = case_when(
. == !!sym(gsub("x", "y", deparse(substitute(.)))) ~ "same",
TRUE ~ "different")))
#> Error in mutate_impl(.data, dots): Evaluation error: object 'value' not found.


This is when I get an exception. It looks like the gsub portion is working fine:



df.debug <- df %>%
mutate_at(vars(one.x, two.x),
funs(suffix = gsub("x", "y", deparse(substitute(.)))))
df.debug
#> # A tibble: 4 x 6
#> one.x one.y two.x two.y one.x_suffix two.x_suffix
#> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
#> 1 1. 2. 5. 6. one.y two.y
#> 2 2. 2. 6. 7. one.y two.y
#> 3 3. 4. 7. 7. one.y two.y
#> 4 4. 3. 8. 9. one.y two.y


It's the !!sym() operation that's causing the exception here. What have I done wrong?



Created on 2018-11-07 by the reprex package (v0.2.1)










share|improve this question




























    up vote
    0
    down vote

    favorite












    I'm trying to get familiar with using NSE in my code where warranted. Let's say I have pairs of columns and want to generate a new string variable for each pair indicating whether the values in that pair are the same.



    library(tidyverse)
    library(magrittr)

    df <- tibble(one.x = c(1,2,3,4),
    one.y = c(2,2,4,3),
    two.x = c(5,6,7,8),
    two.y = c(6,7,7,9),
    # not used but also in df
    extra = c(5,5,5,5))


    I'm trying to write code that would accomplish the same thing as the following code:



    df.mod <- df %>%
    # is one.x the same as one.y?
    mutate(one.x_suffix = case_when(
    one.x == one.y ~ "same",
    TRUE ~ "different")) %>%
    # is two.x the same as two.y?
    mutate(two.x_suffix = case_when(
    two.x == two.y ~ "same",
    TRUE ~ "different"))

    df.mod
    #> # A tibble: 4 x 6
    #> one.x one.y two.x two.y one.x_suffix two.x_suffix
    #> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
    #> 1 1. 2. 5. 6. different different
    #> 2 2. 2. 6. 7. same different
    #> 3 3. 4. 7. 7. different same
    #> 4 4. 3. 8. 9. different different


    In my actual data I have an arbitrary number of such pairs (e.g. three.x and three.y, . . .) so I want to write a more generalized procedure using mutate_at.



    My strategy is to pass in the ".x" variables as the .vars and then gsub the "x" for "y" on one side of the equality test inside the case_when, like so:



    df.mod <- df %>%
    mutate_at(vars(one.x, two.x),
    funs(suffix = case_when(
    . == !!sym(gsub("x", "y", deparse(substitute(.)))) ~ "same",
    TRUE ~ "different")))
    #> Error in mutate_impl(.data, dots): Evaluation error: object 'value' not found.


    This is when I get an exception. It looks like the gsub portion is working fine:



    df.debug <- df %>%
    mutate_at(vars(one.x, two.x),
    funs(suffix = gsub("x", "y", deparse(substitute(.)))))
    df.debug
    #> # A tibble: 4 x 6
    #> one.x one.y two.x two.y one.x_suffix two.x_suffix
    #> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
    #> 1 1. 2. 5. 6. one.y two.y
    #> 2 2. 2. 6. 7. one.y two.y
    #> 3 3. 4. 7. 7. one.y two.y
    #> 4 4. 3. 8. 9. one.y two.y


    It's the !!sym() operation that's causing the exception here. What have I done wrong?



    Created on 2018-11-07 by the reprex package (v0.2.1)










    share|improve this question


























      up vote
      0
      down vote

      favorite









      up vote
      0
      down vote

      favorite











      I'm trying to get familiar with using NSE in my code where warranted. Let's say I have pairs of columns and want to generate a new string variable for each pair indicating whether the values in that pair are the same.



      library(tidyverse)
      library(magrittr)

      df <- tibble(one.x = c(1,2,3,4),
      one.y = c(2,2,4,3),
      two.x = c(5,6,7,8),
      two.y = c(6,7,7,9),
      # not used but also in df
      extra = c(5,5,5,5))


      I'm trying to write code that would accomplish the same thing as the following code:



      df.mod <- df %>%
      # is one.x the same as one.y?
      mutate(one.x_suffix = case_when(
      one.x == one.y ~ "same",
      TRUE ~ "different")) %>%
      # is two.x the same as two.y?
      mutate(two.x_suffix = case_when(
      two.x == two.y ~ "same",
      TRUE ~ "different"))

      df.mod
      #> # A tibble: 4 x 6
      #> one.x one.y two.x two.y one.x_suffix two.x_suffix
      #> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
      #> 1 1. 2. 5. 6. different different
      #> 2 2. 2. 6. 7. same different
      #> 3 3. 4. 7. 7. different same
      #> 4 4. 3. 8. 9. different different


      In my actual data I have an arbitrary number of such pairs (e.g. three.x and three.y, . . .) so I want to write a more generalized procedure using mutate_at.



      My strategy is to pass in the ".x" variables as the .vars and then gsub the "x" for "y" on one side of the equality test inside the case_when, like so:



      df.mod <- df %>%
      mutate_at(vars(one.x, two.x),
      funs(suffix = case_when(
      . == !!sym(gsub("x", "y", deparse(substitute(.)))) ~ "same",
      TRUE ~ "different")))
      #> Error in mutate_impl(.data, dots): Evaluation error: object 'value' not found.


      This is when I get an exception. It looks like the gsub portion is working fine:



      df.debug <- df %>%
      mutate_at(vars(one.x, two.x),
      funs(suffix = gsub("x", "y", deparse(substitute(.)))))
      df.debug
      #> # A tibble: 4 x 6
      #> one.x one.y two.x two.y one.x_suffix two.x_suffix
      #> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
      #> 1 1. 2. 5. 6. one.y two.y
      #> 2 2. 2. 6. 7. one.y two.y
      #> 3 3. 4. 7. 7. one.y two.y
      #> 4 4. 3. 8. 9. one.y two.y


      It's the !!sym() operation that's causing the exception here. What have I done wrong?



      Created on 2018-11-07 by the reprex package (v0.2.1)










      share|improve this question















      I'm trying to get familiar with using NSE in my code where warranted. Let's say I have pairs of columns and want to generate a new string variable for each pair indicating whether the values in that pair are the same.



      library(tidyverse)
      library(magrittr)

      df <- tibble(one.x = c(1,2,3,4),
      one.y = c(2,2,4,3),
      two.x = c(5,6,7,8),
      two.y = c(6,7,7,9),
      # not used but also in df
      extra = c(5,5,5,5))


      I'm trying to write code that would accomplish the same thing as the following code:



      df.mod <- df %>%
      # is one.x the same as one.y?
      mutate(one.x_suffix = case_when(
      one.x == one.y ~ "same",
      TRUE ~ "different")) %>%
      # is two.x the same as two.y?
      mutate(two.x_suffix = case_when(
      two.x == two.y ~ "same",
      TRUE ~ "different"))

      df.mod
      #> # A tibble: 4 x 6
      #> one.x one.y two.x two.y one.x_suffix two.x_suffix
      #> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
      #> 1 1. 2. 5. 6. different different
      #> 2 2. 2. 6. 7. same different
      #> 3 3. 4. 7. 7. different same
      #> 4 4. 3. 8. 9. different different


      In my actual data I have an arbitrary number of such pairs (e.g. three.x and three.y, . . .) so I want to write a more generalized procedure using mutate_at.



      My strategy is to pass in the ".x" variables as the .vars and then gsub the "x" for "y" on one side of the equality test inside the case_when, like so:



      df.mod <- df %>%
      mutate_at(vars(one.x, two.x),
      funs(suffix = case_when(
      . == !!sym(gsub("x", "y", deparse(substitute(.)))) ~ "same",
      TRUE ~ "different")))
      #> Error in mutate_impl(.data, dots): Evaluation error: object 'value' not found.


      This is when I get an exception. It looks like the gsub portion is working fine:



      df.debug <- df %>%
      mutate_at(vars(one.x, two.x),
      funs(suffix = gsub("x", "y", deparse(substitute(.)))))
      df.debug
      #> # A tibble: 4 x 6
      #> one.x one.y two.x two.y one.x_suffix two.x_suffix
      #> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
      #> 1 1. 2. 5. 6. one.y two.y
      #> 2 2. 2. 6. 7. one.y two.y
      #> 3 3. 4. 7. 7. one.y two.y
      #> 4 4. 3. 8. 9. one.y two.y


      It's the !!sym() operation that's causing the exception here. What have I done wrong?



      Created on 2018-11-07 by the reprex package (v0.2.1)







      r dplyr mutate nse






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 8 at 4:19

























      asked Nov 8 at 3:22









      lost

      1696




      1696
























          2 Answers
          2






          active

          oldest

          votes

















          up vote
          1
          down vote



          accepted










          The problem is not in !!sym, as you can see in the following example:



          df %>% mutate_at( vars(one.x, two.x),
          funs(suffix = case_when(
          . == !!sym("one.y") ~ "same",
          TRUE ~ "different")))
          # # A tibble: 4 x 6
          # one.x one.y two.x two.y one.x_suffix two.x_suffix
          # <dbl> <dbl> <dbl> <dbl> <chr> <chr>
          # 1 1 2 5 6 different different
          # 2 2 2 6 7 same different
          # 3 3 4 7 7 different different
          # 4 4 3 8 9 different different


          The problem is in trying to unquote substitute(.) inside case_when:



          df %>% mutate_at( vars(one.x, two.x),
          funs(suffix = case_when(
          . == !!substitute(.) ~ "same",
          TRUE ~ "different")))
          # Error in mutate_impl(.data, dots) :
          # Evaluation error: object 'value' not found.


          The reason for this is operator precedence. From the help page for !!:




          The !! operator unquotes its argument. It gets evaluated immediately in the surrounding context.




          In the example above, the context for !!substitute(.) is the formula, which is itself inside case_when. This leads to the expression getting immediately replaced with value, which is defined inside case_when and which has no meaning inside your data frame.



          You want to keep expressions next to their environment, which is what quosures are for. By replacing substitute with rlang::enquo, you capture the expression that gave rise to . along with its defining environment (your dataframe). To keep things tidy, let's move your gsub manipulation into a separate function:



          x2y <- function(.x)
          {
          ## Capture the expression and its environment
          qq <- enquo(.x)

          ## Retrieve the expression and deparse it
          txt <- rlang::get_expr(qq) %>% rlang::expr_deparse()

          ## Replace x with y, as before
          txty <- gsub("x", "y", txt)

          ## Put the new expression back into the quosure
          rlang::set_expr( qq, sym(txty) )
          }


          You can now use the new x2y function directly in your code. With quosures, no unquoting is necessary because the expressions already carry their environments with them; you can simply evaluate them using rlang::eval_tidy:



          df %>% mutate_at(vars(one.x, two.x),
          funs(suffix = case_when(
          . == rlang::eval_tidy(x2y(.)) ~ "same",
          TRUE ~ "different" )))
          # # A tibble: 4 x 6
          # one.x one.y two.x two.y one.x_suffix two.x_suffix
          # <dbl> <dbl> <dbl> <dbl> <chr> <chr>
          # 1 1 2 5 6 different different
          # 2 2 2 6 7 same different
          # 3 3 4 7 7 different same
          # 4 4 3 8 9 different different




          EDIT to address the question in your comment: Mushing all your code into a single line is almost always A Bad Idea™, and I strongly advise against it. However, since this question is about NSE, I think it's important to understand why simply taking the content of x2y and pasting it inside case_when leads to problems.



          enquo(), like substitute(), look in the calling environment of the function and replace the argument with the expression that was provided to that function. substitute() goes only one environment up (finding value inside case_when when you unquoted it), while enquo() keeps moving up as long as the functions in the calling stack correctly handle quasiquotation. (And most dplyr/tidyverse functions do.) So, when you call enquo(.x) inside x2y, it moves up the expressions provided to each function on the calling stack to eventually find one.x.



          When you call enquo() inside mutate_at, it is now on the same level as one.x, so it too replaces the argument (one.x in this case) with the expression that defined it (the vector c(1,2,3,4) in this case). This is not what you want. Rather than moving up levels, you now want to stay on the same level as one.x. To do so, use rlang::quo() in place of rlang::enquo():



          library( rlang )   ## To maintain at least a little bit of sanity

          df %>%
          mutate_at(vars(one.x, two.x),
          funs(suffix = case_when(
          . == eval_tidy(set_expr(quo(.),
          sym(gsub("x","y", expr_deparse(get_expr(quo(.)))))
          )
          ) ~ "same",
          TRUE ~ "different" )))
          # Now works as expected





          share|improve this answer























          • Thanks! The explanation is very helpful. I have a lot to work through here. I packaged up the x2y function into a single line just to help me see the structure a bit better. set_expr(enquo(.x), sym(gsub("x", "y", expr_deparse(get_expr(enquo(.x)))))) works fine as an ugly version of the x2y function, but if I take that same line and replace .x with . and put that inside eval_tidy() (i.e., without creating a separate function) I get an exception (<dbl: 1, 2, 3, 4>' not found). Is creating a named function required here?
            – lost
            Nov 8 at 5:13






          • 1




            @lost: Please see my edit
            – Artem Sokolov
            Nov 8 at 6:04


















          up vote
          1
          down vote













          Here is an option with map. We split the dataset into pairs of 'x', 'y' columns with the substring of column names, then loop through the list of datasets with map, transmute to create a new 'suffix' column by comparing the rows of each dataset, bind the list of datasets to single dataset and bind with the original dataset (bind_cols)



          library(tidyverse)
          df %>%
          select(matches("\.x|\.y")) %>%
          split.default(str_remove(names(.), "\..*")) %>%
          map( ~ .x %>%
          transmute(!! paste0(names(.)[1], "_suffix") :=
          reduce(., ~ c("different", "same")[(.x == .y) + 1]))) %>%
          bind_cols %>%
          bind_cols(df, .)
          # A tibble: 4 x 7
          # one.x one.y two.x two.y extra one.x_suffix two.x_suffix
          # <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
          #1 1 2 5 6 5 different different
          #2 2 2 6 7 5 same different
          #3 3 4 7 7 5 different same
          #4 4 3 8 9 5 different different




          Or another option is to create an expression and then parse it



          library(rlang)
          expr1 <- paste(grep("\.x", names(df), value = TRUE),
          grep("\.y", names(df), value = TRUE), sep="==", collapse=";")
          df %>%
          mutate(!!!rlang::parse_exprs(expr1)) %>%
          rename_at(vars(matches("==")), ~ paste0(str_remove(.x, "\s.*"), "_suffix"))
          # A tibble: 4 x 7
          # one.x one.y two.x two.y extra one.x_suffix two.x_suffix
          # <dbl> <dbl> <dbl> <dbl> <dbl> <lgl> <lgl>
          #1 1 2 5 6 5 FALSE FALSE
          #2 2 2 6 7 5 TRUE FALSE
          #3 3 4 7 7 5 FALSE TRUE
          #4 4 3 8 9 5 FALSE FALSE


          NOTE: It can be converted to 'same/different' as in the first solution. But, it may be better to keep it as logical columns






          share|improve this answer























          • Thanks. My actual df contains many additional columns (I added that to the example), so this would need to set the others aside first. Maybe with nest?
            – lost
            Nov 8 at 4:20











          Your Answer






          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "1"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53201098%2ferror-when-using-nse-in-dplyr-object-value-not-found%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          2 Answers
          2






          active

          oldest

          votes








          2 Answers
          2






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes








          up vote
          1
          down vote



          accepted










          The problem is not in !!sym, as you can see in the following example:



          df %>% mutate_at( vars(one.x, two.x),
          funs(suffix = case_when(
          . == !!sym("one.y") ~ "same",
          TRUE ~ "different")))
          # # A tibble: 4 x 6
          # one.x one.y two.x two.y one.x_suffix two.x_suffix
          # <dbl> <dbl> <dbl> <dbl> <chr> <chr>
          # 1 1 2 5 6 different different
          # 2 2 2 6 7 same different
          # 3 3 4 7 7 different different
          # 4 4 3 8 9 different different


          The problem is in trying to unquote substitute(.) inside case_when:



          df %>% mutate_at( vars(one.x, two.x),
          funs(suffix = case_when(
          . == !!substitute(.) ~ "same",
          TRUE ~ "different")))
          # Error in mutate_impl(.data, dots) :
          # Evaluation error: object 'value' not found.


          The reason for this is operator precedence. From the help page for !!:




          The !! operator unquotes its argument. It gets evaluated immediately in the surrounding context.




          In the example above, the context for !!substitute(.) is the formula, which is itself inside case_when. This leads to the expression getting immediately replaced with value, which is defined inside case_when and which has no meaning inside your data frame.



          You want to keep expressions next to their environment, which is what quosures are for. By replacing substitute with rlang::enquo, you capture the expression that gave rise to . along with its defining environment (your dataframe). To keep things tidy, let's move your gsub manipulation into a separate function:



          x2y <- function(.x)
          {
          ## Capture the expression and its environment
          qq <- enquo(.x)

          ## Retrieve the expression and deparse it
          txt <- rlang::get_expr(qq) %>% rlang::expr_deparse()

          ## Replace x with y, as before
          txty <- gsub("x", "y", txt)

          ## Put the new expression back into the quosure
          rlang::set_expr( qq, sym(txty) )
          }


          You can now use the new x2y function directly in your code. With quosures, no unquoting is necessary because the expressions already carry their environments with them; you can simply evaluate them using rlang::eval_tidy:



          df %>% mutate_at(vars(one.x, two.x),
          funs(suffix = case_when(
          . == rlang::eval_tidy(x2y(.)) ~ "same",
          TRUE ~ "different" )))
          # # A tibble: 4 x 6
          # one.x one.y two.x two.y one.x_suffix two.x_suffix
          # <dbl> <dbl> <dbl> <dbl> <chr> <chr>
          # 1 1 2 5 6 different different
          # 2 2 2 6 7 same different
          # 3 3 4 7 7 different same
          # 4 4 3 8 9 different different




          EDIT to address the question in your comment: Mushing all your code into a single line is almost always A Bad Idea™, and I strongly advise against it. However, since this question is about NSE, I think it's important to understand why simply taking the content of x2y and pasting it inside case_when leads to problems.



          enquo(), like substitute(), look in the calling environment of the function and replace the argument with the expression that was provided to that function. substitute() goes only one environment up (finding value inside case_when when you unquoted it), while enquo() keeps moving up as long as the functions in the calling stack correctly handle quasiquotation. (And most dplyr/tidyverse functions do.) So, when you call enquo(.x) inside x2y, it moves up the expressions provided to each function on the calling stack to eventually find one.x.



          When you call enquo() inside mutate_at, it is now on the same level as one.x, so it too replaces the argument (one.x in this case) with the expression that defined it (the vector c(1,2,3,4) in this case). This is not what you want. Rather than moving up levels, you now want to stay on the same level as one.x. To do so, use rlang::quo() in place of rlang::enquo():



          library( rlang )   ## To maintain at least a little bit of sanity

          df %>%
          mutate_at(vars(one.x, two.x),
          funs(suffix = case_when(
          . == eval_tidy(set_expr(quo(.),
          sym(gsub("x","y", expr_deparse(get_expr(quo(.)))))
          )
          ) ~ "same",
          TRUE ~ "different" )))
          # Now works as expected





          share|improve this answer























          • Thanks! The explanation is very helpful. I have a lot to work through here. I packaged up the x2y function into a single line just to help me see the structure a bit better. set_expr(enquo(.x), sym(gsub("x", "y", expr_deparse(get_expr(enquo(.x)))))) works fine as an ugly version of the x2y function, but if I take that same line and replace .x with . and put that inside eval_tidy() (i.e., without creating a separate function) I get an exception (<dbl: 1, 2, 3, 4>' not found). Is creating a named function required here?
            – lost
            Nov 8 at 5:13






          • 1




            @lost: Please see my edit
            – Artem Sokolov
            Nov 8 at 6:04















          up vote
          1
          down vote



          accepted










          The problem is not in !!sym, as you can see in the following example:



          df %>% mutate_at( vars(one.x, two.x),
          funs(suffix = case_when(
          . == !!sym("one.y") ~ "same",
          TRUE ~ "different")))
          # # A tibble: 4 x 6
          # one.x one.y two.x two.y one.x_suffix two.x_suffix
          # <dbl> <dbl> <dbl> <dbl> <chr> <chr>
          # 1 1 2 5 6 different different
          # 2 2 2 6 7 same different
          # 3 3 4 7 7 different different
          # 4 4 3 8 9 different different


          The problem is in trying to unquote substitute(.) inside case_when:



          df %>% mutate_at( vars(one.x, two.x),
          funs(suffix = case_when(
          . == !!substitute(.) ~ "same",
          TRUE ~ "different")))
          # Error in mutate_impl(.data, dots) :
          # Evaluation error: object 'value' not found.


          The reason for this is operator precedence. From the help page for !!:




          The !! operator unquotes its argument. It gets evaluated immediately in the surrounding context.




          In the example above, the context for !!substitute(.) is the formula, which is itself inside case_when. This leads to the expression getting immediately replaced with value, which is defined inside case_when and which has no meaning inside your data frame.



          You want to keep expressions next to their environment, which is what quosures are for. By replacing substitute with rlang::enquo, you capture the expression that gave rise to . along with its defining environment (your dataframe). To keep things tidy, let's move your gsub manipulation into a separate function:



          x2y <- function(.x)
          {
          ## Capture the expression and its environment
          qq <- enquo(.x)

          ## Retrieve the expression and deparse it
          txt <- rlang::get_expr(qq) %>% rlang::expr_deparse()

          ## Replace x with y, as before
          txty <- gsub("x", "y", txt)

          ## Put the new expression back into the quosure
          rlang::set_expr( qq, sym(txty) )
          }


          You can now use the new x2y function directly in your code. With quosures, no unquoting is necessary because the expressions already carry their environments with them; you can simply evaluate them using rlang::eval_tidy:



          df %>% mutate_at(vars(one.x, two.x),
          funs(suffix = case_when(
          . == rlang::eval_tidy(x2y(.)) ~ "same",
          TRUE ~ "different" )))
          # # A tibble: 4 x 6
          # one.x one.y two.x two.y one.x_suffix two.x_suffix
          # <dbl> <dbl> <dbl> <dbl> <chr> <chr>
          # 1 1 2 5 6 different different
          # 2 2 2 6 7 same different
          # 3 3 4 7 7 different same
          # 4 4 3 8 9 different different




          EDIT to address the question in your comment: Mushing all your code into a single line is almost always A Bad Idea™, and I strongly advise against it. However, since this question is about NSE, I think it's important to understand why simply taking the content of x2y and pasting it inside case_when leads to problems.



          enquo(), like substitute(), look in the calling environment of the function and replace the argument with the expression that was provided to that function. substitute() goes only one environment up (finding value inside case_when when you unquoted it), while enquo() keeps moving up as long as the functions in the calling stack correctly handle quasiquotation. (And most dplyr/tidyverse functions do.) So, when you call enquo(.x) inside x2y, it moves up the expressions provided to each function on the calling stack to eventually find one.x.



          When you call enquo() inside mutate_at, it is now on the same level as one.x, so it too replaces the argument (one.x in this case) with the expression that defined it (the vector c(1,2,3,4) in this case). This is not what you want. Rather than moving up levels, you now want to stay on the same level as one.x. To do so, use rlang::quo() in place of rlang::enquo():



          library( rlang )   ## To maintain at least a little bit of sanity

          df %>%
          mutate_at(vars(one.x, two.x),
          funs(suffix = case_when(
          . == eval_tidy(set_expr(quo(.),
          sym(gsub("x","y", expr_deparse(get_expr(quo(.)))))
          )
          ) ~ "same",
          TRUE ~ "different" )))
          # Now works as expected





          share|improve this answer























          • Thanks! The explanation is very helpful. I have a lot to work through here. I packaged up the x2y function into a single line just to help me see the structure a bit better. set_expr(enquo(.x), sym(gsub("x", "y", expr_deparse(get_expr(enquo(.x)))))) works fine as an ugly version of the x2y function, but if I take that same line and replace .x with . and put that inside eval_tidy() (i.e., without creating a separate function) I get an exception (<dbl: 1, 2, 3, 4>' not found). Is creating a named function required here?
            – lost
            Nov 8 at 5:13






          • 1




            @lost: Please see my edit
            – Artem Sokolov
            Nov 8 at 6:04













          up vote
          1
          down vote



          accepted







          up vote
          1
          down vote



          accepted






          The problem is not in !!sym, as you can see in the following example:



          df %>% mutate_at( vars(one.x, two.x),
          funs(suffix = case_when(
          . == !!sym("one.y") ~ "same",
          TRUE ~ "different")))
          # # A tibble: 4 x 6
          # one.x one.y two.x two.y one.x_suffix two.x_suffix
          # <dbl> <dbl> <dbl> <dbl> <chr> <chr>
          # 1 1 2 5 6 different different
          # 2 2 2 6 7 same different
          # 3 3 4 7 7 different different
          # 4 4 3 8 9 different different


          The problem is in trying to unquote substitute(.) inside case_when:



          df %>% mutate_at( vars(one.x, two.x),
          funs(suffix = case_when(
          . == !!substitute(.) ~ "same",
          TRUE ~ "different")))
          # Error in mutate_impl(.data, dots) :
          # Evaluation error: object 'value' not found.


          The reason for this is operator precedence. From the help page for !!:




          The !! operator unquotes its argument. It gets evaluated immediately in the surrounding context.




          In the example above, the context for !!substitute(.) is the formula, which is itself inside case_when. This leads to the expression getting immediately replaced with value, which is defined inside case_when and which has no meaning inside your data frame.



          You want to keep expressions next to their environment, which is what quosures are for. By replacing substitute with rlang::enquo, you capture the expression that gave rise to . along with its defining environment (your dataframe). To keep things tidy, let's move your gsub manipulation into a separate function:



          x2y <- function(.x)
          {
          ## Capture the expression and its environment
          qq <- enquo(.x)

          ## Retrieve the expression and deparse it
          txt <- rlang::get_expr(qq) %>% rlang::expr_deparse()

          ## Replace x with y, as before
          txty <- gsub("x", "y", txt)

          ## Put the new expression back into the quosure
          rlang::set_expr( qq, sym(txty) )
          }


          You can now use the new x2y function directly in your code. With quosures, no unquoting is necessary because the expressions already carry their environments with them; you can simply evaluate them using rlang::eval_tidy:



          df %>% mutate_at(vars(one.x, two.x),
          funs(suffix = case_when(
          . == rlang::eval_tidy(x2y(.)) ~ "same",
          TRUE ~ "different" )))
          # # A tibble: 4 x 6
          # one.x one.y two.x two.y one.x_suffix two.x_suffix
          # <dbl> <dbl> <dbl> <dbl> <chr> <chr>
          # 1 1 2 5 6 different different
          # 2 2 2 6 7 same different
          # 3 3 4 7 7 different same
          # 4 4 3 8 9 different different




          EDIT to address the question in your comment: Mushing all your code into a single line is almost always A Bad Idea™, and I strongly advise against it. However, since this question is about NSE, I think it's important to understand why simply taking the content of x2y and pasting it inside case_when leads to problems.



          enquo(), like substitute(), look in the calling environment of the function and replace the argument with the expression that was provided to that function. substitute() goes only one environment up (finding value inside case_when when you unquoted it), while enquo() keeps moving up as long as the functions in the calling stack correctly handle quasiquotation. (And most dplyr/tidyverse functions do.) So, when you call enquo(.x) inside x2y, it moves up the expressions provided to each function on the calling stack to eventually find one.x.



          When you call enquo() inside mutate_at, it is now on the same level as one.x, so it too replaces the argument (one.x in this case) with the expression that defined it (the vector c(1,2,3,4) in this case). This is not what you want. Rather than moving up levels, you now want to stay on the same level as one.x. To do so, use rlang::quo() in place of rlang::enquo():



          library( rlang )   ## To maintain at least a little bit of sanity

          df %>%
          mutate_at(vars(one.x, two.x),
          funs(suffix = case_when(
          . == eval_tidy(set_expr(quo(.),
          sym(gsub("x","y", expr_deparse(get_expr(quo(.)))))
          )
          ) ~ "same",
          TRUE ~ "different" )))
          # Now works as expected





          share|improve this answer














          The problem is not in !!sym, as you can see in the following example:



          df %>% mutate_at( vars(one.x, two.x),
          funs(suffix = case_when(
          . == !!sym("one.y") ~ "same",
          TRUE ~ "different")))
          # # A tibble: 4 x 6
          # one.x one.y two.x two.y one.x_suffix two.x_suffix
          # <dbl> <dbl> <dbl> <dbl> <chr> <chr>
          # 1 1 2 5 6 different different
          # 2 2 2 6 7 same different
          # 3 3 4 7 7 different different
          # 4 4 3 8 9 different different


          The problem is in trying to unquote substitute(.) inside case_when:



          df %>% mutate_at( vars(one.x, two.x),
          funs(suffix = case_when(
          . == !!substitute(.) ~ "same",
          TRUE ~ "different")))
          # Error in mutate_impl(.data, dots) :
          # Evaluation error: object 'value' not found.


          The reason for this is operator precedence. From the help page for !!:




          The !! operator unquotes its argument. It gets evaluated immediately in the surrounding context.




          In the example above, the context for !!substitute(.) is the formula, which is itself inside case_when. This leads to the expression getting immediately replaced with value, which is defined inside case_when and which has no meaning inside your data frame.



          You want to keep expressions next to their environment, which is what quosures are for. By replacing substitute with rlang::enquo, you capture the expression that gave rise to . along with its defining environment (your dataframe). To keep things tidy, let's move your gsub manipulation into a separate function:



          x2y <- function(.x)
          {
          ## Capture the expression and its environment
          qq <- enquo(.x)

          ## Retrieve the expression and deparse it
          txt <- rlang::get_expr(qq) %>% rlang::expr_deparse()

          ## Replace x with y, as before
          txty <- gsub("x", "y", txt)

          ## Put the new expression back into the quosure
          rlang::set_expr( qq, sym(txty) )
          }


          You can now use the new x2y function directly in your code. With quosures, no unquoting is necessary because the expressions already carry their environments with them; you can simply evaluate them using rlang::eval_tidy:



          df %>% mutate_at(vars(one.x, two.x),
          funs(suffix = case_when(
          . == rlang::eval_tidy(x2y(.)) ~ "same",
          TRUE ~ "different" )))
          # # A tibble: 4 x 6
          # one.x one.y two.x two.y one.x_suffix two.x_suffix
          # <dbl> <dbl> <dbl> <dbl> <chr> <chr>
          # 1 1 2 5 6 different different
          # 2 2 2 6 7 same different
          # 3 3 4 7 7 different same
          # 4 4 3 8 9 different different




          EDIT to address the question in your comment: Mushing all your code into a single line is almost always A Bad Idea™, and I strongly advise against it. However, since this question is about NSE, I think it's important to understand why simply taking the content of x2y and pasting it inside case_when leads to problems.



          enquo(), like substitute(), look in the calling environment of the function and replace the argument with the expression that was provided to that function. substitute() goes only one environment up (finding value inside case_when when you unquoted it), while enquo() keeps moving up as long as the functions in the calling stack correctly handle quasiquotation. (And most dplyr/tidyverse functions do.) So, when you call enquo(.x) inside x2y, it moves up the expressions provided to each function on the calling stack to eventually find one.x.



          When you call enquo() inside mutate_at, it is now on the same level as one.x, so it too replaces the argument (one.x in this case) with the expression that defined it (the vector c(1,2,3,4) in this case). This is not what you want. Rather than moving up levels, you now want to stay on the same level as one.x. To do so, use rlang::quo() in place of rlang::enquo():



          library( rlang )   ## To maintain at least a little bit of sanity

          df %>%
          mutate_at(vars(one.x, two.x),
          funs(suffix = case_when(
          . == eval_tidy(set_expr(quo(.),
          sym(gsub("x","y", expr_deparse(get_expr(quo(.)))))
          )
          ) ~ "same",
          TRUE ~ "different" )))
          # Now works as expected






          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Nov 8 at 6:11

























          answered Nov 8 at 4:37









          Artem Sokolov

          4,66721936




          4,66721936












          • Thanks! The explanation is very helpful. I have a lot to work through here. I packaged up the x2y function into a single line just to help me see the structure a bit better. set_expr(enquo(.x), sym(gsub("x", "y", expr_deparse(get_expr(enquo(.x)))))) works fine as an ugly version of the x2y function, but if I take that same line and replace .x with . and put that inside eval_tidy() (i.e., without creating a separate function) I get an exception (<dbl: 1, 2, 3, 4>' not found). Is creating a named function required here?
            – lost
            Nov 8 at 5:13






          • 1




            @lost: Please see my edit
            – Artem Sokolov
            Nov 8 at 6:04


















          • Thanks! The explanation is very helpful. I have a lot to work through here. I packaged up the x2y function into a single line just to help me see the structure a bit better. set_expr(enquo(.x), sym(gsub("x", "y", expr_deparse(get_expr(enquo(.x)))))) works fine as an ugly version of the x2y function, but if I take that same line and replace .x with . and put that inside eval_tidy() (i.e., without creating a separate function) I get an exception (<dbl: 1, 2, 3, 4>' not found). Is creating a named function required here?
            – lost
            Nov 8 at 5:13






          • 1




            @lost: Please see my edit
            – Artem Sokolov
            Nov 8 at 6:04
















          Thanks! The explanation is very helpful. I have a lot to work through here. I packaged up the x2y function into a single line just to help me see the structure a bit better. set_expr(enquo(.x), sym(gsub("x", "y", expr_deparse(get_expr(enquo(.x)))))) works fine as an ugly version of the x2y function, but if I take that same line and replace .x with . and put that inside eval_tidy() (i.e., without creating a separate function) I get an exception (<dbl: 1, 2, 3, 4>' not found). Is creating a named function required here?
          – lost
          Nov 8 at 5:13




          Thanks! The explanation is very helpful. I have a lot to work through here. I packaged up the x2y function into a single line just to help me see the structure a bit better. set_expr(enquo(.x), sym(gsub("x", "y", expr_deparse(get_expr(enquo(.x)))))) works fine as an ugly version of the x2y function, but if I take that same line and replace .x with . and put that inside eval_tidy() (i.e., without creating a separate function) I get an exception (<dbl: 1, 2, 3, 4>' not found). Is creating a named function required here?
          – lost
          Nov 8 at 5:13




          1




          1




          @lost: Please see my edit
          – Artem Sokolov
          Nov 8 at 6:04




          @lost: Please see my edit
          – Artem Sokolov
          Nov 8 at 6:04












          up vote
          1
          down vote













          Here is an option with map. We split the dataset into pairs of 'x', 'y' columns with the substring of column names, then loop through the list of datasets with map, transmute to create a new 'suffix' column by comparing the rows of each dataset, bind the list of datasets to single dataset and bind with the original dataset (bind_cols)



          library(tidyverse)
          df %>%
          select(matches("\.x|\.y")) %>%
          split.default(str_remove(names(.), "\..*")) %>%
          map( ~ .x %>%
          transmute(!! paste0(names(.)[1], "_suffix") :=
          reduce(., ~ c("different", "same")[(.x == .y) + 1]))) %>%
          bind_cols %>%
          bind_cols(df, .)
          # A tibble: 4 x 7
          # one.x one.y two.x two.y extra one.x_suffix two.x_suffix
          # <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
          #1 1 2 5 6 5 different different
          #2 2 2 6 7 5 same different
          #3 3 4 7 7 5 different same
          #4 4 3 8 9 5 different different




          Or another option is to create an expression and then parse it



          library(rlang)
          expr1 <- paste(grep("\.x", names(df), value = TRUE),
          grep("\.y", names(df), value = TRUE), sep="==", collapse=";")
          df %>%
          mutate(!!!rlang::parse_exprs(expr1)) %>%
          rename_at(vars(matches("==")), ~ paste0(str_remove(.x, "\s.*"), "_suffix"))
          # A tibble: 4 x 7
          # one.x one.y two.x two.y extra one.x_suffix two.x_suffix
          # <dbl> <dbl> <dbl> <dbl> <dbl> <lgl> <lgl>
          #1 1 2 5 6 5 FALSE FALSE
          #2 2 2 6 7 5 TRUE FALSE
          #3 3 4 7 7 5 FALSE TRUE
          #4 4 3 8 9 5 FALSE FALSE


          NOTE: It can be converted to 'same/different' as in the first solution. But, it may be better to keep it as logical columns






          share|improve this answer























          • Thanks. My actual df contains many additional columns (I added that to the example), so this would need to set the others aside first. Maybe with nest?
            – lost
            Nov 8 at 4:20















          up vote
          1
          down vote













          Here is an option with map. We split the dataset into pairs of 'x', 'y' columns with the substring of column names, then loop through the list of datasets with map, transmute to create a new 'suffix' column by comparing the rows of each dataset, bind the list of datasets to single dataset and bind with the original dataset (bind_cols)



          library(tidyverse)
          df %>%
          select(matches("\.x|\.y")) %>%
          split.default(str_remove(names(.), "\..*")) %>%
          map( ~ .x %>%
          transmute(!! paste0(names(.)[1], "_suffix") :=
          reduce(., ~ c("different", "same")[(.x == .y) + 1]))) %>%
          bind_cols %>%
          bind_cols(df, .)
          # A tibble: 4 x 7
          # one.x one.y two.x two.y extra one.x_suffix two.x_suffix
          # <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
          #1 1 2 5 6 5 different different
          #2 2 2 6 7 5 same different
          #3 3 4 7 7 5 different same
          #4 4 3 8 9 5 different different




          Or another option is to create an expression and then parse it



          library(rlang)
          expr1 <- paste(grep("\.x", names(df), value = TRUE),
          grep("\.y", names(df), value = TRUE), sep="==", collapse=";")
          df %>%
          mutate(!!!rlang::parse_exprs(expr1)) %>%
          rename_at(vars(matches("==")), ~ paste0(str_remove(.x, "\s.*"), "_suffix"))
          # A tibble: 4 x 7
          # one.x one.y two.x two.y extra one.x_suffix two.x_suffix
          # <dbl> <dbl> <dbl> <dbl> <dbl> <lgl> <lgl>
          #1 1 2 5 6 5 FALSE FALSE
          #2 2 2 6 7 5 TRUE FALSE
          #3 3 4 7 7 5 FALSE TRUE
          #4 4 3 8 9 5 FALSE FALSE


          NOTE: It can be converted to 'same/different' as in the first solution. But, it may be better to keep it as logical columns






          share|improve this answer























          • Thanks. My actual df contains many additional columns (I added that to the example), so this would need to set the others aside first. Maybe with nest?
            – lost
            Nov 8 at 4:20













          up vote
          1
          down vote










          up vote
          1
          down vote









          Here is an option with map. We split the dataset into pairs of 'x', 'y' columns with the substring of column names, then loop through the list of datasets with map, transmute to create a new 'suffix' column by comparing the rows of each dataset, bind the list of datasets to single dataset and bind with the original dataset (bind_cols)



          library(tidyverse)
          df %>%
          select(matches("\.x|\.y")) %>%
          split.default(str_remove(names(.), "\..*")) %>%
          map( ~ .x %>%
          transmute(!! paste0(names(.)[1], "_suffix") :=
          reduce(., ~ c("different", "same")[(.x == .y) + 1]))) %>%
          bind_cols %>%
          bind_cols(df, .)
          # A tibble: 4 x 7
          # one.x one.y two.x two.y extra one.x_suffix two.x_suffix
          # <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
          #1 1 2 5 6 5 different different
          #2 2 2 6 7 5 same different
          #3 3 4 7 7 5 different same
          #4 4 3 8 9 5 different different




          Or another option is to create an expression and then parse it



          library(rlang)
          expr1 <- paste(grep("\.x", names(df), value = TRUE),
          grep("\.y", names(df), value = TRUE), sep="==", collapse=";")
          df %>%
          mutate(!!!rlang::parse_exprs(expr1)) %>%
          rename_at(vars(matches("==")), ~ paste0(str_remove(.x, "\s.*"), "_suffix"))
          # A tibble: 4 x 7
          # one.x one.y two.x two.y extra one.x_suffix two.x_suffix
          # <dbl> <dbl> <dbl> <dbl> <dbl> <lgl> <lgl>
          #1 1 2 5 6 5 FALSE FALSE
          #2 2 2 6 7 5 TRUE FALSE
          #3 3 4 7 7 5 FALSE TRUE
          #4 4 3 8 9 5 FALSE FALSE


          NOTE: It can be converted to 'same/different' as in the first solution. But, it may be better to keep it as logical columns






          share|improve this answer














          Here is an option with map. We split the dataset into pairs of 'x', 'y' columns with the substring of column names, then loop through the list of datasets with map, transmute to create a new 'suffix' column by comparing the rows of each dataset, bind the list of datasets to single dataset and bind with the original dataset (bind_cols)



          library(tidyverse)
          df %>%
          select(matches("\.x|\.y")) %>%
          split.default(str_remove(names(.), "\..*")) %>%
          map( ~ .x %>%
          transmute(!! paste0(names(.)[1], "_suffix") :=
          reduce(., ~ c("different", "same")[(.x == .y) + 1]))) %>%
          bind_cols %>%
          bind_cols(df, .)
          # A tibble: 4 x 7
          # one.x one.y two.x two.y extra one.x_suffix two.x_suffix
          # <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
          #1 1 2 5 6 5 different different
          #2 2 2 6 7 5 same different
          #3 3 4 7 7 5 different same
          #4 4 3 8 9 5 different different




          Or another option is to create an expression and then parse it



          library(rlang)
          expr1 <- paste(grep("\.x", names(df), value = TRUE),
          grep("\.y", names(df), value = TRUE), sep="==", collapse=";")
          df %>%
          mutate(!!!rlang::parse_exprs(expr1)) %>%
          rename_at(vars(matches("==")), ~ paste0(str_remove(.x, "\s.*"), "_suffix"))
          # A tibble: 4 x 7
          # one.x one.y two.x two.y extra one.x_suffix two.x_suffix
          # <dbl> <dbl> <dbl> <dbl> <dbl> <lgl> <lgl>
          #1 1 2 5 6 5 FALSE FALSE
          #2 2 2 6 7 5 TRUE FALSE
          #3 3 4 7 7 5 FALSE TRUE
          #4 4 3 8 9 5 FALSE FALSE


          NOTE: It can be converted to 'same/different' as in the first solution. But, it may be better to keep it as logical columns







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Nov 8 at 4:29

























          answered Nov 8 at 3:57









          akrun

          391k13180253




          391k13180253












          • Thanks. My actual df contains many additional columns (I added that to the example), so this would need to set the others aside first. Maybe with nest?
            – lost
            Nov 8 at 4:20


















          • Thanks. My actual df contains many additional columns (I added that to the example), so this would need to set the others aside first. Maybe with nest?
            – lost
            Nov 8 at 4:20
















          Thanks. My actual df contains many additional columns (I added that to the example), so this would need to set the others aside first. Maybe with nest?
          – lost
          Nov 8 at 4:20




          Thanks. My actual df contains many additional columns (I added that to the example), so this would need to set the others aside first. Maybe with nest?
          – lost
          Nov 8 at 4:20


















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Stack Overflow!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.





          Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


          Please pay close attention to the following guidance:


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53201098%2ferror-when-using-nse-in-dplyr-object-value-not-found%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          這個網誌中的熱門文章

          Combustion

          Academy of Television Arts & Sciences

          L'Équipe