---
title: "Iterative Parameter Estimation"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Iterative Parameter Estimation}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
```

```{r setup}
library(trtswitch)
library(dplyr, warn.conflicts = FALSE)
library(ggplot2)
library(survival)
```

# Introduction
The iterative parameter estimation (IPE) method is an alternative to the rank preserving structural failure time model (RPSFTM) method to adjust for treatment switching within a counterfactual framework. Both methods assume a common treatment effect. However, instead of using g-estimation to find the optimal value of $\psi$, the IPE method iteratively fits a parametric survival model.

# Estimation of $\psi$

With an initial estimate of $\psi$ from the intention-to-treat (ITT) analysis using an accelerated failure time (AFT) model to compare the randomized treatment groups, the IPE method iterates between the following two steps until convergence: 

1. Derive the counterfactual survival times and event indicators (possibly recensored) for patients in the control group, 
\[
  U_{i,\psi} = T_{C_i} + e^{\psi} T_{E_i},
\]
\[
  D_{i,\psi}^* = \min(C_i, e^{\psi} C_i),
\]
\[
  U_{i,\psi}^* = \min(U_{i,\psi}, D_{i,\psi}^*),
\]
\[
  \Delta_{i,\psi}^* = \Delta_i I(U_{i,\psi} \leq D_{i,\psi}^*).
\]

2. Fit an AFT model to the adjusted data set consisting of 

    - The observed survival times of the experimental group:  $\{(T_i,\Delta_i,Z_i): A_i = 1\}$

    - The counterfactual survival times for the control group:  $\{(U_{i,\psi}^*, \Delta_{i,\psi}^*, Z_i): A_i = 0\}$ evaluated at 
$\psi = \hat{\psi}$.

The updated estimate of $\psi$ is equal to the negative of the regression coefficient for the treatment indicator in the AFT model.

# Estimation of Hazard Ratio

This step is the same as the RPSFTM method. Once $\psi$ has been estimated, we can fit a (potentially stratified) Cox proportional hazards model to the adjusted data set. This allows us to obtain an estimate of the hazard ratio. The confidence interval for the hazard ratio can be derived by either 

1. Matching the p-value from the log-rank test for an ITT analysis, or
2. Bootstrapping the entire adjustment and subsequent model-fitting process. 


# Concorde Trial Example
We will demonstrate the use of the `ipe` function with simulated data based on the randomized Concorde trial. 

We start by preparing the data and then apply the IPE method:
```{r analysis}
data <- immdef %>% mutate(rx = 1-xoyrs/progyrs)

fit1 <- ipe(
  data, id = "id", time = "progyrs", event = "prog", treat = "imm", 
  rx = "rx", censor_time = "censyrs", aft_dist = "weibull",
  boot = FALSE)
```

The log-rank test for an ITT analysis, which ignores treatment changes, yields a borderline significant p-value of $0.056$.
```{r logrank}
paste0("P-value", " (", fit1$pvalue_type, "): ", formatC(fit1$pvalue, format = "f", digits = 4))
```

Using the IPE method with a Weibull AFT model, we estimate $\hat{\psi} = -0.183$, which is similar to the estimate obtained from the RPSFTM analysis.   
```{r psi}
fit1$psi
```

The Kaplan-Meier plot of counterfactual survival times supports the estimated $\hat{\psi}$.
```{r km}
ggplot(fit1$kmstar, aes(x=time, y=surv, group=treated,
                        linetype=as.factor(treated))) + 
  geom_step() + 
  scale_linetype_discrete(name = "treated") + 
  scale_y_continuous(limits = c(0,1))
```

The estimated hazard ratio from the Cox proportional hazards model is $0.766$, with a 95\% confidence interval of $(0.583, 1.006)$, closely aligning with the results from the RPSFTM analysis.
```{r hr}
c(fit1$hr, fit1$hr_CI)
```

# Potential Convergence Issues
There is no guarantee that the IPE method will produce an unique estimate of the causal parameter $\psi$. To see this, consider the following SHIVA data for illustration purposes only. 
```{r SHIVA data}
shilong1 <- shilong %>%
  arrange(bras.f, id, tstop) %>%
  group_by(bras.f, id) %>%
  slice(n()) %>%
  select(-c("ps", "ttc", "tran"))

shilong2 <- shilong1 %>%
  mutate(rx = ifelse(co, ifelse(bras.f == "MTA", dco/ady, 
                                1 - dco/ady),
                     ifelse(bras.f == "MTA", 1, 0)),
         treated = 1*(bras.f == "MTA"))
```

Now let us apply the IPE method using the Brent's method for root finding:
```{r SHIVA analysis}
fit2 <- ipe(
  shilong2, id = "id", time = "tstop", event = "event",
  treat = "bras.f", rx = "rx", censor_time = "dcut",
  base_cov = c("agerand", "sex.f", "tt_Lnum", "rmh_alea.c",
               "pathway.f"),
  aft_dist = "weibull", boot = FALSE)
```

The reported causal parameter estimate is $\hat{\psi} = 0.953$, while the negative of the coefficient for the treatment variable in the updated AFT model fit equals $0.950$: 
```{r AFT update}
fit2$fit_aft$parest[, c("param", "beta", "sebeta", "z")]
```

This suggests that the $\psi$ value has not converged. The following code demonstrates the oscillation of $\psi$ values between 0.955, 0.951, and 0.960 after additional iterations.
```{r convergence issue}

f <- function(psi) {
  data1 <- shilong2 %>%
    filter(treated == 0) %>%
    mutate(u_star = tstop*(1 - rx + rx*exp(psi)),
           c_star = pmin(dcut, dcut*exp(psi)),
           t_star = pmin(u_star, c_star),
           d_star = ifelse(c_star < u_star, 0, event)) %>%
    select(-c("u_star", "c_star")) %>%
    bind_rows(shilong2 %>%
                filter(treated == 1) %>%
                mutate(u_star = tstop*(rx + (1-rx)*exp(-psi)),
                       c_star = pmin(dcut, dcut*exp(-psi)),
                       t_star = pmin(u_star, c_star),
                       d_star = ifelse(c_star < u_star, 0, event)))
  
  fit_aft <- survreg(Surv(t_star, d_star) ~ treated + agerand + sex.f +  
                       tt_Lnum + rmh_alea.c + pathway.f, data = data1)  
  -fit_aft$coefficients[2]
}

B <- 30
psihats <- rep(0, B)
psihats[1] <- fit2$psi
for (i in 2:B) {
  psihats[i] <- f(psihats[i-1])
}

data2 <- data.frame(index = 1:B, psi = psihats)
tail(data2)
```

```{r psi plot}
ggplot(data2, aes(x = index, y = psi)) + 
  geom_point() + 
  geom_line()
```



