如何使用赋值运算符来结束管道?

16

我希望在R语言中以赋值运算符结束管道操作。

我的目标(伪代码):

data %>% analysis functions %>% analyzedData

其中data和analyzedData都是数据框(data.frame)。

我已经尝试了几个变体,每个都给出了一个独特的错误信息。一些尝试的迭代:

data %>% analysis functions %>% -> analyzedData
data %>% analysis functions %>% .-> analyzedData
data %>% analysis functions %>% <-. analyzedData
data %>% analysis functions %>% <- analyzedData

错误信息:

Error in function_list[[k]](value) : 
  could not find function "analyzedData"
Error: object 'analyzedData' not found
Error: unexpected assignment in: ..

更新: 我找到的方法是:

data %>% do analysis %>% {.} -> analyzedData

为了解决/调试长管道的问题,您可以将这两行代码插入到管道中,以最小化代码重新运行并分离问题。

data %>% pipeline functions %>% 
   {.}-> tempWayPoint
   tmpWayPoint %>% 
more pipeline functions %>% {.} -> endPipe 

3
分析数据 <- 数据 %>% 分析函数 - scoa
1
你的标题有点误导人,你真正想做的是将一个赋值操作插入到管道中,而不是结束它。 - Hong Ooi
@Hong-Ooi 我来到这里是因为我的问题就是标题中的那个,但实际上,那并不是问题。但对于像我这样想要得到答案的人,答案是中缀函数 [<-()。(我无法正确使用反引号;[<-在反引号中)。第一个参数是要进行子集分配的对象(因此被管道传入),下一个参数是该对象维度的子集(每个维度一个),最后是要将其分配给该子集或用其覆盖的对象。 - DHW
5个回答

13

如果你想最简单的做法,可以像scoa所说的那样把分配作为第一件事情,但如果你真的想把它放在最后,你可以使用assign

mtcars %>% 
  group_by(cyl) %>% 
  summarize(m = mean(hp)) %>% 
  assign("bar", .)

这将把输出存储到“bar”中。

另外,您也可以直接使用->操作符。您在问题中提到了它,但似乎您使用了类似于

mtcars %>% -> yourvariable

而不是

mtcars -> yourvariable

你不想在->前面加上%>%


谢谢,这似乎解决了问题。你知道使用 '{.} -> endPipe' 和 'assign("endPipe", .)' 的相对优点吗?我看到 assign 允许你将环境作为参数进行指定。除此之外,如果我们只关心分配到当前环境,那么其中一个比另一个更好吗?这只是风格上的差异吗? - t-kalinowski
2
我尝试过这个,但是赋值没有起作用。代码被评估了,但是我没有得到一个叫做“bar”的新对象。 - ccoffman
@ccoffman 你应该得到一个名为'bar'的新对象。它将在当前环境中进行评估,因此如果您在函数内部执行此操作,然后在函数退出后查找"bar",那么'bar'将不再存在。这更多是作用域的问题。 - Dason
2
我和coffman遇到了同样的问题。代码没有在函数中运行,正如@Dason所建议的那样。不过,将赋值语句更改为assign("bar", .,envir = .GlobalEnv)确实解决了问题。我正在使用R版本3.4.1。 - Wilbert
在R 3.5.2中,使用管道的assign()方式无法与marittr 1.5一起使用。使用pos=参数来显式设置要分配变量的环境(例如,pos=1)。或者,可以像@Wilbert一样使用envir=参数。 - mikoontz
@t.kalinowski 为了后人:使用 assign() 而不是 -> 的一个原因是你可以使用 paste() 构造变量名。请查看 ?assign() 中的示例,了解玩具用例! - mikoontz

7
看起来您正在尝试为创建新对象的副作用装饰%>管道运算符。人们可能会认为可以使用赋值运算符->来完成这个操作,但在管道中无法使用。这是因为->比用户定义的运算符如%>优先级低,这会导致解析出现问题:您的管道将被解析为(initial_stages)->(final_stages),这是没有意义的。
解决方案是用用户定义的版本替换->。顺便说一下,我们可以使用lazyeval包来确保它将在应该去的地方创建对象。
`%->%` <- function(value, x)
{
    x <- lazyeval::lazy(x)
    assign(deparse(x$expr), value, x$env)
    value
}

这是一个使用示例:
smry <- mtcars %>% 
    group_by(cyl) %->%   # ->, not >
    tmp %>%
    summarise(m=mean(mpg))

tmp
#Source: local data frame [32 x 11]
#Groups: cyl
#
#    mpg cyl  disp  hp drat    wt  qsec vs am gear carb
#1  21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
#2  21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
#3  22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1
#4  21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1
#5  18.7   8 360.0 175 3.15 3.440 17.02  0  0    3    2
#..  ... ...   ... ...  ...   ...   ... .. ..  ...  ...

smry
#Source: local data frame [3 x 2]
#
#  cyl        m
#1   4 26.66364
#2   6 19.74286
#3   8 15.10000

5

你可以将管道链想象成一个多行函数,与其他多行函数一样工作。通常保存输出的方式是在第一行上进行赋值:

analyzedData <- data %>% analysis functions

就像你会做的一样:

plot <- ggplot(data,aes(x=x,y=x)) +
  geom_point()

OP并不是在问“我该如何完成一个任务?”。OP在问:“如果我更喜欢在管道的末尾完成我的任务,我该怎么做?”因此,你的回答可能会被视为轻蔑。 - Michael Tuchman

4
更新:我找到的方法是:data %>% 进行分析 %>% {.} -> 分析数据 这样,为了排除/调试长管道的问题,您可以将这两行代码插入到管道中,以最小化代码重新运行并隔离问题。
data %>% pipeline functions %>% 
   {.}-> tempWayPoint
   tmpWayPoint %>% 
more pipeline functions %>% {.} -> endPipe 

如果您有更好的方法,请告诉我。

3
不需要使用 %>% {.},只需执行 pipeline_functions -> tmpWaypoint - Hong Ooi

3
你所需的内容也可以使用花括号进行,例如:
data %>% analysis_functions %>% {analyzedData <<-.}

您还可以在对象分配后扩展管道。我发现在将数据框分配到长管道的末尾后,将其管道化到 ggplot 或在 tidy() 前保存模型对象等其他目的非常方便。

注意编辑:用点表示管道中对象的当前状态"." 仅适用于 magrittr 管道符号 %>% 而不是原生的 R 管道符号 |>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接