You can use the indices stored in the "na.action" component. Example using modified mtcars:
> head(mtcars2)
mpg cyl disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160 110 3.90 NA 2.620 0 1 4 4 #
Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 2.875 0 1 4 4
Datsun 710 22.8 4 108 93 3.85 2.320 2.320 1 1 4 1
Hornet 4 Drive 21.4 6 258 110 3.08 3.215 NA 1 0 3 1 #
Hornet Sportabout 18.7 8 360 175 3.15 NA 3.440 0 0 3 2 #
Valiant 18.1 6 225 105 2.76 3.460 3.460 1 0 3 1
Fit the models:
> fit_wt <- lm(wt ~ mpg + cyl + disp + hp + drat + vs + am + gear + carb, mtcars2)
> fit_qsec <- lm(qsec ~ mpg + cyl + disp + hp + drat + vs + am + gear + carb, mtcars2)
Notice the na.action component:
> fit_wt$na.action
Mazda RX4 Hornet Sportabout Chrysler Imperial
1 5 17
attr(,"class")
[1] "omit"
You can then replace the missings with the fitted values:
> mtcars2 |>
+ transform(wt=replace(wt, fit_wt$na.action, fit_wt$fitted.values[fit_wt$na.action]),
+ qsec=replace(qsec, fit_qsec$na.action, fit_qsec$fitted.values[fit_qsec$na.action]))
mpg cyl disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160 110 3.90 2.983000 2.620000 0 1 4 4 #
Mazda RX4 Wag 21.0 6 160 110 3.90 2.875000 2.875000 0 1 4 4
Datsun 710 22.8 4 108 93 3.85 2.320000 2.320000 1 1 4 1
Hornet 4 Drive 21.4 6 258 110 3.08 3.215000 3.825437 1 0 3 1 #
Hornet Sportabout 18.7 8 360 175 3.15 4.014875 3.440000 0 0 3 2 #
Valiant 18.1 6 225 105 2.76 3.460000 3.460000 1 0 3 1
...
Or simply
> mtcars2$wt[fit_wt$na.action] <- fit_wt$fitted.values[fit_wt$na.action]
> mtcars2$qsec[fit_qsec$na.action] <- fit_qsec$fitted.values[fit_qsec$na.action]
Additionally, we could round to the given accuracy.
> dlen <- \(x) {
+ ## returns max number of decimal digits
+ max(nchar(gsub(".*\\.", "", na.omit(x))))
+ }
> mtcars2 |>
+ transform(wt=replace(wt, fit_wt$na.action, fit_wt$fitted.values[fit_wt$na.action]) |>
+ round(dlen(mtcars2$qsec)),
+ qsec=replace(qsec, fit_qsec$na.action, fit_qsec$fitted.values[fit_qsec$na.action]) |>
+ round(dlen(mtcars2$wt)))
mpg cyl disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160 110 3.90 2.983 2.620 0 1 4 4 #
Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 2.875 0 1 4 4
Datsun 710 22.8 4 108 93 3.85 2.320 2.320 1 1 4 1
Hornet 4 Drive 21.4 6 258 110 3.08 3.215 3.825 1 0 3 1 #
Hornet Sportabout 18.7 8 360 175 3.15 4.015 3.440 0 0 3 2 #
Valiant 18.1 6 225 105 2.76 3.460 3.460 1 0 3 1
...
Note that in model.frame—used internally by lm—all variables referenced in the formula are included in the data frame, even those preceded by -. So when abbreviating a model as wt ~ . - qsec, the qsec variable is not excluded prior to evaluation:
> lm(wt ~ . - qsec, mtcars2a)$na.action
Mazda RX4 Hornet 4 Drive Hornet Sportabout Merc 280 Chrysler Imperial Pontiac Firebird
1 4 5 10 17 25
attr(,"class")
[1] "omit"
Statistical note: Make sure that you use the appropriate model; in this example, the outcome variable is continuous, so OLS is perfectly fine. However, for binary or count outcomes, you'd need logistic or Poisson regression, respectively.
Data:
set.seed(42)
mtcars2 <- transform(mtcars,
wt=replace(wt, sample.int(nrow(mtcars), nrow(mtcars)*.1), NA),
qsec=replace(wt, sample.int(nrow(mtcars), nrow(mtcars)*.1), NA))