How can you add (say a vectors name) to an object of type 'formula' within a function? - class

This is a very trimmed down version of what I want to do, I can't paste my exact problem cause the code is too long and complex but I think this gets at the root of issue. Thanks to Josh's answer to this question How do you code an R function so that it 'knows' to look in 'data' for the variables in other arguments? I'm part way there.
example <- function(model, xvar3=NULL, xvar4=NULL, data){
print(class(model))
#xvar3 <- eval(substitute(xvar3), envir=data, enclos=parent.frame())
#xvar4 <- eval(substitute(xvar4), envir=data, enclos=parent.frame())
print(class(xvar3))
xvar5 <- xvar4^2
mod <- glm( model + xvar3 + xvar5, data=data)
return(mod)
}
example(mpg ~ cyl, hp, wt, data=mtcars)
This fails. If you remove the comments (based on help from previous question) it solves the problem of 'finding' hp and wt. 'model' is of class formula and I would like that to become 'mpg ~ cyl + xvar3 + xvar5' so that the glm will run. But I can't seem to be able to add them to the formula.
I've been toying around with 'call' classes and further with 'eval', and 'as.formula' with variations of 'paste' and 'noquote' etc but can't see to get it to stick.

Here's one way. The trick I used is to create a new formula based on the given one + the two extra variables. I then did a trick with the environment of the formula so that both xvar3/xvar5 AND any variables local to the caller can be used.
glm will always look in the formula's environment AND in the data for variables (and nowhere else!). That's why the formula environment must the manipulated a bit in this case: it contains xvar3 and xvar5, and the parent environment is set to the original formula's environment so that it is also searched for variables (foo in the last example)...
example <- function(model, xvar3=NULL, xvar4=NULL, data){
e <- new.env(parent=environment(model))
e$xvar3 <- eval(substitute(xvar3), envir=data, enclos=parent.frame())
e$xvar4 <- eval(substitute(xvar4), envir=data, enclos=parent.frame())
e$xvar5 <- e$xvar4^2
model <- update(model, . ~ . + xvar3 + xvar5)
environment(model) <- e
mod <- glm(model, data=data)
return(mod)
}
example(mpg ~ cyl, hp, wt, data=mtcars)
# Using a local variable should work too:
doit <- function(d) {
foo <- d$cyl+1
example(mpg ~ foo, hp, wt, data=d)
}
doit(mtcars)

Here's how I'd do it:
add_vars <- function(model, xvar3=NULL, xvar4=NULL, data){
# Capture the unevalated calls to xvar3 and xvar4
xvar3 <- substitute(xvar3)
xvar4 <- substitute(xvar4)
# Use substitute to create the correct formula to supply to update
update_f <- eval(substitute(. ~ . + xvar3 + I(xvar4 ^ 2),
list(xvar3 = xvar3, xvar4 = xvar4)))
# Modify the original formula string
update(model, update_f)
}
add_vars(mpg ~ cyl, hp, wt)
# mpg ~ cyl + hp + I(wt^2)

Another option for this (from a colleague):
example <- function(model, xvar3=NULL, xvar4=NULL, data){
data$xvar3 <- eval(substitute(xvar3), envir=data, enclos=parent.frame())
data$xvar4 <- eval(substitute(xvar4), envir=data, enclos=parent.frame())
data$xvar5 <- data$xvar4^2
model <- as.formula(paste(model[2], paste(model[3], "xvar4","xvar5", sep="+"), sep="~"))
mod <- glm(model, data=data)
return(mod)
}
example(mpg ~ cyl, hp, wt, data=mtcars)
I like this it's quite clean.

Related

MUMPS Address Validation

I am working on prerequisite questions for a class I am trying to attend. I am working on revisions to two pieces of code. I have completed one and I am stuck on this one. I am trying to read an abbreviated address line. In this case FL33606. I am able to read the address. But I am receiving an undefined error for the Quit command "Q: done". Would someone be able to assist me in identifying what is wrong?
N prompt,val, done
S prompt="Enter State and Zip (StateZip): "
F W !,prompt R val Q:val="" D Q:done
. I val'="?2A5N" W !,"Invalid entry" Q
. S done=1
I val="" q
W !,"Valid Entry: ",val
Q
I have two errors
done variable should be defined before the first read
the pattern should not be in quotes, where ? is the operator not =
S prompt="Enter State and Zip (StateZip): "
S done=0
F W !,prompt R val Q:val="" D Q:done
. I val'?2A5N W !,"Invalid entry" Q
. S done=1
I val="" q
W !,"Valid Entry: ",val
Q
Why do you use short commands, and dots?
Is not this much better readable?
Set prompt = "Enter State and Zip (StateZip): "
For {
Write !,prompt
Read val
Quit:val=""
Quit:val?2A5N
Write !,"Invalid entry"
}
If val="" Quit
Write !,"Valid Entry: ",val
Quit

