I don't understand why you want the final result coefficients and CI's as character strings but the code below produces them like that.
- I have changed the
for loop, the question's didn't work;
- the regression formulas are created with
reformulate, not with paste;
- in the pipe, the numbers are not rounded,
sprintf takes care of that.
suppressPackageStartupMessages(library(tidyverse))
set.seed(42)
n <- 1000
dat <- data.frame(id=1:n,
q=runif(n, min=45, max=85),
r=runif(n, min=2.4, max=6.0),
s=runif(n, min=24, max=60),
t=runif(n, min=0.28, max=1.73))
vd <- runif(n, min=15, max=125)
my_models <- vector("list", length = ncol(dat) - 1L)
for(i in seq_along(names(dat))[-1]){
resp <- names(dat)[i]
fmla <- reformulate(termlabels = c(-1, "vd"), response = resp)
model <- lm(fmla, data = dat)
smry <- summary(model)
my_models[[i - 1L]] <- data.frame(ModelNo = i - 1L, coefficients(smry), check.names = FALSE)
}
output <- do.call(rbind, my_models)
row.names(output) <- NULL
output %>%
mutate(beta = exp(Estimate),
lower = exp(Estimate - 1.96*`Std. Error`),
upper = exp(Estimate + 1.96*`Std. Error`)) %>%
select(-Estimate, -`Std. Error`, -`t value`, -`Pr(>|t|)`) %>%
mutate(coef = sprintf("%.03g (%.03g to %.03g)", beta, lower, upper)) %>%
mutate(outcome = if_else(ModelNo == 1, "MI", "Stroke")) %>%
select(-ModelNo, -beta, -lower, -upper)
#> coef outcome
#> 1 2.13 (2.08 to 2.18) MI
#> 2 1.05 (1.05 to 1.05) Stroke
#> 3 1.64 (1.61 to 1.66) Stroke
#> 4 1.01 (1.01 to 1.01) Stroke
Created on 2022-03-07 by the reprex package (v2.0.1)
Edit
Here is another way. At first it seems more complicated but it simplifies the main loop, making it a simple purrr::map call.
Write a regression function reg_fun taking care of everything, the regression and the computation of the confidence intervals lower and upper bounds.
Then have it return an S3 class object sub-classing class "data.frame". I have called this custom class "Sandro", feel free to change this.
The custom class serves a purpose, to have its own print method. Like this the regression's return value will still be numbers and can be used extracting them with the standard extraction operators, but they will print in the desired format.
Then call purrr::map_dfr in a much simpler pipe.
suppressPackageStartupMessages(library(tidyverse))
set.seed(42)
n <- 1000
dat <- data.frame(id=1:n,
q=runif(n, min=45, max=85),
r=runif(n, min=2.4, max=6.0),
s=runif(n, min=24, max=60),
t=runif(n, min=0.28, max=1.73))
vd <- runif(n, min=15, max=125)
reg_fun <- function(y, x, data, conf = 0.95){
alpha <- qnorm(1 - (1 - conf)/2)
fit <- lm(y ~ 0 + x, data = data)
smry <- summary(fit)
estimate <- coef(smry)[, 1]
se <- coef(smry)[, 2]
out <- data.frame(
beta = exp(estimate),
lower = exp(estimate - alpha * se),
upper = exp(estimate + alpha * se)
)
class(out) <- c("Sandro", class(out))
out
}
# custom print method
print.Sandro <- function(x, digits = 3){
outcome <- rep("Stroke", nrow(x))
outcome[1] <- "MI"
fmt <- paste0("%.0", digits, "g")
fmt <- paste0(fmt, " (", fmt, " to ", fmt, ")")
out <- data.frame(
coef = sprintf(fmt, x[["beta"]], x[["lower"]], x[["upper"]]),
outcome = outcome
)
print.data.frame(out)
}
dat %>%
select(-id) %>%
map_dfr(reg_fun, x = vd, data = dat)
#> coef outcome
#> 1 2.13 (2.08 to 2.18) MI
#> 2 1.05 (1.05 to 1.05) Stroke
#> 3 1.64 (1.61 to 1.66) Stroke
#> 4 1.01 (1.01 to 1.01) Stroke
Created on 2022-03-07 by the reprex package (v2.0.1)
Edit 2
To have the responses also print, change the print method and the pipe to the following.
print.Sandro <- function(x, digits = 3){
fmt <- paste0("%.0", digits, "g")
fmt <- paste0(fmt, " (", fmt, " to ", fmt, ")")
out <- data.frame(
coef = sprintf(fmt, x[["beta"]], x[["lower"]], x[["upper"]])
)
print.data.frame(out)
}
dat %>%
select(-id) %>%
map_dfr(reg_fun, x = vd, data = dat) %>%
mutate(outcome = names(dat)[-1]) %>%
relocate(outcome, .before = "beta")
And to see the return data.frame with the coefficients, lower and upper bounds, end the pipe with a call to as.data.frame.
dat %>%
select(-id) %>%
map_dfr(reg_fun, x = vd, data = dat) %>%
mutate(outcome = names(dat)[-1]) %>%
relocate(outcome, .before = "beta") %>%
as.data.frame()
#> outcome beta lower upper
#> 1 q 2.128118 2.079911 2.177443
#> 2 r 1.050421 1.048759 1.052085
#> 3 s 1.638222 1.612149 1.664716
#> 4 t 1.012015 1.011534 1.012497