Skip to content

Commit 36a6363

Browse files
authored
Merge pull request #1 from nt-williams/devel
log and exp operators
2 parents e81b8be + 94d9c7a commit 36a6363

7 files changed

Lines changed: 137 additions & 46 deletions

File tree

DESCRIPTION

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,24 @@
11
Package: ife
22
Type: Package
3-
Title: Influence Function Based Estimate Objects
4-
Version: 0.1.12
3+
Title: Autodiff for Influence Function Based Estimates
4+
Version: 0.1.2
55
Authors@R:
66
c(person(given = "Nicholas",
77
family = "Williams",
88
role = c("aut", "cre", "cph"),
99
email = "ntwilliams.personal@gmail.com",
1010
comment = c(ORCID = "0000-0002-1378-4831")))
1111
Maintainer: Nicholas Williams <ntwilliams.personal@gmail.com>
12-
Description: Implements an S7 class for estimates based on influence functions.
13-
Standard arithmetic operations are defined for the class.
12+
Description: Implements an S7 class for estimates based on influence functions,
13+
with forward mode automatic differentiation defined for standard arithmetic
14+
operations.
1415
License: GPL (>= 3)
1516
Encoding: UTF-8
1617
LazyData: true
1718
Imports:
1819
cli,
1920
generics,
20-
S7
21+
S7 (>= 0.2.0)
2122
RoxygenNote: 7.3.2
2223
Suggests:
2324
testthat (>= 3.0.0)

NAMESPACE

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
export(ife)
44
export(influence_func_estimate)
55
export(tidy)
6-
if (getRversion() < "4.3.0") importFrom(S7,`@`)
76
importFrom(S7,S7_object)
87
importFrom(S7,`method<-`)
98
importFrom(S7,class_character)
@@ -13,8 +12,10 @@ importFrom(S7,new_class)
1312
importFrom(S7,new_generic)
1413
importFrom(S7,new_object)
1514
importFrom(S7,new_property)
15+
importFrom(cli,cli)
1616
importFrom(cli,cli_div)
1717
importFrom(cli,cli_end)
1818
importFrom(cli,cli_text)
19+
importFrom(cli,format_inline)
1920
importFrom(generics,tidy)
2021
importFrom(stats,qnorm)

NEWS.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# ife 0.1.2
2+
3+
* Adding `log` and `exp` methods
4+
* Printing now returns up to 3 digits
5+
16
# ife 0.1.12
27

38
* Conditional `importFrom` statement to fix bug when R version is less than 4.3.0.

R/influence_func_estimand.R

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
#' @importFrom cli cli_div cli_text cli_end
2-
#' @importFrom S7 `@` new_class new_generic new_property new_object S7_object class_double class_character `method<-` class_numeric
3-
#' @rawNamespace if (getRversion() < "4.3.0") importFrom(S7,`@`)
1+
#' @importFrom cli cli cli_div format_inline cli_text cli_end
2+
#' @importFrom S7 new_class new_generic new_property new_object S7_object class_double class_character `method<-` class_numeric
43
#' @importFrom stats qnorm
54
NULL
65

