Use multiple command chains with piping

早过忘川 提交于 2020-01-04 08:23:09

问题


EDIT: I reworked the question to make it clearer and integrate what I found by myself


Pipes are a great way to make the code more readable when using a single command chain

In some cases however, I feel one is forced to be inconsistent to its philosophy, either by creating unnecessary temp variables, mixing piping and embedded parenthesis, or defining custom functions.

See this SO question for example, where OP wants to know how to convert colnames to lower case with pipes: Dplyr or Magrittr - tolower?

I'll forget about the existence of names<- to make my point There's basically 3 ways to do it:

  1. Use a temp variable

    temp <- df %>% names %>% tolower
    df %>% setNames(temp)
    
  2. Use embedded parenthesis

    df %>% setNames(tolower(names(.)))
    
  3. Define custom function

    upcase <- function(df) {names(df) <- tolower(names(df)); df}
    df %>% upcase
    

I think it would be more consistent to be able to do something like this:

df %T>%                              # create new branch with %T%>%
  {names(.) %>% tolower %as% n} %>%  # parallel branch assigned to alias n, then going back to main branch with %>%
  setNames(n)                        # combine branches

For more complex cases, it is in my opinion more readable than the 3 examples above and I'm not polluting my workspace.

So far I've been able to come quite close, I can type:

df %T>%
  {names(.) %>% tolower %as% n} %>%
  setNames(A(n));fp()

OR (a little tribute to old school calculators)

df %1%                   # puts lhs in first memory slot (notice "%1%", I define these up to "%9%")
  names %>%
  tolower %>%
  setNames(M(1),.);fp()  # call the first stored value

(see code at bottom)


My issues are the following:

  1. I create a new environment in my global environment, and I have to flush it manually with fp(), it's quite ugly
  2. I'd like to get rid of this A function, but I don't understand well enough the environment structure of pipe chains to do so

Here's my code :

  • It creates an environment named PipeAliasEnv for aliases
  • %as% creates an alias in an isolated environment
  • %to% creates a variable in the calling environment
  • A calls an alias
  • fp removes all objects from PipeAliasEnv

This is the code that I used and a reproducible example solved in 4 different ways:

library(magrittr)
alias_init <- function(){
  assign("PipeAliasEnv",new.env(),envir=.GlobalEnv)
  assign("%as%"      ,function(value,variable)    {assign(as.character(substitute(variable)),value,envir=PipeAliasEnv)},envir=.GlobalEnv)
  assign("%to%"      ,function(value,variable)    {assign(as.character(substitute(variable)),value,envir=parent.frame())},envir=.GlobalEnv)
  assign("A"         ,function(variable)          {   get(as.character(substitute(variable)),      envir=PipeAliasEnv)},envir=.GlobalEnv)
  assign("fp"        ,function(remove_envir=FALSE){if(remove_envir) rm(PipeAliasEnv,envir=.GlobalEnv) else rm(list=ls(envir=PipeAliasEnv),envir=PipeAliasEnv)},envir=.GlobalEnv) # flush environment
  # to handle `%i%` and M(i) notation, 9 should be enough :
  sapply(1:9,function(i){assign(paste0("%",i,"%"),eval(parse(text=paste0('function(lhs,rhs){lhs <- eval(lhs)
                     rhs <- as.character(substitute(rhs))
                     str <- paste("lhs %>%",rhs[1],"(",paste(rhs[-1],collapse=","),")")
                     assign("x',i,'",lhs,envir=PipeAliasEnv)
                     eval(parse(text= str))}'))),envir=.GlobalEnv)})
  assign("M"         ,function(i)                 {   get(paste0("x",as.character(substitute(i))), envir=PipeAliasEnv)},envir=.GlobalEnv)
}
alias_init()

# using %as%
df <- iris %T>%
  {names(.) %>% toupper %as% n} %>%
  setNames(A(n)) %T>%
  {. %>% head %>% print}(.) ;fp()

# still using %as%, choosing another main chain
df <- iris %as% dataset %>%
  names %>%
  toupper %>%
  setNames(A(dataset),.) %T>%
  {. %>% head %>% print}(.);fp()

# using %to% (notice no assignment on 1st line)
iris %T>%
  {names(.) %>% toupper %as% n} %>%
  {setNames(.,A(n))} %to% df %>%     # no need for '%T>%' and '{}' here
  head %>% print;fp()

# or using the old school calculator fashion (probably the clearest for this precise task)
df <- iris %1%
  names %>%
  toupper %>%
  setNames(M(1),.) %T>%
  {. %>% head %>% print}(.);fp()

My question in short:

How do I get rid of A and fp ? Bonus: %to% doesn't work when inside {}, how can I solve this ?

来源:https://stackoverflow.com/questions/44831342/use-multiple-command-chains-with-piping

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!