Search code examples
rdplyrmagrittr

Using the compound pipe %<>% from magrittr doesn't appear to be assigning correctly


I seem to have some issue with using the %<>% pipe.

Per the magrittr documentation

The compound assignment pipe-operator, %<>%, is used to update a value by first piping it into one or more rhs expressions, and then assigning the result. For example, some_object %<>% foo %>% bar is equivalent to some_object <- some_object %>% foo %>% bar. It must be the first pipe-operator in a chain, but otherwise it works like %>%.

However, I am seeing behavior that contradicts this. Here's my counterexample (apologies that it's directly from my code)

Temp$IncurralAge <- Temp$IncurralAge %>% round((.-2)/5)*5

Works perfectly.

Temp$IncurralAge %<>% round((.-2)/5)*5

Instead prints identical output to if I did

Temp$IncurralAge %>% round((.-2)/5)*5

I don't see what's going wrong here, anyone see any problems with my syntax?


Solution

  • None of the expressions shown in the question work the way that the question seems to believe.

    R Expression Precedence

    %...% has a higher precedence than * so * is performed AFTER %>% is performed so the multiplication of 5 is not part of the right hand side of %>% but rather the result of x %>% round(...) is multiplied by 5 in a last step. See ?Syntax for the precedence table.

    For example below y is set to its square root, not to twice its square root, because y %<>% sqrt is done first since %<>% has higher precedence than * and the multiplication is done only after the assignment is done.

    y <- 1:3
    y %<>% sqrt * 2
    y
    

    Using Dot

    Also dot must be an argument to the function that is passed on the RHS if it is desired to avoid having it automatically inserted as the first argument -- if the dot is just part of an expression that is one of its arguments that does not count -- dot will still be inserted.

    For example,

    10 %>% sum(.+2)
    ## [1] 22
    
    10 %>% sum(., .+2) # same
    

    Using %>%

    In all the cases below x or equivalently . is the first argument of round and the second argument is the expression containing x or dot. The multiplication by 5 is done on the output of x %>% round(...)

    x <- 1:10 # assumed input
    
    # these are all equivalent
    
    x %>% round((.-2)/5)*5
    
    round(x, (x-2)/5)*5
    
    x %>% round(., (.-2)/5)*5
    
    x %>% round((.-2)/5)*5
    
    (x %>% round(., (.-2)/5)) * 5
    

    Presumably what is wanted is one of these equivalent expressions:

    round((x-2)/5) * 5
    
    x %>% `-`(2) %>% `/`(5) %>% round %>% `*`(5)
    
    x %>% subtract(2) %>% divide_by(5) %>% round %>% multiply_by(5)
    
    x %>% { round((.-2)/5) * 5 }
    

    Using %<>%

    The last two lines of code above also work with %<>% in place of %>%, e.g.

    x %<>% { round((.-2)/5) * 5 }