5

我正在尝试将一个参数传递给一个函数,该函数是一个字符串,但必须同时评估它的名称(符号?)和它的值(参见下面的示例)。到目前为止,我可以用来base::get获取实际值,但分配flowCore::'transform,flowSet-method'失败。我知道关于 SO 的许多问题都是关于将字符串评估为变量名,正如您将在下面看到的那样,我尝试了很多。我认为必须有一个rlang基于的答案,但我找不到任何解决方案,任何指针将不胜感激。

一个可重现的例子:

# load required packages -------------------------------------------------------
library(flowAI)   # Bioconductor
library(flowCore) # Bioconductor
library(rlang)
# load example data ------------------------------------------------------------
data(Bcells) # from flowAI
# reprex -----------------------------------------------------------------------
timeCh <- "Time" # this could be variable

x <- flowCore::transform(Bcells,`Time`=(`Time`-min(`Time`)))           # this works
y <- flowCore::transform(Bcells,`Time`=(get(timeCh)-min(get(timeCh)))) # still good
z <- flowCore::transform(Bcells,timeCh=(get(timeCh)-min(get(timeCh)))) # not good

虽然在上面的代码中,transform forz可以正常运行,但实际上向 flowSet 添加了一个名为“timeCh”的新列。这不是想要的效果,因为我想使用转换来专门更改现有的 column Time。因此,我一直在尝试一些策略来评估存储在 timeCh 中的字符串作为对象名称 (?) into transform,但无济于事:

timeSym <- sym("Time")
timequo <- quo(timeCh)

t1 <- flowCore::transform(Bcells,!!timeSym=(get(timeCh)-min(get(timeCh)))) 
# Error: unexpected '=' in "t1 <- flowCore::transform(Bcells,!!timeSym="
t2 <- flowCore::transform(Bcells,{{timeSym}}=(get(timeCh)-min(get(timeCh))))
# Error: unexpected '=' in "t2 <- flowCore::transform(Bcells,{{timeSym}}="
t3 <-  flowCore::transform(Bcells,eval(parse(text=timeCh))=(get(timeCh)-min(get(timeCh))))
# Error: unexpected '=' in "t3 <-  flowCore::transform(Bcells,eval(parse(text=timeCh))="
t4 <-  flowCore::transform(Bcells,assign(timeCh,(get(timeCh)-min(get(timeCh)))))
# Error in get(timeCh) : object 'Time' not found
t5 <-  flowCore::transform(Bcells,assign(timeCh,(get(timeCh)-min(get(timeCh))),inherits = TRUE))
# Error in get(timeCh) : object 'Time' not found
t6 <-  flowCore::transform(Bcells,with(Bcells,assign(timeCh,(get(timeCh)-min(get(timeCh))),inherits = TRUE)))
# Error in get(timeCh) : object 'Time' not found
t7 <-  flowCore::transform(Bcells,as.name(timeCh)=(get(timeCh)-min(get(timeCh))))
# Error: unexpected '=' in "t7 <-  flowCore::transform(Bcells,as.name(timeCh)="
t8 <-  flowCore::transform(Bcells,UQ(timequo)=(get(timeCh)-min(get(timeCh))))
# Error: unexpected '=' in "t8 <-  flowCore::transform(Bcells,UQ(timequo)="
t9 <-  flowCore::transform(Bcells,(eval(quote(timeCh)))=(get(timeCh)-min(get(timeCh))))
# Error: unexpected '=' in "t9 <-  flowCore::transform(Bcells,(eval(quote(timeCh)))="

在我看来,范围界定是一个问题,但我真的很难解决如何解决它。

4

2 回答 2

5

rlang使用表达式算术的一种可能方法:

# Compose the expression `Time = Time - min(Time)`
tfarg <- rlang::exprs( !!timeSym := !!timeSym - min(!!timeSym) )

# Compose the expression `flowCore::transform(Bcells, Time = Time - min(Time))`
xpr  <- rlang::expr( flowCore::transform(Bcells, !!!tfarg) )
xpr2 <- rlang::call2( flowCore::transform, Bcells, !!!tfarg )   # Alternative

# Evaluate the second expression
t1 <- eval(xpr)      # or t1 <- eval(xpr2)

# Compare to desired output
identical( summary(t1), summary(x) )  # TRUE

在上面,我们首先Time = Time - min(Time)使用quasiquotation创建表达式。将其视为替换!!timeSym为存储在内部的符号timeSym(即Time),同时使用:=使其在分配的左侧工作。

flowCore::transform(Bcells, Time = Time - min(Time))然后,我们通过将第一个表达式粘贴到flowCore::transform()函数调用中来创建第二个表达式。最后,我们对其进行评估并将结果与​​您想要的输出进行比较。

旁注:最小时间值似乎为零,因此您transform()实际上什么也没做。

于 2020-03-17T15:33:24.743 回答
4

看起来这个功能基本上模仿base::transform了它的非标准评估。这意味着我可以将其用作 MRE:

expectedresult <- transform(airquality, Ozone = Ozone - min(Ozone))

首先,我们使用以下代码重写它do.call

all.equal(
  do.call(transform, list(`_data` = airquality, Ozone = quote(Ozone - min(Ozone))))
  ,expectedresult)
#[1] TRUE

太好了,现在我们可以以编程方式设置列表名称并替换为引用的表达式:

varname <- "Ozone"
varsymbol <- as.name(varname)

all.equal(
  do.call(transform, 
          setNames(list(airquality, bquote(.(varsymbol) - min(.(varsymbol)))), 
                   c("_data", varname)))
  ,expectedresult)
#[1] TRUE

让我以一些建议结束:

尽量避免依赖非标准评估的便利函数,如果它们在您的代码中使此类体操成为必要的话。

于 2020-03-17T15:31:02.470 回答