How to get a zoo object with a num and Date object in it?

I want to transform my excel (bank return & the date) in a zoo object, with the data in the zoo object being numeric & date. I used the following data:
Classes ‘tbl_df’, ‘tbl’ and 'data.frame': 1455 obs. of 2 variables:
$ date : POSIXct, format: "1925-01-02" "1925-01-03" "1925-01-05" "1925-01-06" ...
$ Deutsche Bank: num 0.181 0.191 0.191 0.184 0.186 ...
I used the following code:
db.xts <- na.omit(as.data.frame(db.kurs))
db.xts2 <- db.xts %>% mutate(date = as.Date(date, format = "%d.%m.%Y")) %>% mutate(`Deutsche Bank` = as.numeric(`Deutsche Bank`))
db.xts3 <- as.xts(db.xts2, db.kurs$date)
db.zoo <- as.zoo(db.xts3)
db.zoo <- db.zoo[, colnames(db.zoo) != "date"]
which leaves me with the following:
‘zoo’ series from 1925-01-02 to 1929-12-31
Data: chr [1:1455] "0.1807194" "0.1911455" "0.1911455" "0.1841948" "0.1859325" "0.1841948" "0.1807194" "0.1789817" ...
Index: POSIXct[1:1455], format: "1925-01-02" "1925-01-03" "1925-01-05" "1925-01-06" "1925-01-07" "1925-01-08" "1925-01-09" "1925-01-10" ...
If I try to run it without the as.xts command R deletes all the dates and uses an iteger from 1 to 1455.
Does anybody have an idea how to solve it?
Thanks for the help,
Nick

Aligning and italicising table column headings using Rmarkdown and pander

I am writing a rmarkdown document knitting to pdf with tables taken from portions of lists from the ezANOVA package. The tables are made using the pander package. Toy Rmarkdown file with toy dataset below.
---
title: "Table Doc"
output: pdf_document
---
```{r global_options, include=FALSE}
#set global knit options parameters.
knitr::opts_chunk$set(fig.width=12, fig.height=8, fig.path='Figs/',
echo=FALSE, warning=FALSE, message=FALSE, dev = 'pdf')
```
```{r, echo=FALSE}
# toy data
id <- rep(c(1,2,3,4), 5)
group1 <- factor(rep(c("A", "B"), 10))
group2 <- factor(rep(c("A", "B"), each = 10))
dv <- runif(20, min = 0, max = 10)
df <- data.frame(id, group1, group2, dv)
```
``` {r anova, echo = FALSE}
library(ez)
library(plyr)
library(pander)
# create anova object
anOb <- ezANOVA(df,
dv = dv,
wid = id,
between = c(group1, group2),
type = 3,
detailed = TRUE)
# extract the output table from the anova object, reduce it down to only desired columns
anOb <- data.frame(anOb[[1]][, c("Effect", "F", "p", "p<.05")])
# format entries in columns
anOb[,2] <- format( round (anOb[,2], digits = 1), nsmall = 1)
anOb[,3] <- format( round (anOb[,3], digits = 4), nsmall = 1)
pander(anOb, justify = c("left", "center", "center", "right"))
```
Now I have a few problems
a) For the last three columns I would like to have the column heading in the table aligned in the center, but the actual column entries underneath those headings aligned to the right.
b) I would like to have the column headings 'F' and 'p' in italics and the 'p' in the 'p<.05' column in italics also but the rest in normal font. So they read F, p and p<.05
I tried renaming the column headings using plyr::rename like so
anOb <- rename(anOb, c("F" = "italic(F)", "p" = "italic(p)", "p<.05" = ""))
But it didn't work
In markdown, you have to use the markdown syntax for italics, which is wrapping text between a star or underscore:
> names(anOb) <- c('Effect', '*F*', '*p*', '*p<.05*')
> pander(anOb)
-----------------------------------------
Effect *F* *p* *p<.05*
--------------- ------ -------- ---------
(Intercept) 52.3 0.0019 *
group1 1.3 0.3180
group2 2.0 0.2261
group1:group2 3.7 0.1273
-----------------------------------------
If you want to do that in a programmatic way, you can also use the pandoc.emphasis helper function to add the starts to a string.
But your other problem is due to a bug in the package, for which I've just proposed a fix on GH. Please feel free to give a try to that branch and report back on GH -- I will try to get some time later this week to clean up the related unit tests and merge the branch if everything seem to be OK.

How to specify column alignment and width at the same time with xtable

