使用`svycontrast`在函数内部时,当对比涉及到反引号和`I()`时。

huangapple go评论97阅读模式
英文:

Using svycontrast inside a function when contrasts involve backticks and I()

问题

我试图编写一个R函数,实现了在`survey`包中估计峰度的方法,根据这里的讨论[链接](https://stackoverflow.com/questions/76733872/using-svyrecvar-to-get-the-variance-of-a-statistic-in-the-survey-r-package):

svykurt <- function(
x,
design,
na.rm = FALSE,
excess = TRUE
) {

if (!inherits(design, "survey.design"))
stop("design is not a survey design")

将变量存储为字符串的公式

这对于构建下面的 moments 公式是必要的

var_name <- as.character(x)[2]

x <- model.frame(x, design$variables, na.action = na.pass)
x <- as.matrix(x)

if(na.rm){
x <- x[!is.na(x)]
}

momnts_fmla <- paste0(
"~",
var_name,
" + I(",
var_name,
"^2) + I(",
var_name,
"^3) + I(",
var_name,
"^4)")

momnts <- svymean(
as.formula(momnts_fmla),
design,
na.rm = na.rm
)

centrl_momnts <- svycontrast(
momnts,
list(
mu4 = bquote(-3 * .(x^4) + 6 * .(x^2) * .(I(x^2)) - 4 * .(x) * .(I(x^3)) + .(I(x^4))),
sigma2 = bquote(.(I(x^2)) - .(x^2))
)
)

return(centrl_momnts)

}


我在使用 `svycontrast` 将原始矩转换为中心矩时遇到了问题。我找到了一个有用的讨论[链接](https://stackoverflow.com/questions/67812320/pass-expression-as-argument-in-r-survey-package),但在将像 `I(x^2)` 这样的内容传递给 `svycontrast` 的 `contrasts` 参数中的 `bquote` 时仍然遇到了问题。

data(api)
dclus1 <- svydesign(id = ~dnum, weights = ~pw, data = apiclus1, fpc = ~fpc)
svykurt(x = ~api00, dclus1, na.rm = TRUE)


Error in eval(e[[2L]], where) : object 'I(x^2)' not found


如果有人对我如何处理这个问题有想法,请告诉我。

谢谢!
英文:

I am trying to write an R function implementing the approach to estimating kurtosis in the survey package, following the discussion here:

svykurt &lt;- function(
    x,
    design,
    na.rm = FALSE,
    excess = TRUE
) {
  
  if (!inherits(design, &quot;survey.design&quot;))
    stop(&quot;design is not a survey design&quot;)
  
  # Storing variable in formula as string
  # This is necessary to construct the  moments formula below
  var_name &lt;- as.character(x)[2]
  
  x &lt;- model.frame(x, design$variables, na.action = na.pass)
  x &lt;- as.matrix(x)
  
  if(na.rm){
    x &lt;- x[!is.na(x)]
  }
  
  momnts_fmla &lt;- paste0(
    &quot;~&quot;,
    var_name,
    &quot; + I(&quot;,
    var_name,
    &quot;^2) + I(&quot;,
    var_name,
    &quot;^3) + I(&quot;,
    var_name,
    &quot;^4)&quot;)
  
  momnts &lt;- svymean(
    as.formula(momnts_fmla),
    design,
    na.rm = na.rm
  )
  
  centrl_momnts &lt;- svycontrast(
    momnts,
    list(
      mu4 = bquote(-3 * .(x^4) + 6 * .(x^2) * .(`I(x^2)`) - 4 * .(x) * .(`I(x^3)`) + .(`I(x^4)`)),
      sigma2 = bquote(.(`I(x^2)`) - .(x^2))
    )
  )
  
  return(centrl_momnts)
  
}

I am stuck on using svycontrast to transform raw moment into central moments. I found a helpful discussion here, but still run into trouble when passing things like I(x^2) to bquote inside the contrasts argument of svycontrast.

data(api)
dclus1 &lt;- svydesign(id = ~dnum, weights = ~pw, data = apiclus1, fpc = ~fpc)
svykurt(x = ~api00, dclus1, na.rm = TRUE)
Error in eval(e[[2L]], where) : object &#39;I(x^2)&#39; not found

If anyone has an idea on how I could approach this, let me know.

Thank you!

答案1

得分: 1

我认为最好在开始时重命名变量。例如

svykurt <- function(formula, design) {
    var_name <- formula[[2]]
    if (".x." %in% colnames(design)) stop("😀")
    design <- eval(bquote(update(design, .x. = .(var_name))))
    moments <- svymean(~ .x. + I(.x.^2) + I(.x.^3) + I(.x.^4), design)
    central_moments <- svycontrast(moments, 
        list(mu4 = quote(-3 * .x.^4 + 6 * .x.^2 * `I(.x.^2)` - 4 * .x. * `I(.x.^3)` + `I(.x.^4)`),
             sigma2 = quote(`I(.x.^2)` - `.x.`^2)
        ))
    svycontrast(central_moments, quote(mu4 / (sigma2 * sigma2)))	
}

这显然不是完美的,因为有人可能有一个名为.x.的变量。

另一种方法是改变moments的元素名称,这会打破类的不透明性:

svykurt1 <- function(formula, design) {
    var_name <- as.character(formula[[2]])
    moments_formula <- as.formula(paste0(
        "~",
        var_name,
        " + I(",
        var_name,
        "^2) + I(",
        var_name,
        "^3) + I(",
        var_name,
        "^4)"
    ))
    moments <- svymean(moments_formula, design)
    attr(moments, "names") <- c("one", "two", "three", "four")
    dimnames(attr(moments, "var")) <- list(c("one", "two", "three", "four"), c("one", "two", "three", "four"))
    central_moments <- svycontrast(moments, 
        list(mu4 = quote(-3 * one^4 + 6 * one^2 * two - 4 * one * three + four),
             sigma2 = quote(two - one^2)
        ))
    svycontrast(central_moments, quote(mu4 / (sigma2 * sigma2)))	
}

这样做会得到:

> svykurt1(~api00, dclus2)
          nlcon     SE
contrast 1.7765 0.2284
> svykurt(~api00, dclus2)
          nlcon     SE
contrast 1.7765 0.2284

这没有回答如何引用包含反引号的名称的问题,这确实很难。您可以通过另一种方式绕过它,将字符转换为名称的翻译延迟。在这里,我们通过将字符字符串替换为表达式来构建进入svycontrast的表达式。字符字符串不需要额外的引号,因为它们已经是死的。

这可能是三种解决方案中最优雅的:

svykurt2 <- function(formula, design) {
    var_name <- as.character(formula[[2]])
    moments_char <- paste0("I(", var_name, "^", 1:4, ")")
    moments_formula <- make.formula(moments_char)
    moments <- svymean(moments_formula, design)
    mu4expr <- substitute(-3 * one^4 + 6 * one^2 * two - 4 * one * three + four, 
        list(one = as.name(moments_char[1]), two = as.name(moments_char[2]),
             three = as.name(moments_char[3]), four = as.name(moments_char[4]))
    )
    sigma2expr <- substitute(two - one^2, 
        list(one = as.name(moments_char[1]), two = as.name(moments_char[2]))
    )
    central_moments <- svycontrast(moments, 
        list(mu4 = mu4expr,
             sigma2 = sigma2expr
        ))
    svycontrast(central_moments, quote(mu4 / (sigma2 * sigma2)))	
}

这会得到正确的答案:

> svykurt2(~api00, dclus2)
          nlcon     SE
contrast 1.7765 0.2284

PS:为什么你省略了元音字母?

英文:

I think it's easier to rename the variable at the beginning. For example

svykurt&lt;-function(formula, design){
	var_name &lt;- formula[[2]]
	if (&quot;.x.&quot; %in% colnames(design)) stop(&quot;&#128580;&quot;)
	design&lt;-eval(bquote(update(design, .x.=.(var_name))))
	moments&lt;-svymean(~.x.+I(.x.^2)+I(.x.^3)+I(.x.^4), design)
	central_moments&lt;-svycontrast(moments, 
		list(mu4=quote(-3*.x.^4+6*.x.^2*`I(.x.^2)`-4*.x.*`I(.x.^3)`+`I(.x.^4)`),
			 sigma2=quote(`I(.x.^2)`-`.x.`^2)
	))
	svycontrast(central_moments, quote(mu4/(sigma2*sigma2)))	
}

This is obviously not perfect, because someone might have a variable called .x..

An alternative, which breaks the opaqueness of classes, is to rename the elements of moments

svykurt1&lt;-function(formula, design){
	var_name &lt;- as.character(formula[[2]])
	moments_formula &lt;- as.formula(paste0(
    &quot;~&quot;,
    var_name,
    &quot; + I(&quot;,
    var_name,
    &quot;^2) + I(&quot;,
    var_name,
    &quot;^3) + I(&quot;,
    var_name,
    &quot;^4)&quot;))
  
	moments&lt;-svymean(moments_formula, design)
	attr(moments,&quot;names&quot;)&lt;-c(&quot;one&quot;,&quot;two&quot;,&quot;three&quot;,&quot;four&quot;)
	dimnames(attr(moments,&quot;var&quot;))&lt;-list(c(&quot;one&quot;,&quot;two&quot;,&quot;three&quot;,&quot;four&quot;),c(&quot;one&quot;,&quot;two&quot;,&quot;three&quot;,&quot;four&quot;))
	central_moments&lt;-svycontrast(moments, 
		list(mu4=quote(-3*one^4+6*one^2*two-4*one*three+four),
			 sigma2=quote(two-one^2)
	))
	svycontrast(central_moments, quote(mu4/(sigma2*sigma2)))	
}

Giving

&gt; svykurt1(~api00, dclus2)
          nlcon     SE
contrast 1.7765 0.2284
&gt; svykurt(~api00, dclus2)
          nlcon     SE
contrast 1.7765 0.2284

This doesn't answer the question of how you quote a name that contains backticks, which does indeed seem hard. You can work around it another way by deferring the translation from character to name. Here we construct the expressions that go into svycontrast by substituting character strings into an expression. The character strings don't need extra quoting because they are already dead.

This is probably the most elegant of the three solutions:

svykurt2&lt;-function(formula, design){
	var_name &lt;- as.character(formula[[2]])
	moments_char&lt;-paste0(&quot;I(&quot;,var_name,&quot;^&quot;,1:4,&quot;)&quot;)
	moments_formula &lt;- make.formula(moments_char)
	moments&lt;-svymean(moments_formula, design)
	mu4expr&lt;-substitute(-3*one^4+6*one^2*two-4*one*three+four, 
			list(one=as.name(moments_char[1]),two=as.name(moments_char[2]),
			three=as.name(moments_char[3]),four=as.name(moments_char[4])))
	sigma2expr&lt;-substitute(two-one^2, 
			list(one=as.name(moments_char[1]),two=as.name(moments_char[2])))
	central_moments&lt;-svycontrast(moments, 
		list(mu4=mu4expr,
			 sigma2=sigma2expr
	))
	svycontrast(central_moments, quote(mu4/(sigma2*sigma2)))	
}

and it gives the right answer

&gt; svykurt2(~api00, dclus2)
          nlcon     SE
contrast 1.7765 0.2284

PS: Wht hv y gt gnst vwls?

huangapple
  • 本文由 发表于 2023年8月4日 01:16:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/76830298.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定