@@ -97,10 +96,14 @@ influence_func_estimate <- new_class("influence_func_estimate",
9796

9897
# print
9998
method(print, influence_func_estimate) <- function(x, ...) {
100-
div <- cli_div(theme = list(.val = list(digits = 2)))
101-
cli_text(cat(" "), "Estimate: {.val {x@x}}")
102-
cli_text(cat(" "), "Std. error: {.val {x@std_error}}")
103-
cli_text("95% Conf. int.: {.val {x@conf_int[1]}}, {.val {x@conf_int[2]}}")
99+
div <- cli_div(theme = list(.val = list(digits = 3)))
100+
cli({
101+
cat(format_inline(" Estimate: {.val {x@x}}\n"))
102+
# cli_text(cat(" "), "Estimate: {.val {x@x}}")
103+
cat(format_inline(" Std. error: {.val {x@std_error}}\n"))
104+
# cli_text(cat(" "), "Std. error: {.val {x@std_error}}")
105+
cli_text("95% Conf. int.: {.val {x@conf_int[1]}}, {.val {x@conf_int[2]}}")
106+
})
104107
cli_end(div)
105108
}
106109

@@ -156,3 +159,13 @@ method(`*`, list(class_numeric, influence_func_estimate)) <- function(e1, e2) {
156159
}
157160

158161
method(`*`, list(influence_func_estimate, class_numeric)) <- function(e1, e2) e2 * e1
162+
163+
# log(x)
164+
method(log, influence_func_estimate) <- function(x, base) {
165+
influence_func_estimate(log(x@x), x@eif / x@x, x@weights, x@id)
166+
}
167+
168+
# exp(x)
169+
method(exp, influence_func_estimate) <- function(x) {
170+
influence_func_estimate(exp(x@x), exp(x@x) * x@eif, x@weights, x@id)
171+
}

README.Rmd

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ output: github_document
55
<!-- README.md is generated from README.Rmd. Please edit that file -->
66

77
```{r, include = FALSE}
8+
options(digits = 3)
9+
810
knitr::opts_chunk$set(
911
collapse = TRUE,
1012
comment = "#>",
@@ -16,45 +18,72 @@ knitr::opts_chunk$set(
1618
# ife
1719

1820
<!-- badges: start -->
21+
[![CRAN status](https://www.r-pkg.org/badges/version/ife)](https://CRAN.R-project.org/package=ife)
1922
<!-- badges: end -->
2023

21-
S7 class (with Ops) for influence function based estimands
24+
S7 class for influence function estimands with forward mode automatic differentiation for variance estimation.
2225

2326
## Installation
2427

25-
You can install the development version of ife from [GitHub](https://github.com/) with:
28+
You can install the development version of *ife* from [GitHub](https://github.com/) with:
2629

2730
``` r
2831
# install.packages("pak")
2932
pak::pak("nt-williams/ife")
3033
```
3134

35+
## Example
36+
37+
Consider estimating the population mean outcome under treatment $E[E[Y \mid A=1,W]]$ and control $E[E[Y \mid A=0,W]]$ using augmented inverse probability weighting (AIPW).
38+
3239
```{r example}
3340
library(ife)
3441
42+
# Generate simulated data
3543
n <- 500
36-
w <- runif(n)
37-
a <- rbinom(n, 1, 0.5)
38-
y <- rbinom(n, 1, plogis(-0.75 + a + w))
44+
w <- runif(n) # confounder
45+
a <- rbinom(n, 1, 0.5) # treatment (randomized)
46+
y <- rbinom(n, 1, plogis(-0.75 + a + w)) # outcome
3947
48+
# Create data-frames for counterfactual predictions
4049
foo <- data.frame(w, a, y)
4150
foo1 <- foo0 <- foo
42-
foo1$a <- 1
43-
foo0$a <- 0
51+
foo1$a <- 1 # everyone treated
52+
foo0$a <- 0 # everyone untreated
4453
45-
pi <- 0.5
54+
# Fit outcome model and generate predictions
55+
pi <- 0.5 # known propensity score
4656
m <- glm(y ~ a + w, data = foo, family = binomial())
57+
Qa <- predict(m, type = "response") # predicted outcomes
58+
Q1 <- predict(m, newdata = foo1, type = "response") # under treatment
59+
Q0 <- predict(m, newdata = foo0, type = "response") # under control
4760
48-
Qa <- predict(m, type = "response")
49-
Q1 <- predict(m, newdata = foo1, type = "response")
50-
Q0 <- predict(m, newdata = foo0, type = "response")
61+
# Calculate un-centered influence functions
62+
if1 <- a / pi * (y - Qa) + Q1
63+
if0 <- (1 - a) / (1 - pi) * (y - Qa) + Q0
64+
```
5165

52-
if1 <- a / pi * (y - Qa) + Q1
53-
if0 <- (1 - a) / pi * (y - Qa) + Q0
66+
Create *ife* objects for these estimates using `influence_func_estimate()` or `ife()`:
5467

68+
```{r}
5569
ife1 <- influence_func_estimate(mean(if1), if1)
5670
ife0 <- ife(mean(if0), if0)
71+
```
72+
73+
*ife* then allows you to estimate contrasts between estimates, with variance estimated using automatic differentiation. The additive effect (risk difference) can be calculated as:
5774

75+
```{r riskdiff}
5876
ife1 - ife0
77+
```
78+
79+
The multiplicative effect (risk ratio) can be estimated as:
80+
81+
```{r}
5982
ife1 / ife0
6083
```
84+
85+
For the risk ratio, which is strictly positive, you can estimate the effect on the log scale and exponentiate the confidence intervals to ensure the lower bound is always positive:
86+
87+
```{r}
88+
exp(log(ife1 / ife0)@conf_int)
89+
```

README.md

Lines changed: 54 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,56 +4,90 @@
44
# ife
55

66
<!-- badges: start -->
7+
8+
[![CRAN
9+
status](https://www.r-pkg.org/badges/version/ife)](https://CRAN.R-project.org/package=ife)
710
<!-- badges: end -->
811

9-
S7 class (with Ops) for influence function based estimands
12+
S7 class for influence function estimands with forward mode automatic
13+
differentiation for variance estimation.
1014

1115
## Installation
1216

13-
You can install the development version of ife from
17+
You can install the development version of *ife* from
1418
[GitHub](https://github.com/) with:
1519

1620
``` r
1721
# install.packages("pak")
1822
pak::pak("nt-williams/ife")
1923
```
2024

25+
## Example
26+
27+
Consider estimating the population mean outcome under treatment
28+
$E[E[Y \mid A=1,W]]$ and control $E[E[Y \mid A=0,W]]$ using augmented
29+
inverse probability weighting (AIPW).
30+
2131
``` r
2232
library(ife)
2333

34+
# Generate simulated data
2435
n <- 500
25-
w <- runif(n)
26-
a <- rbinom(n, 1, 0.5)
27-
y <- rbinom(n, 1, plogis(-0.75 + a + w))
36+
w <- runif(n) # confounder
37+
a <- rbinom(n, 1, 0.5) # treatment (randomized)
38+
y <- rbinom(n, 1, plogis(-0.75 + a + w)) # outcome
2839

40+
# Create data-frames for counterfactual predictions
2941
foo <- data.frame(w, a, y)
3042
foo1 <- foo0 <- foo
31-
foo1$a <- 1
32-
foo0$a <- 0
43+
foo1$a <- 1 # everyone treated
44+
foo0$a <- 0 # everyone untreated
3345

34-
pi <- 0.5
46+
# Fit outcome model and generate predictions
47+
pi <- 0.5 # known propensity score
3548
m <- glm(y ~ a + w, data = foo, family = binomial())
49+
Qa <- predict(m, type = "response") # predicted outcomes
50+
Q1 <- predict(m, newdata = foo1, type = "response") # under treatment
51+
Q0 <- predict(m, newdata = foo0, type = "response") # under control
3652

37-
Qa <- predict(m, type = "response")
38-
Q1 <- predict(m, newdata = foo1, type = "response")
39-
Q0 <- predict(m, newdata = foo0, type = "response")
53+
# Calculate un-centered influence functions
54+
if1 <- a / pi * (y - Qa) + Q1
55+
if0 <- (1 - a) / (1 - pi) * (y - Qa) + Q0
56+
```
4057

41-
if1 <- a / pi * (y - Qa) + Q1
42-
if0 <- (1 - a) / pi * (y - Qa) + Q0
58+
Create *ife* objects for these estimates using
59+
`influence_func_estimate()` or `ife()`:
4360

61+
``` r
4462
ife1 <- influence_func_estimate(mean(if1), if1)
4563
ife0 <- ife(mean(if0), if0)
64+
```
4665

66+
*ife* then allows you to estimate contrasts between estimates, with
67+
variance estimated using automatic differentiation. The additive effect
68+
(risk difference) can be calculated as:
69+
70+
``` r
4771
ife1 - ife0
72+
#> Estimate: 0.254
73+
#> Std. error: 0.042
74+
#> 95% Conf. int.: 0.172, 0.336
4875
```
4976

50-
#> Estimate: 0.15
77+
The multiplicative effect (risk ratio) can be estimated as:
5178

52-
#> Std. error: 0.04
53-
#> 95% Conf. int.: 0.07, 0.24
54-
ife1 / ife0
79+
``` r
80+
ife1 / ife0
81+
#> Estimate: 1.583
82+
#> Std. error: 0.129
83+
#> 95% Conf. int.: 1.33, 1.837
84+
```
5585

56-
#> Estimate: 1.32
86+
For the risk ratio, which is strictly positive, you can estimate the
87+
effect on the log scale and exponentiate the confidence intervals to
88+
ensure the lower bound is always positive:
5789

58-
#> Std. error: 0.11
59-
#> 95% Conf. int.: 1.11, 1.52
90+
``` r
91+
exp(log(ife1 / ife0)@conf_int)
92+
#> [1] 1.35 1.86
93+
```

tests/testthat/test-Ops.R

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,12 @@ describe("operators", {
8484
expect_equal(z@std_error, 0.6581774, tolerance = tol)
8585
})
8686
})
87+
88+
describe("log", {
89+
it("log(ife / ife)", {
90+
z <- log(x / y)
91+
log_eif <- (x@eif / x@x) - (y@eif / y@x)
92+
expect_equal(z@eif, log_eif)
93+
})
94+
})
8795
})

0 commit comments

Comments
 (0)