在标记为dup之前,我知道Use character string as function argument,但我的用例略有不同。我不需要在函数中传递参数,我想在+
之后传递动态数量的参数(想想ggplot2
)。
(注意:请不要格式化并删除额外的####,我已将它们留在原处,以便人们可以将代码复制粘贴到R中以简化)。
这是我的过程:
####所以让我们重现这个例子:
library(condformat)
condformat(iris[c(1:5,70:75, 120:125),]) +
rule_fill_discrete(Species) +
rule_fill_discrete(Petal.Width)
####我希望能够动态传递两个rule_fill_discrete()
函数(在我的实际用例中,我有可变数量的可能输入,并且无法对这些函数进行硬编码在)。
####首先,创建一个概括的函数:
PlotSeries <- function(x){
b=NULL
for (i in 1:length(x)){
a <- paste('rule_fill_discrete(',x[i],')',sep="")
b <- paste(paste(b,a,sep="+"))
}
b <- gsub("^\\+","",b)
eval(parse(text = b))
}
####哪个适用于一个参数
condformat(iris[c(1:5,70:75, 120:125),]) +
PlotSeries("Species")
####但是如果我们传递两个参数,那就不行了:
condformat(iris[c(1:5,70:75, 120:125),]) +
PlotSeries(c("Species","Petal.Width"))
rule_fill_discrete(Species)+ rule_fill_discrete(Petal.Width)中的错误: 二元运算符的非数字参数
####如果我们单独调用
,它将起作用condformat(iris[c(1:5,70:75, 120:125),]) +
PlotSeries("Species") +
PlotSeries("Petal.Width")
####这给了我们一个关于问题是什么的指示......当rule_fill_discrete
语句被传递为一个声明。让我们测试一下:
condformat(iris[c(1:5,70:75, 120:125),]) +
eval(rule_fill_discrete(Species) +
rule_fill_discrete(Petal.Width) )
rule_fill_discrete(Species)+ rule_fill_discrete(Petal.Width)中的错误: 二元运算符的非数字参数
####失败。但是:
condformat(iris[c(1:5,70:75, 120:125),]) +
eval(rule_fill_discrete(Species)) +
eval(rule_fill_discrete(Petal.Width) )
####这很有效。但是我们需要能够传递一组语句(这就是整点)。因此,让我们尝试在以下位置获取eval语句:
Nasty <- "eval(rule_fill_discrete(Species)) eval(rule_fill_discrete(Petal.Width))"
condformat(iris[c(1:5,70:75, 120:125),]) + Nasty #### FAIL
+.default
中的错误(condformat(iris [c(1:5,70:75,120:125),]),讨厌): 二元运算符的非数字参数
condformat(iris[c(1:5,70:75, 120:125),]) + eval(Nasty) #### FAIL
+.default
中的错误(condformat(iris [c(1:5,70:75,120:125),]),eval(Nasty)): 二元运算符的非数字参数
condformat(iris[c(1:5,70:75, 120:125),]) + parse(text=Nasty) #### FAIL
+.default
中的错误(condformat(iris [c(1:5,70:75,120:125),]),解析(text = Nasty)): 二元运算符的非数字参数
condformat(iris[c(1:5,70:75, 120:125),]) + eval(parse(text=Nasty)) #### FAIL
eval中的错误(rule_fill_discrete(Species))+ eval(rule_fill_discrete(Petal.Width)): 二元运算符的非数字参数
那我们怎么做呢?
答案 0 :(得分:2)
感谢这个stackoverflow问题,感谢@ amit-kohli的错误报告,我发现condformat包中有一个错误。
更新:更新了答案,以反映condformat 0.7中引入的新condformat API。
这里我展示了如何(使用condformat 0.7.0)。请注意,我在标准评估函数中使用的语法是从rlang包派生的。
安装condformat:
install.packages("condformat)"
# Reproduce the example
library(condformat)
condformat(iris[c(1:5,70:75, 120:125),]) %>%
rule_fill_discrete(Species) %>%
rule_fill_discrete(Petal.Width)
# With variables:
col1 <- rlang::quo(Species)
col2 <- rlang::quo(Petal.Width)
condformat(iris[c(1:5,70:75, 120:125),]) %>%
rule_fill_discrete(!! col1) %>%
rule_fill_discrete(!! col2)
# Or even with character strings to give the column names:
col1 <- "Species"
col2 <- "Petal.Width"
condformat(iris[c(1:5,70:75, 120:125),]) %>%
rule_fill_discrete(!! col1) %>%
rule_fill_discrete(!! col2)
# Do it programmatically (In a function)
#' @importFrom magrittr %>%
some_color <- function(data, col1, col2) {
condformat::condformat(data) %>%
condformat::rule_fill_discrete(!! col1) %>%
condformat::rule_fill_discrete(!! col2)
}
some_color(iris[c(1:5,70:75, 120:125),], "Species", "Petal.Width")
# General example, using an expression:
condformat(iris[c(1:5,70:75, 120:125),]) %>%
rule_fill_gradient(Species, expression = Sepal.Width - Sepal.Length)
# General example, using a column given as character and an
# expression given as character as well:
expr <- rlang::parse_expr("Sepal.Width - Sepal.Length")
condformat(iris[c(1:5,70:75, 120:125),]) %>%
rule_fill_gradient("Species", expression = !! expr)
# General example, in a function, everything given as a character:
two_column_difference <- function(data, col_to_colour, col1, col2) {
expr1 <- rlang::parse_expr(col1)
expr2 <- rlang::parse_expr(col2)
condformat::condformat(data) %>%
condformat::rule_fill_gradient(
!! col_to_colour,
expression = (!!expr1) - (!!expr2))
}
two_column_difference(iris[c(1:5,70:75, 120:125),],
col_to_colour = "Species",
col1 = "Sepal.Width",
col2 = "Sepal.Length")
可以使用将连续列预处理为离散比例的函数指定自定义离散颜色值:
discretize <- function(column) {
sapply(column,
FUN = function(value) {
if (value < 4.7) {
return("low")
} else if (value < 5.0) {
return("mid")
} else {
return("high")
}
})
}
我们可以使用colours =
指定每个比例级别的颜色:
condformat(head(iris)) %>%
rule_fill_discrete(
"Sepal.Length",
expression = discretize(Sepal.Length),
colours = c("low" = "red", "mid" = "yellow", "high" = "green"))
如果需要,discretize
函数可以返回颜色:
discretize_colours <- function(column) {
sapply(column,
FUN = function(value) {
if (value < 4.7) {
return("red")
} else if (value < 5.0) {
return("yellow")
} else {
return("green")
}
})
}
使用它的代码:
condformat(head(iris)) %>%
rule_fill_discrete(
"Sepal.Length",
expression = discretize_colours(Sepal.Length),
colours = identity)
请注意,expression
会返回我们使用的颜色colours = identity
。 identity
只是function(x) x
。
最后,使用一些rlang
tidy evaluation我们可以创建一个函数:
colour_based_function <- function(data, col1) {
col <- rlang::parse_expr(col1)
condformat::condformat(data) %>%
condformat::rule_fill_discrete(
columns = !! col1,
expression = discretize_colours(!! col),
colours = identity)
}
colour_based_function(head(iris), "Sepal.Length")
答案 1 :(得分:1)
注意:此答案为旧版condformat
中的错误提供了解决方法。该错误已经修复,在修复此错误后,请参阅@ zeehio对当前版本的回答。
我认为你有两个主要是分开的问题。这些都在你的帖子中混合在一起。我会尝试单独重述并回答它们,然后把它们放在一起 - 这一点在这一点上并没有起作用但是接近了。
首先,让我们通过定义几个变量来保存一些输入:
ir = iris[c(1:5,70:75, 120:125), ]
cf = condformat(ir)
+
? 这是一个简单的问题。 base
答案为Reduce
。以下都是等效的:
10 + 1 + 2 + 5
"+"("+"("+"(10, 1), 2), 5)
Reduce("+", c(1, 2, 5), init = 10))
更适合您的情况,我们可以这样做来复制您想要的输出:
fills = list(rule_fill_discrete(Species), rule_fill_discrete(Petal.Width))
res = Reduce(f = "+", x = fills, init = cf)
res
rule_fill_discrete
的字符串输入? 这是我第一次使用condformat
,但它似乎是在lazyeval
范例中用rule_fill_discrete_
编写的,作为非标准评估rule_fill_discrete
的标准评估对应物。 1}}。这个例子甚至在?rule_fill_discrete
中给出,但它没有按预期工作
cf + rule_fill_discrete_(columns = "Species")
# bad: Species column colored entirely red, not colored by species
# possibly a bug? At the very least misleading documentation...
cf + rule_fill_discrete_(columns = "Species", expression = expression(Species))
# bad: works as expected, but still uses an unquoted Species
# other failed attempts
cf + rule_fill_discrete_(columns = "Species", expression = expression("Species"))
cf + rule_fill_discrete_(columns = "Species", expression = "Species")
# bad: single color still single color column
SE功能中还有一个env
环境参数,但我也没有运气。也许有lazyeval
/表达经验更多的人可以指出我忽视或做错的事情。
解决方法:我们可以做的是直接传递列。这是有效的,因为我们没有做任何奇特的功能,只是直接使用它的值来确定颜色:
cf + rule_fill_discrete_(columns = c("Species"), expression = ir[["Species"]])
# hacky, but it works
将NSE版本与Reduce
一起使用很简单:
fills = list(rule_fill_discrete(Species), rule_fill_discrete(Petal.Width))
res = Reduce(f = "+", x = fills, init = cf)
res
# works!
将SE用于输入字符串,我们可以使用hacky解决方法。
input = c("Species", "Petal.Width")
fills_ = lapply(input, function(x) rule_fill_discrete_(x, expression = ir[[x]]))
res_ = Reduce(f = "+", x = fills_, init = cf)
res_
# works!
当然,这可以包含一个自定义函数,它将数据框和列名的字符串向量作为输入。
答案 2 :(得分:0)
在我的用例中,我需要更多的复杂功能,我会在这里发布,以防其他人有用。
在我的用例中,我需要能够根据一列的值为多个列着色。 condformat允许我们这样做,但我们又遇到了参数化问题。根据Gregor的回应,这是我的解决方案:
public class RaycastReflection : MonoBehaviour
{
public int gunDamage = 1;
public float hitForce = 1f;
private Transform goTransform;
private LineRenderer lineRenderer;
private Ray ray;
private RaycastHit hit;
private Vector3 inDirection;
public int nReflections = 2;
private int nPoints;
void Awake()
{
}
void Update()
{
nReflections = Mathf.Clamp(nReflections, 1, nReflections);
ray = new Ray(goTransform.position, goTransform.forward);
nPoints = nReflections;
lineRenderer.SetVertexCount(nPoints);
lineRenderer.SetPosition(0, goTransform.position);
for (int i = 0; i <= nReflections; i++)
{
//If the ray hasn't reflected yet
if (i == 0)
{
//cast the ray 100 units at the specified direction
if (Physics.Raycast(ray.origin, ray.direction, out hit, 100))
{
inDirection = Vector3.Reflect(ray.direction, hit.normal);
ray = new Ray(hit.point, inDirection);
if (nReflections == 1)
{
lineRenderer.SetVertexCount(++nPoints);
}
lineRenderer.SetPosition(i + 1, hit.point);
}
}
else
{
if (Physics.Raycast(ray.origin, ray.direction, out hit, 100))
{
Target health = hit.collider.GetComponent<Target>()
if (health != null)
health.Damage(gunDamage);
if (hit.rigidbody != null)
hit.rigidbody.AddForce(-hit.normal * hitForce);
inDirection = Vector3.Reflect(inDirection, hit.normal);
ray = new Ray(hit.point, inDirection);
lineRenderer.SetVertexCount(++nPoints);
lineRenderer.SetPosition(i + 1, hit.point);
}
}
}
}
}