英文:
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 <- function(
x,
design,
na.rm = FALSE,
excess = TRUE
) {
if (!inherits(design, "survey.design"))
stop("design is not a survey design")
# Storing variable in formula as string
# This is necessary to construct the moments formula below
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)
}
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 <- 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
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<-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)))
}
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<-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)))
}
Giving
> svykurt1(~api00, dclus2)
nlcon SE
contrast 1.7765 0.2284
> 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<-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)))
}
and it gives the right answer
> svykurt2(~api00, dclus2)
nlcon SE
contrast 1.7765 0.2284
PS: Wht hv y gt gnst vwls?
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论