23

This is a fairly simply question. But I couldn't find the answer per google/stackexchange and looking at the documentation of magrittr. How do you feed the result of a chain of functions which are connected via %>% to create a vector?

what I saw most people do is:

a <-
data.frame( x = c(1:3), y = (4:6)) %>%
sum()

but is there also a solution where I can just pipe-chain the result to feed it to an object, maybe an alias or sth of the like, somewhat like this:

data.frame( x = c(1:3), y = (4:6)) %>%
sum() %>%
a <- ()

this would help with keeping all of the code in the same logic of feeding results forward "down the pipe".

Jaap
  • 81,064
  • 34
  • 182
  • 193
grrgrrbla
  • 2,529
  • 2
  • 16
  • 29

5 Answers5

27

Try this:

data.frame( x = c(1:3), y = (4:6)) %>% sum -> a
G. Grothendieck
  • 254,981
  • 17
  • 203
  • 341
  • 7
    +1 I never thought there would be a justification for the `->`. Now there is! – Carlos Cinelli Sep 22 '14 at 14:19
  • 7
    But if you just use `->` you can´t keep going with the chain: for example `data.frame( x = c(1:3), y = (4:6)) %>% sum -> a %>% exp` gives an error, you would have to use parenthesis `(data.frame( x = c(1:3), y = (4:6)) %>% sum -> a) %>% (exp)` and if one is not careful that may lead to unexpected results . – Carlos Cinelli Sep 22 '14 at 14:23
  • G. Grothendieck, I've tried the code above (`data.frame( x = c(1:3), y = (4:6)) %>% (sum -> a) %>% exp`), but It does not correctly assign the partial result to `a`. – Carlos Cinelli Sep 22 '14 at 14:43
  • 7
    (1) Its not a good idea to continue the chain. If you really want to assign a variable part way through its better to make it into two chains. One could still write it like this: `data.frame( x = c(1:3), y = (4:6)) %>% sum -> a; a %>% exp` to avoid the problem. (2) Assigning part way through is a side effect and the functional style is to avoid side effects. – G. Grothendieck Sep 22 '14 at 15:18
  • 1
    How about: `tee=function(v,n){assign(n,v,.GlobalEnv);v}` and then `d %>% whatever %>% tee("part1") %>% otherstuff $>$ tee("part2") %>% etcetc` – Spacedman Sep 24 '14 at 16:41
  • I think one normally wants the object to be assigned into the current environment rather than the global environment in order to be able to use it in functions. – G. Grothendieck Sep 24 '14 at 18:43
  • Bizarre `a` is half the answer to life, the universe, and everything. `a * 2 == 42`. – Paul Rougieux Dec 19 '16 at 10:52
13

You can do it like so:

data.frame( x = c(1:3), y = (4:6)) %>%  
sum %>%  
assign(x="a",value=.,pos=1)  

A couple of things to note:

You can use "." to tell magrittr which argument the object being brought forward belongs in. By default it is the first, but here I use the . to indicate that I want it in the second value argument instead.

Second I had to use the pos=1 argument to make the assignment in the global environment.

John Paul
  • 12,196
  • 6
  • 55
  • 75
5

You can also use the <<- operator:

data.frame( x = c(1:3), y = (4:6)) %>%
  sum() %>%
  `<<-`(a,.)

Edit: I think John Paul's is the safest suggestion, and you could keep going with the chain doing different assignments of partial results. For example:

data.frame( x = c(1:3), y = (4:6)) %>%  
  sum %>%  
  assign(x="a",value=., pos=1)  %>% 
  exp %>%
  assign(x="b",value=., pos=1) %>% 
  sqrt %>%
  assign(x="c", value=., pos=1)

This will correctly create a, b and c.

Carlos Cinelli
  • 11,354
  • 9
  • 43
  • 66
  • 1
    You sure it won't work with `<-` ? Better not to use `<<-` when not strictly required. – Carl Witthoft Sep 22 '14 at 14:54
  • 1
    @CarlWitthoft, it won't, the `<-` operator will assign to the local environment of the function, so it will not create the variable in the global environment. – Carlos Cinelli Sep 22 '14 at 14:56
4

Using pipeR's %>>% this should be very easy.

library(pipeR)
data.frame( x = c(1:3), y = (4:6)) %>>%
  sum %>>%
  (~ a)

The pipeR tutorial may be helpful: http://renkun.me/pipeR-tutorial/ For assignment: http://renkun.me/pipeR-tutorial/Pipe-operator/Pipe-with-assignment.html

Kun Ren
  • 4,715
  • 3
  • 35
  • 50
3

What I like to do (and I found this trick somewhere I can't remember) is to use {.} -> obj at the end of my pipe-chain. This way I can add extra steps to the end of the chain by just inserting a new line, and not have to re-position to -> assignment operator.

You can also use (.) isntead of {.} but it looks a bit, odd.

For example, instead of this:

  iris %>% 
    ddply(.(Species), summarise, 
          mean.petal = mean(Petal.Length),
          mean.sepal = mean(Sepal.Length)) -> summary

Do this:

iris %>% 
    ddply(.(Species), summarise, 
          mean.petal = mean(Petal.Length),
          mean.sepal = mean(Sepal.Length)) %>% 
    {.} -> summary

It makes it easier to see where your piped data ends up. Also, while it doesn't seem like a big deal, it's easier to add another final step as you don't need to move the -> down to a new line, just add a new line before the {.} and add the step.

Like so:

iris %>% 
    ddply(.(Species), summarise, 
          mean.petal = mean(Petal.Length),
          mean.sepal = mean(Sepal.Length)) %>% 
    arrange(desc(mean.petal)) %>%   # just add a step here
    {.} -> summary

This doesn't help with saving intermediate results though. John Paul's answer to use assign() is nice, but its a bit long to type. You need to use the . since the data isn't the first argument, you have to put the name of the new argument in ""'s, and specify the environment (pos = 1). It seems lazy on my part, but using %>% is about speed.

So I wrapped the assign() in a little function which speeds it up a bit:

keep <- function(x, name) {assign(as.character(substitute(name)), x, pos = 1)}

So now you can do this:

  keep <- function(x, name) {assign(as.character(substitute(name)), x, pos = 1)}

  iris %>% 
    ddply(.(Species), summarise, 
          mean.petal = mean(Petal.Length),
          mean.sepal = mean(Sepal.Length)) %>% keep(unsorted.data) %>% # keep this step
    arrange(mean.petal) %>%
    {.} -> sorted.data

sorted.data
#     Species mean.petal mean.sepal
#1     setosa      1.462      5.006
#2 versicolor      4.260      5.936
#3  virginica      5.552      6.588

unsorted.data
#     Species mean.petal mean.sepal
#1     setosa      1.462      5.006
#2 versicolor      4.260      5.936
#3  virginica      5.552      6.588
ccoffman
  • 431
  • 4
  • 9