Although the problem described in How to set both column width and text alignment in align argument of xtable? looks the same and solution looks promising I can't get this working.
---
output: pdf_document
header-includes:
- \usepackage{longtable}
- \newcolumntype{R}[1]{>{\raggedleft\let\newline\\\arraybackslash\hspace{0pt}}p{#1}}
---
```{r, results='asis', echo=FALSE, warning=FALSE}
library(xtable)
options(xtable.comment = FALSE)
addtorow <- list()
addtorow$pos <- list()
addtorow$pos[[1]] <- c(0)
addtorow$command <- c(paste("\\hline \n",
"\\endhead \n",
"\\hline \n",
"{\\footnotesize See next page} \n",
"\\endfoot \n",
"\\endlastfoot \n",
sep=""))
irisShort <- head(iris)
print(xtable(irisShort,
digits=rep(0,6),
align=c(
"p{0.015\\textwidth}|",
"R{0.37\\textwidth}|",
"R{0.12\\textwidth}|",
"R{0.08\\textwidth}|",
"R{0.02\\textwidth}|",
"p{0.35\\textwidth}|")))
```
The newcolumntype declaration seems to be wrong in my example, but I don't know how to fix it. The error I'm getting is:
! Undefined control sequence.
<recently read> \newcolumntype
Any ideas how to fix it?

Standardized coefficients for lmer model

I used to use the code below to calculate standardized coefficients of a lmer model. However, with the new version of lme the structure of the returned object has changed.
How to adapt the function stdCoef.lmer to make it work with the new lme4 version?
# Install old version of lme 4
install.packages("lme4.0", type="both",
repos=c("http://lme4.r-forge.r-project.org/repos",
getOption("repos")[["CRAN"]]))
# Load package
detach("package:lme4", unload=TRUE)
library(lme4.0)
# Define function to get standardized coefficients from an lmer
# See: https://github.com/jebyrnes/ext-meta/blob/master/r/lmerMetaPrep.R
stdCoef.lmer <- function(object) {
sdy <- sd(attr(object, "y"))
sdx <- apply(attr(object, "X"), 2, sd)
sc <- fixef(object)*sdx/sdy
#mimic se.ranef from pacakge "arm"
se.fixef <- function(obj) attr(summary(obj), "coefs")[,2]
se <- se.fixef(object)*sdx/sdy
return(list(stdcoef=sc, stdse=se))
}
# Run model
fm0 <- lmer(Reaction ~ Days + (Days | Subject), sleepstudy)
# Get standardized coefficients
stdCoef.lmer(fm0)
# Comparison model with prescaled variables
fm0.comparison <- lmer(scale(Reaction) ~ scale(Days) + (scale(Days) | Subject), sleepstudy)
The answer by #LeonardoBergamini works, but this one is more compact and understandable and only uses standard accessors — less likely to break in the future if/when the structure of the summary() output, or the internal structure of the fitted model, changes.
stdCoef.merMod <- function(object) {
sdy <- sd(getME(object,"y"))
sdx <- apply(getME(object,"X"), 2, sd)
sc <- fixef(object)*sdx/sdy
se.fixef <- coef(summary(object))[,"Std. Error"]
se <- se.fixef*sdx/sdy
return(data.frame(stdcoef=sc, stdse=se))
}
library("lme4")
fm1 <- lmer(Reaction ~ Days + (Days | Subject), sleepstudy)
fixef(fm1)
## (Intercept) Days
## 251.40510 10.46729
stdCoef.merMod(fm1)
## stdcoef stdse
## (Intercept) 0.0000000 0.00000000
## Days 0.5352302 0.07904178
(This does give the same results as stdCoef.lmer in
#LeonardoBergamini's answer ...)
You can get partially scaled coefficients — scaled by 2 times the SD of x, but not scaled by SD(y), and not centred — using broom.mixed::tidy + dotwhisker::by_2sd:
library(broom.mixed)
library(dotwhisker)
(fm1
|> tidy(effect="fixed")
|> by_2sd(data=sleepstudy)
|> dplyr::select(term, estimate, std.error)
)
this should work:
stdCoef.lmer <- function(object) {
sdy <- sd(attr(object, "resp")$y) # the y values are now in the 'y' slot
### of the resp attribute
sdx <- apply(attr(object, "pp")$X, 2, sd) # And the X matriz is in the 'X' slot of the pp attr
sc <- fixef(object)*sdx/sdy
#mimic se.ranef from pacakge "arm"
se.fixef <- function(obj) as.data.frame(summary(obj)[10])[,2] # last change - extracting
## the standard errors from the summary
se <- se.fixef(object)*sdx/sdy
return(data.frame(stdcoef=sc, stdse=se))
}