| Title: | Compare DID, SDID, Matrix Completion and the Triply Robust Panel Estimator |
| Version: | 0.2.1 |
| Description: | A comparison toolkit for binary-treatment panel causal inference. Runs difference-in-differences (DID; two-way fixed effects), synthetic difference-in-differences (SDID), synthetic control (SC), matrix completion (MC), and the Triply RObust Panel (TROP) estimator of Athey, Imbens, Qu and Viviano (2026) <doi:10.1002/jae.70061> on the same data, and returns their average treatment effects on the treated (ATT) on a single tidy schema with shared plots. TROP, DID and matrix completion are implemented natively; SDID and SC are obtained through the 'synthdid' package, and an alternative matrix-completion / interactive fixed-effects estimator through 'gsynth'. The suggested packages 'synthdid' and 'augsynth' are not on CRAN; they can be installed from GitHub at https://github.com/synth-inference/synthdid and https://github.com/ebenmichael/augsynth, and every feature that needs them is skipped with a message when they are absent. This is an unofficial, independent implementation and is not affiliated with or endorsed by the authors of the TROP estimator. |
| License: | MIT + file LICENSE |
| Encoding: | UTF-8 |
| Depends: | R (≥ 4.1) |
| Imports: | ggplot2, stats, utils |
| Suggests: | RSpectra, future, future.apply, patchwork, synthdid, gsynth, augsynth, did, fixest, testthat (≥ 3.0.0), knitr, rmarkdown |
| URL: | https://github.com/takuma1102/cfcompare, https://takuma1102.github.io/cfcompare/ |
| BugReports: | https://github.com/takuma1102/cfcompare/issues |
| VignetteBuilder: | knitr |
| Config/testthat/edition: | 3 |
| Config/testthat/parallel: | true |
| Config/roxygen2/version: | 8.0.0 |
| NeedsCompilation: | no |
| Packaged: | 2026-10-23 23:33:09 UTC; 81809 |
| Author: | Takuma Iwasaki [aut, cre, cph] |
| Maintainer: | Takuma Iwasaki <iwasakit@stanford.edu> |
| Repository: | CRAN |
| Date/Publication: | 2026-11-07 17:10:02 UTC |
cfcompare: Compare DID, SDID, MC and the Triply Robust Panel estimator
Description
cfcompare lets applied researchers run several panel causal-inference estimators
on the same data and compare them on one tidy schema and one plot. It
provides a native R implementation of the Triply RObust Panel (TROP)
estimator of Athey, Imbens, Qu & Viviano (2026), and a comparison harness that
also wraps difference-in-differences (TWFE), matrix completion, and – via the
synthdid package – synthetic difference-in-differences and synthetic
control.
Details
Key functions:
-
trop()– the native TROP estimator. -
panel_compare()– run DID / SDID / MC / TROP together. -
as_att()– coerce external results into the shared schema. -
autoplot.cf_comparison(),plot_counterfactual()– visuals. -
sim_panel()– simulate factor-model panels for experiments.
Author(s)
Maintainer: Takuma Iwasaki iwasakit@stanford.edu [copyright holder]
Authors:
Takuma Iwasaki iwasakit@stanford.edu [copyright holder]
References
Athey, S., Imbens, G. W., Qu, Z., & Viviano, D. (2026). Triply Robust Panel Estimators. Journal of Applied Econometrics. doi:10.1002/jae.70061.
See Also
Useful links:
Report bugs at https://github.com/takuma1102/cfcompare/issues
Coerce estimator output to the cfcompare ATT schema
Description
Brings the output of trop(), panel_compare(), a synthdid estimate,
or any object with an estimate into the common cf_att_tbl schema, so
results computed elsewhere can be slotted into the same comparison and plots.
Usage
as_att(x, ...)
Arguments
x |
An object to tidy. |
... |
Passed to methods (e.g. |
Value
A cf_att_tbl (a data.frame).
Forest plot of ATT estimates across methods
Description
Point estimate and (where available) confidence interval for each method, on a single axis – the quick visual comparison this package is built for.
Usage
## S3 method for class 'cf_comparison'
autoplot(object, ...)
## S3 method for class 'cf_att_tbl'
autoplot(object, ...)
Arguments
object |
A |
... |
Unused. |
Value
A ggplot2 object.
Examples
df <- sim_panel(N = 14, T = 9, n_treated = 3, t0 = 6, seed = 1)
cmp <- panel_compare(df, "y", "w", "id", "t",
methods = c("DID", "MC", "TROP"), se = "none",
control = trop_control(n_cv_cells = 5L, cv_cycles = 1L,
tol = 1e-3, seed = 1L))
autoplot(cmp)
Line plot of estimation RMSE versus a design dimension
Description
One line per estimator; the y axis is on a log scale (as in the paper). Lower is better.
Usage
## S3 method for class 'cf_rmse_curve'
autoplot(object, log_y = TRUE, ...)
Arguments
object |
A |
log_y |
Logical; log10 y axis (default |
... |
Unused. |
Value
A ggplot object.
Autoplot for paired RMSE curves
Description
Autoplot for paired RMSE curves
Usage
## S3 method for class 'cf_rmse_curves'
autoplot(object, combined = FALSE, log_y = TRUE, ...)
Arguments
object |
A |
combined |
|
log_y |
Logical; log10 y axis. |
... |
Unused. |
Value
A list of two ggplots, or one combined ggplot.
Bar chart of out-of-sample RMSE across methods
Description
Visualises a panel_rmse() result as a ranked bar chart (lowest RMSE first),
with +/- 1 standard-error whiskers across placebo runs. This is the
cross-model RMSE comparison from the paper.
Usage
## S3 method for class 'cf_rmse_tbl'
autoplot(object, ...)
Arguments
object |
A |
... |
Unused. |
Value
A ggplot object.
Plot a TROP standard-error comparison
Description
Forest plot of a compare_se_modes() result: every row carries the same TROP
point estimate and only the confidence interval differs between resampling
schemes. Zero is always kept in view and marked with a dashed reference line,
since whether the interval covers zero is the quantity of interest.
Usage
## S3 method for class 'cf_se_comparison'
autoplot(object, ...)
Arguments
object |
A |
... |
Unused. |
Value
A ggplot2 object.
Heatmap of the TROP penalty-sensitivity grid
Description
Cells are coloured by cross-validation loss (darker = better out-of-sample
fit) and annotated with the ATT estimate at that penalty pair; the
CV-selected cell is outlined. The two swept penalties (see the axes
argument of trop_sensitivity()) form the x and y axes, and the fixed
penalty is reported in the subtitle.
Usage
## S3 method for class 'cf_trop_grid'
autoplot(object, ...)
Arguments
object |
A |
... |
Unused. |
Value
A ggplot object.
Synthetic-control-style trajectory plot for a single TROP fit
Description
Draws the treated-unit average observed path against the estimated untreated
(counterfactual) path, in the style of the synthdid plot: a dashed line
marks the last pre-treatment period, the post-treatment gap between the two lines is
the estimated effect, and – since TROP carries explicit time weights – the
time weights \theta_s = \exp(-\lambda_{time} |t - s|) are drawn as a
filled band along the bottom (as in synthdid's time-weight strip) to
show which periods the counterfactual leans on. The band shows weights, not
observation counts; heights are scaled to the largest weight, and with
\lambda_{time} = 0 the weights are uniform and the band is flat.
Usage
## S3 method for class 'trop'
autoplot(object, show_weights = TRUE, show_se = FALSE, ...)
Arguments
object |
A |
show_weights |
Logical; draw the time-weight band along the bottom. |
show_se |
Logical; append the fit's standard-error method to the subtitle.
Defaults to |
... |
Unused. |
Details
This is a point-estimate trajectory: it shows no standard errors or
confidence intervals (the post-treatment gap is left unshaded so it is not
mistaken for a confidence band). To visualise the fit's uncertainty, use
trop_event_study() and call autoplot() on the result, which draws
per-period estimates with pointwise confidence bars. Accordingly the subtitle
reports only the selected penalties by default; set show_se = TRUE to also
note which standard-error method the fit used.
Value
A ggplot object.
See Also
trop_event_study() and its autoplot() method for per-period
effects with confidence bars.
Examples
df <- sim_panel(N = 14, T = 9, n_treated = 3, t0 = 6, att = 3, seed = 1)
autoplot(trop(df, "y", "w", "id", "t", se = "none",
control = trop_control(n_cv_cells = 5L, cv_cycles = 1L,
tol = 1e-3, seed = 1L)))
Event-study plot for a TROP fit
Description
Plots the per-period treatment effects from trop_event_study() against event
time, with pointwise confidence-interval bars, a solid line at zero, and a
dotted line marking treatment onset. When pre-treatment periods are present
they are drawn as distinct placebo / pre-trend points (open markers) so the
pre-period profile can be read at a glance. The SE method is named at the end
of the subtitle.
Usage
## S3 method for class 'trop_event_study'
autoplot(object, ...)
Arguments
object |
A |
... |
Unused. |
Value
A ggplot object.
Examples
df <- sim_panel(N = 16, T = 9, n_treated = 4, t0 = 6, att = 3, seed = 1)
# fixed penalties keep the example fast; omit `lambda` to cross-validate
fit <- trop(df, "y", "w", "id", "t", se = "none",
lambda = list(time = 0.1, unit = 0.5, nn = 2))
autoplot(trop_event_study(fit, se = "bootstrap",
control = trop_control(n_boot = 20L, seed = 1)))
Plot a TROP placebo test
Description
Histogram of the placebo-null ATT distribution with the observed ATT drawn as a solid reference line and zero as a dashed line.
Usage
## S3 method for class 'trop_placebo_test'
autoplot(object, ...)
Arguments
object |
A |
... |
Unused. |
Value
A ggplot2 object.
Stack ATT results into one comparison table
Description
Row-binds several ATT results — trop() fits, panel_compare() results,
synthdid estimates, existing cf_att_tbls, or anything as_att()
understands — into a single cf_att_tbl ready for autoplot() or
plot_counterfactual(). Each argument is passed through as_att() first, so
multi-row inputs (e.g. a panel_compare() result) keep all their rows, and
columns are aligned automatically. This replaces the manual
rbind(as.data.frame(...), ...) + class(...) <- "cf_att_tbl" dance.
Usage
bind_att(...)
Arguments
... |
Objects coercible by |
Details
Names given to the arguments become method labels, overriding the label
already on a single-row result; a name on a multi-row input is ignored with
a warning (it cannot sensibly relabel several methods at once). For per-object
control of other fields such as outcome, coerce that object with as_att()
first and pass the result in unnamed.
Value
A cf_att_tbl (a data.frame) with the inputs stacked row-wise.
See Also
as_att(), panel_compare(), compare_se_modes()
Examples
df <- sim_panel(N = 14, T = 9, n_treated = 3, t0 = 6, att = 2, seed = 1)
lam <- list(time = 0.1, unit = 0.5, nn = 2) # fixed penalties (fast example)
f_pooled <- trop(df, "y", "w", "id", "t", anchor = "pooled",
lambda = lam, se = "none")
f_per_cell <- trop(df, "y", "w", "id", "t", anchor = "per_cell",
lambda = lam, se = "none")
bind_att(pooled = f_pooled, per_cell = f_per_cell)
Compare TROP standard-error methods on one fit
Description
Refits the TROP estimator under several inference methods — the unit-level
block bootstrap and/or the delete-one-unit jackknife (over every unit) —
and returns the estimate, standard error and confidence interval from each on
the common cf_att_tbl schema. The penalties are chosen once (a
single se = "none" fit — by cross-validation, or fixed up front with
lambda) and reused for every inference method, exactly as trop_ablation()
reuses one fit's penalties, so the ATT is
identical across rows and only the uncertainty changes. This is the table to
reach for when checking how sensitive the confidence interval is to the choice
of resampling scheme — it is an inference-mode comparison, not an
estimator comparison, so all rows share one point estimate by construction.
Usage
compare_se_modes(
data,
outcome,
treatment,
unit,
time,
covariates = NULL,
lambda = NULL,
se = c("bootstrap", "jackknife"),
anchor = "pooled",
control = trop_control(),
labels = NULL
)
Arguments
data, outcome, treatment, unit, time, covariates |
Passed to |
lambda |
Optional named list |
se |
Character vector of inference methods to compare; any of
|
anchor |
Weight anchoring, as in |
control |
A |
labels |
Optional |
Value
An object of class cf_se_comparison (extending cf_att_tbl) with one
row per inference method, sharing a common point estimate. If a method does
not apply to the design (for example the bootstrap with a single control
unit) it
still contributes a row, carrying the shared estimate with NA uncertainty
and an explanatory note.
See Also
trop(), trop_ablation(), bind_att()
Examples
df <- sim_panel(N = 16, T = 9, n_treated = 3, t0 = 6, att = 2, seed = 1)
compare_se_modes(df, "y", "w", "id", "t",
control = trop_control(n_cv_cells = 5L, cv_cycles = 1L,
tol = 1e-3, n_boot = 20L,
seed = 1L))
# Fix the penalties (skip cross-validation), as with trop()'s `lambda`:
compare_se_modes(df, "y", "w", "id", "t",
lambda = list(time = 0, unit = 0, nn = Inf),
control = trop_control(n_boot = 20L))
Estimated counterfactual (untreated potential outcome) matrix
Description
Returns a method's estimate of the untreated potential outcome Y(0) as
an N x T matrix on the panel's unit/time grid. This gives every estimator a
common interface, so counterfactuals – and the unit-period prediction RMSE
built on them (panel_rmse() with metric = "prediction") – can be compared
on the same footing.
Usage
counterfactual_matrix(object, ...)
predict_counterfactual(object, ...)
Arguments
object |
A fitted object: a |
... |
Unused; for method extensibility. |
Details
Coverage of the returned matrix differs by method. The native engines
(trop(), and the DID/MC/TROP fits inside panel_compare()) return a fitted
value for every cell. Methods whose model only defines a counterfactual for
the treated units – DIFP, and (when the optional package is installed) SDID,
SC and gsynth – fill the treated rows and leave control cells NA (a
control's untreated outcome is simply its observed value).
predict_counterfactual() is an alias for counterfactual_matrix().
Value
For a single fit, an N x T numeric matrix (with NA where the
method does not define a counterfactual). For a cf_comparison, a named
list of such matrices, one per method.
See Also
panel_rmse(), trop(), panel_compare()
Examples
df <- sim_panel(N = 14, T = 9, n_treated = 3, t0 = 6, seed = 1)
fit <- trop(df, "y", "w", "id", "t",
lambda = list(time = 0.1, unit = 0.5, nn = 2), se = "none")
M <- counterfactual_matrix(fit)
dim(M)
Render a TROP ablation table as paste-ready LaTeX or Markdown
Description
Produces a publication-quality table from a trop_ablation() result. The
"latex" output uses booktabs rules and math-mode penalty headers,
matching the style of the paper's tables (load the booktabs LaTeX package).
Usage
## S3 method for class 'trop_ablation'
format(
x,
output = c("latex", "markdown"),
digits = 3,
caption = "TROP penalty ablation",
label = "tab:trop-ablation",
...
)
Arguments
x |
A |
output |
|
digits |
Number of decimal places. |
caption, label |
Table caption and cross-reference label (LaTeX only). |
... |
Unused. |
Value
A character vector of lines (paste-ready). Combine with
cat(..., sep = "\n") or writeLines().
See Also
trop_ablation()
Examples
df <- sim_panel(N = 14, T = 8, n_treated = 3, t0 = 5, att = 2, seed = 1)
ab <- trop_ablation(df, "y", "w", "id", "t",
lambda_full = list(time = 0.1, unit = 0.5, nn = 2))
writeLines(format(ab, "markdown"))
Compare DID, SDID, MC and TROP on the same panel
Description
Runs a set of panel estimators on a single long panel and returns their average-treatment-effect-on-the-treated (ATT) estimates on a common tidy schema, so applied researchers can compare them at a glance. DID, MC and TROP are computed natively (no external dependencies); SDID and SC are routed through the synthdid package when available, and an alternative MC/IFE can be routed through gsynth. Methods whose optional package is missing, or that do not apply to the design, are skipped with a message.
Usage
panel_compare(
data,
outcome,
treatment,
unit,
time,
covariates = NULL,
methods = c("TROP", "DID", "SC", "MC", "SDID", "DIFP"),
exclude = NULL,
anchor = "auto",
se = c("auto", "jackknife", "bootstrap", "none"),
control = trop_control(),
verbose = FALSE
)
Arguments
data |
A long |
outcome, treatment, unit, time |
Column names (strings). |
covariates |
Optional character vector of time-varying covariate column
names. Passed to the native engines ( |
methods |
Character vector of methods to run. Any of |
exclude |
Optional character vector of methods to drop from |
anchor |
Weight anchoring for TROP; see |
se |
Standard-error method for the native engines; see |
control |
A list of solver/CV settings from |
verbose |
Logical; print CV progress. |
Value
An object of class cf_comparison: a list with the tidy table
att (class cf_att_tbl), per-method counterfactual matrices
counterfactual, the native fit objects fits, and the reshaped panel.
See Also
trop(), autoplot.cf_comparison(), plot_counterfactual()
Examples
# panel_compare() fits TROP with cross-validated penalties, so it is wrapped
# in \donttest; few CV cells and a loose solver tolerance keep it quick
# (use the trop_control() defaults for real analyses)
df <- sim_panel(N = 14, T = 9, n_treated = 3, t0 = 6, seed = 3)
cmp <- panel_compare(df, "y", "w", "id", "t",
methods = c("DID", "MC", "TROP"), se = "none",
control = trop_control(n_cv_cells = 5L, cv_cycles = 1L,
tol = 1e-3, seed = 1L))
cmp$att
Reshape a long panel into TROP matrices
Description
Converts a long panel data.frame into the N x T outcome (Y) and
treatment (W) matrices used by trop_matrix(), and reports the treated rows
and the width of the treated block. It is the data-frame-to-matrix companion
to trop_matrix(): rather than building Y, W, treated_units and
treated_periods by hand, call panel_matrices() and pass the pieces
straight through.
Usage
panel_matrices(data, outcome, treatment, unit, time)
Arguments
data |
A long |
outcome, treatment, unit, time |
Column names (strings). |
Value
A list with components:
YN x Toutcome matrix (rows = units, columns = times).WN x T0/1 treatment matrix on the same grid.units,timesthe sorted unit and time labels.
treated_unitsinteger row indices of units with any treated cell.
treated_periodsnumber of periods in which any treated unit is active, i.e. the width of the post block.
See Also
Examples
df <- sim_panel(N = 14, T = 9, n_treated = 3, t0 = 6, att = 2, seed = 1)
pm <- panel_matrices(df, "y", "w", "id", "t")
str(pm)
# Feed straight into the matrix-in entry point (treated rows/periods inferred).
trop_matrix(pm$Y, pm$W, lambda_unit = 0.1, lambda_time = 0.1, lambda_nn = Inf)
Out-of-sample RMSE across panel estimators
Description
Compares estimators by how well each does on held-out control data, following the "random blocks" placebo idea of the doubly/triply robust panel estimator paper. Two scoring rules are available:
Usage
panel_rmse(
data,
outcome,
treatment,
unit,
time,
methods = c("DID", "SC", "SDID", "MC", "TROP", "DIFP"),
exclude = NULL,
metric = c("placebo", "prediction"),
horizon = 10L,
n_pseudo = 10L,
n_runs = 10L,
control = trop_control(),
seed = NULL,
verbose = FALSE
)
Arguments
data |
A long |
outcome, treatment, unit, time |
Column names (strings). |
methods |
Methods to compare; subset of |
exclude |
Optional character vector of methods to drop from |
metric |
|
horizon |
Number of final periods held out per placebo cohort. |
n_pseudo |
Number of placebo (pseudo-treated) control units per run. |
n_runs |
Number of placebo runs to average over. |
control |
A list of solver/CV controls from |
seed |
Optional integer seed for reproducible placebo draws. |
verbose |
Logical; print progress. |
Details
-
metric = "placebo"(default): in each run a random set of control units is given a placebo block treatment in the finalhorizonperiods (true effect zero), every method is estimated on that control-only panel, and the score issqrt(mean(ATT^2))across runs. This works for every method – native (DID, MC, TROP) and wrapped (SDID/SCvia synthdid,gsynth,augsynth,CS= Callaway & Sant'Anna via did). -
metric = "prediction": per-cell one-step-ahead held-out RMSE (the paper's Table-1 unit-period counterfactual prediction error). A block of control cells is held out and each method predicts the untreated outcome there via its counterfactual matrix (counterfactual_matrix()). Supported for the native methods (DID,MC,TROP), forDIFP, and – when synthdid is installed – forSDIDandSC. The remaining wrapped methods (gsynth,augsynth,CS) are reported asNAfor this metric; usemetric = "placebo"for them.
Lower is better. Native methods are tuned once on the real data (DID is
parameter-free; MC selects lambda_nn; TROP selects the full triplet by
cross-validation) and reused across runs. Wrapped methods are skipped with a
note when their package is missing or the design does not apply.
Value
A cf_rmse_tbl (a data.frame) with one row per method and columns
method, rmse, rmse_se, n_runs, engine, note.
References
Athey, S., Imbens, G. W., Qu, Z., & Viviano, D. (2025/2026). Triply/Doubly Robust Panel Estimators.
See Also
panel_compare(), autoplot.cf_rmse_tbl()
Examples
df <- sim_panel(N = 14, T = 9, n_treated = 3, t0 = 6, att = 2, seed = 1)
r <- panel_rmse(df, "y", "w", "id", "t",
methods = c("DID", "TROP"),
horizon = 2, n_pseudo = 3, n_runs = 2,
control = trop_control(n_cv_cells = 5L, cv_cycles = 1L,
tol = 1e-3, seed = 1L),
seed = 1)
r
autoplot(r)
Plot one or both estimation-RMSE curves
Description
Plot one or both estimation-RMSE curves
Usage
## S3 method for class 'cf_rmse_curve'
plot(x, log_y = TRUE, file = NULL, width = 8, height = 5, ...)
## S3 method for class 'cf_rmse_curves'
plot(
x,
combined = FALSE,
log_y = TRUE,
file = NULL,
width = NULL,
height = NULL,
...
)
Arguments
x |
A |
log_y |
Logical; log10 y axis (default |
file |
Optional path. If given, the figure is written to a PNG at
|
width, height |
Figure size in inches. Defaults are deliberately generous (single 8x5; combined 12x5). |
... |
Unused. |
combined |
For |
Value
The input, invisibly.
Plot observed vs predicted counterfactual trajectories
Description
For each native engine in a comparison (DID, MC, TROP), draws the average outcome over the treated units against the predicted control counterfactual, to show how the methods extrapolate through the post-treatment period.
Usage
plot_counterfactual(x, methods = NULL)
Arguments
x |
A |
methods |
Optional subset of methods to draw. |
Value
A ggplot2 object.
Base-R surface views of a TROP penalty-sensitivity grid
Description
Draws the cross-validation-loss surface and the ATT surface from a
trop_sensitivity() grid as two separate, full-width base-graphics plots,
each with a colour key (legend) on the right. Unlike autoplot.cf_trop_grid()
— a single ggplot2 heatmap that packs both into one panel — this view
keeps the surfaces apart so neither is squeezed, and needs no ggplot2. The
surface axes follow the swept penalties recorded on the grid (see the axes
argument of trop_sensitivity()).
Usage
plot_trop_surfaces(
grid,
which = c("both", "cv_loss", "att"),
ask = interactive(),
...
)
Arguments
grid |
A |
which |
Which surface(s) to draw: |
ask |
Logical; when drawing both on an interactive device, prompt before
the second figure. Defaults to |
... |
Further arguments passed to |
Value
Invisibly, a list with the cv_loss and att surface matrices
(rows = y-axis penalty, columns = x-axis penalty).
See Also
trop_sensitivity(), autoplot.cf_trop_grid()
Examples
df <- sim_panel(N = 14, T = 9, n_treated = 3, t0 = 6, att = 2, seed = 1)
g <- trop_sensitivity(df, "y", "w", "id", "t",
lambda_time = c(0, 0.25, 1),
control = trop_control(n_cv_cells = 5L, cv_cycles = 1L,
tol = 1e-3, seed = 1L))
plot_trop_surfaces(g, which = "cv_loss")
Objects exported from other packages
Description
These objects are imported from other packages. Follow the links below to see their documentation.
- ggplot2
Estimation-RMSE curve over one design dimension
Description
Sweeps one design dimension – the number of control units ("n_control"),
pre-treatment periods ("n_pre"), treated units ("n_treated") or
post-treatment periods ("n_post") – and, at each value, runs a
semi-synthetic Monte Carlo: panels are drawn from a latent factor model in
which treatment is selected on the factor loadings (so plain DID/TWFE is
biased), a known constant effect att is imposed, every estimator is run, and
the estimation RMSE against the known truth is recorded. The result is the
data behind the paper-style "RMSE vs. N_control / T_pre" line plot, one line
per estimator. With a dense values grid and enough n_runs the curves are
smooth, as in the paper.
Usage
rmse_curve(
vary = c("n_control", "n_pre", "n_treated", "n_post"),
values = NULL,
n_runs = 500L,
methods = c("DID", "SDID", "SC", "MC", "DIFP", "TROP"),
exclude = NULL,
n_control = 60L,
n_treated = 8L,
n_pre = 16L,
n_post = 6L,
rank = 4L,
att = 2,
noise = 1,
ar = 0.4,
trend_sd = 0.05,
anchor = "pooled",
control = trop_control(),
seed = 1L,
parallel = FALSE,
progress = interactive(),
verbose = FALSE
)
Arguments
vary |
Which dimension to sweep: |
values |
Integer vector of values for the swept dimension. Defaults to
|
n_runs |
Monte Carlo replications per value (higher = smoother; the paper
uses ~1000). Default 500. Note: with the dense default grid and six
estimators this is a large simulation (thousands of fits per panel); reduce
|
methods |
Estimators to include; any subset of the |
exclude |
Optional character vector of methods to drop from |
n_control, n_treated, n_pre, n_post |
Base design; the dimension named in
|
rank, att, noise |
Number of latent factors, the imposed (true) ATT, and the idiosyncratic-noise scale. |
ar |
AR(1) coefficient of the idiosyncratic errors (serial correlation). |
trend_sd |
SD of the heterogeneous unit-specific linear trend slopes (non-parallel trends); treatment is also selected on this slope. |
anchor |
Estimation anchor for the native ATT ( |
control |
Solver/CV controls from |
seed |
Base integer seed (each replication uses a distinct offset). |
parallel |
Logical; if |
progress |
Logical; print a rough progress indicator (completed fits, a
percentage, elapsed time and an ETA) periodically while the curve runs.
Helpful for the heavy large- |
verbose |
Logical; additionally print a line as each swept value
finishes. Independent of |
Details
This is a simulation diagnostic on synthetic data; it does not take a user
panel. To run a curve on your own data, draw panels with sim_semisynthetic()
and call panel_compare() in a loop.
Value
A cf_rmse_curve (a data.frame) with columns method, x
(the swept value), rmse, bias, n_runs. The swept-dimension label is in
attr(., "vary").
See Also
rmse_curves(), panel_rmse(), autoplot.cf_rmse_curve()
Examples
# quick look (small grid + few reps); raise n_runs/values for paper quality
cc <- rmse_curve("n_control", values = c(12, 16), n_runs = 1, n_pre = 6, n_post = 3,
n_treated = 2, methods = c("DID", "TROP"),
control = trop_control(n_cv_cells = 5L, cv_cycles = 1L,
tol = 1e-3, seed = 1L))
autoplot(cc)
Estimation-RMSE curves over both design dimensions
Description
Convenience wrapper that runs rmse_curve() for both the number of control
units and the number of pre-treatment periods and bundles the two curves, so
they can be plotted individually (default) or side by side (combined = TRUE),
as in the paper's two-panel figure.
Usage
rmse_curves(values_control = NULL, values_pre = NULL, ...)
Arguments
values_control, values_pre |
Optional grids for each sweep (see
|
... |
Passed to |
Value
A cf_rmse_curves object: a list with $n_control and $n_pre,
each a cf_rmse_curve.
See Also
rmse_curve(), autoplot.cf_rmse_curves()
Examples
# defaults are a large simulation; use a small grid + few reps for a quick look
g <- rmse_curves(values_control = c(12, 16), values_pre = c(5, 8), n_post = 3,
n_treated = 2, n_runs = 1, methods = c("DID", "TROP"),
control = trop_control(n_cv_cells = 5L, cv_cycles = 1L,
tol = 1e-3, seed = 1L))
plot(g) # two separate figures (default)
# combined = TRUE needs the optional 'patchwork' package:
if (requireNamespace("patchwork", quietly = TRUE))
plot(g, combined = TRUE) # side-by-side, paper-style
CV-selected penalties from a TROP penalty-sensitivity grid
Description
Returns the cross-validation-selected penalties of a trop_sensitivity()
grid — the grid cell minimising the CV loss — as a list(time=, unit=, nn=), the same shape trop() accepts for its lambda argument. So the
data-driven choice on a grid can be fed straight back into a fit, e.g.
trop(..., lambda = selected_lambda(g)), without digging the values out of
attr(g, "selected") by hand.
Usage
selected_lambda(grid)
Arguments
grid |
A |
Value
A named list with the selected time, unit and nn penalties. The
full selected row (including the ATT and CV loss there) remains available in
attr(grid, "selected").
See Also
trop_sensitivity(), surface_matrix(), trop()
Examples
df <- sim_panel(N = 14, T = 9, n_treated = 3, t0 = 6, att = 2, seed = 1)
g <- trop_sensitivity(df, "y", "w", "id", "t",
lambda_time = c(0, 0.25, 1),
control = trop_control(n_cv_cells = 5L, cv_cycles = 1L,
tol = 1e-3, seed = 1L))
selected_lambda(g)
Simulate a panel from a low-rank factor model
Description
Generates a long panel whose untreated potential outcomes follow an
interactive-fixed-effects (factor) model on top of two-way fixed effects,
in the spirit of the data-generating processes in Athey, Imbens, Qu &
Viviano (2026). A block treatment is applied to n_treated units from period
t0 onward, with a constant additive effect att.
Usage
sim_panel(
N = 30,
T = 20,
n_treated = 5,
t0 = NULL,
rank = 3L,
att = 1,
noise = 0.5,
seed = NULL,
n_cov = 0L,
phi = NULL,
confounding = 0
)
Arguments
N |
Number of units. |
T |
Number of periods. |
n_treated |
Number of treated units. |
t0 |
First treated period (block design). |
rank |
Number of latent factors. |
att |
True treatment effect added to treated cells. |
noise |
Standard deviation of idiosyncratic noise. |
seed |
Optional RNG seed. |
n_cov |
Number of time-varying covariates to generate (default |
phi |
Optional numeric coefficient vector for the covariates (length
|
confounding |
Non-negative strength of selection-on-confounders in the
treatment assignment. With |
Value
A long data.frame with columns id, t, y, w, and the noiseless
counterfactual y0 (useful for evaluating estimators). When n_cov > 0 it
additionally carries covariate columns x1, ..., x{n_cov}, and the true
coefficients are stored on attr(., "phi").
Examples
df <- sim_panel(N = 16, T = 9, n_treated = 3, t0 = 6, seed = 42)
head(df)
# Two covariates with known coefficients, recoverable by trop(covariates=):
dc <- sim_panel(N = 16, T = 9, n_treated = 3, t0 = 6,
n_cov = 2, phi = c(1.2, -0.7), seed = 1)
attr(dc, "phi")
Build a semi-synthetic panel from real data
Description
Takes a real long panel, uses its outcomes (optionally smoothed through a
low-rank-plus-two-way-fixed-effects fit) as the untreated potential outcomes
Y(0), and imposes a known treatment effect on a chosen block of units and
periods. Because the baseline is real data but the effect is known, the result
is a ground-truth benchmark that "closely matches" the real setting – the
style of semi-synthetic experiment used to evaluate panel estimators in
Athey, Imbens, Qu & Viviano (2026). Pair it with panel_compare() or
panel_rmse() to score estimators against the truth.
Usage
sim_semisynthetic(
data,
outcome,
unit,
time,
n_treated,
t0 = NULL,
att = 1,
effect = NULL,
baseline = c("observed", "lowrank"),
lambda_nn = NULL,
noise = 0,
seed = NULL
)
Arguments
data |
A real long |
outcome, unit, time |
Column names (strings). |
n_treated |
Number of units to assign to the placebo treated group (sampled at random from all units). |
t0 |
First treated period (block design). Defaults to about three quarters of the way through the panel. |
att |
Constant additive treatment effect imposed on treated cells. Ignored
if |
effect |
Optional per-treated-period effect: a single number, or a numeric
vector of length |
baseline |
|
lambda_nn |
Nuclear-norm penalty for the |
noise |
For |
seed |
Optional RNG seed. |
Value
A long data.frame with columns id, t, y, w, y0 (the imposed
untreated potential outcome) and tau (the true effect, 0 off treatment).
See Also
sim_panel(), panel_compare(), panel_rmse()
Examples
real <- sim_panel(N = 16, T = 10, n_treated = 0L, att = 0, seed = 1)
ss <- sim_semisynthetic(real, "y", "id", "t",
n_treated = 6, t0 = 8, att = 3, seed = 2)
mean(ss$tau[ss$w == 1]) # true ATT = 3
Surface matrix from a TROP penalty-sensitivity grid
Description
Reshapes a long trop_sensitivity() grid into a wide matrix of one quantity
— the cross-validation loss or the ATT — laid out over the two swept
penalties (see the axes argument of trop_sensitivity()). Rows are the
y-axis penalty, columns the x-axis penalty; the matrix dimnames are named
after the two penalty columns so the layout is self-describing. This is the
same reshape plot_trop_surfaces() performs internally, exposed so the
surfaces can be inspected or post-processed without re-deriving them with
xtabs().
Usage
surface_matrix(grid, value = c("cv_loss", "att"))
Arguments
grid |
A |
value |
Which quantity to lay out: |
Value
A numeric matrix (y-axis penalty in rows, x-axis penalty in columns).
See Also
trop_sensitivity(), selected_lambda(), plot_trop_surfaces()
Examples
df <- sim_panel(N = 14, T = 9, n_treated = 3, t0 = 6, att = 2, seed = 1)
g <- trop_sensitivity(df, "y", "w", "id", "t",
lambda_time = c(0, 0.25, 1),
control = trop_control(n_cv_cells = 5L, cv_cycles = 1L,
tol = 1e-3, seed = 1L))
surface_matrix(g, "cv_loss")
surface_matrix(g, "att")
Triply RObust Panel (TROP) estimator
Description
Fits the TROP estimator of Athey, Imbens, Qu & Viviano (2026) on a long panel. TROP combines a low-rank-plus-two-way-fixed-effects outcome model with exponential-decay unit weights (upweighting controls similar to the treated) and time weights (upweighting periods near the treated periods). Penalties are chosen by leave-one-out cross-validation on the control cells. The estimator nests DID/TWFE, matrix completion and synthetic-control-type weighting as special cases.
Usage
trop(
data,
outcome,
treatment,
unit,
time,
covariates = NULL,
lambda = NULL,
standardize = FALSE,
anchor = c("auto", "per_cell", "pooled"),
se = c("bootstrap", "auto", "jackknife", "none"),
grids = NULL,
control = trop_control(),
verbose = FALSE
)
Arguments
data |
A long |
outcome, treatment, unit, time |
Column names (strings). |
covariates |
Optional character vector of column names for time-varying
covariates. When supplied, the low-rank term is augmented additively as
|
lambda |
Optional named list |
standardize |
Logical; if |
anchor |
How weights are anchored to treated cells: |
se |
Standard-error method: |
grids |
Optional list of penalty grids; see |
control |
A list of solver/CV settings from |
verbose |
Logical; print CV progress. |
Value
An object of class trop: a list with the ATT estimate,
std.error, conf.low/conf.high, the selected penalties lambda,
any covariate coefficients phi, per-cell effects (tau_cells), the
per-spell-group effects tau_groups (pooled anchor only: one row per
group with its start/end period, unit and cell counts and estimate;
the ATT is their n_cells-weighted average), the estimated
counterfactual matrix, the reshaped panel, and scaling (the outcome
centre/scale used internally; the identity unless standardize = TRUE).
All effect and outcome-level components are on the raw outcome scale;
panel$Y and lambda are on the fitting scale.
References
Athey, S., Imbens, G. W., Qu, Z., & Viviano, D. (2026). Triply Robust Panel Estimators. Journal of Applied Econometrics. doi:10.1002/jae.70061.
Examples
df <- sim_panel(N = 14, T = 9, n_treated = 3, t0 = 6, seed = 1)
# fixed penalties keep this example fast and deterministic; omit `lambda`
# to choose (time, unit, nn) by cross-validation
fit <- trop(df, "y", "w", "id", "t",
lambda = list(time = 0.1, unit = 0.5, nn = 2), se = "none")
fit
Penalty ablation for the TROP estimator
Description
Refits TROP under a sequence of penalty constraints to show how the ATT moves
as the estimator is stripped back towards matrix completion and
difference-in-differences. This is the robustness exercise behind Table 5 of
Athey, Imbens, Qu & Viviano (2026): constraining lambda_unit and/or
lambda_time to zero (dropping unit/time weights) and/or lambda_nn to
infinity (dropping the low-rank regression adjustment).
Usage
trop_ablation(
data,
outcome,
treatment,
unit,
time,
covariates = NULL,
control = trop_control(),
anchor = "pooled",
se = "none",
lambda_full = NULL,
rmse = FALSE,
horizon = NULL,
n_pseudo = NULL,
n_runs = 200L
)
## S3 method for class 'trop_ablation'
print(x, digits = 3, ...)
## S3 method for class 'trop_ablation'
plot(x, file = NULL, width = NULL, height = NULL, res = 200, digits = 3, ...)
Arguments
data, outcome, treatment, unit, time, covariates |
Passed to |
control |
A |
anchor |
Weight anchoring, as in |
se |
Standard-error method for every specification (default |
lambda_full |
Optional named list |
rmse |
Logical; if |
horizon, n_pseudo |
Placebo design used when |
n_runs |
Number of placebo draws when |
x |
A |
digits |
Number of decimal places for the penalties and estimates. |
... |
Unused. |
file |
Optional output path. When supplied, the table is rendered to that
file instead of the active graphics device; the format is taken from the
extension ( |
width, height |
Figure size in inches. Defaults adapt to the number of rows and columns. |
res |
Resolution in PPI for the |
Details
The full specification's penalties are taken from a single cross-validated
trop() fit; every constrained specification then reuses those values where
they are not constrained, so the rows are directly comparable. The result is a
table object: print it for a clean console table, or format() it to
paste-ready LaTeX (booktabs) or Markdown. It is also an ordinary
data.frame, so it can be passed to knitr::kable(), gt::gt(), etc.
Value
An object of class trop_ablation (a data.frame) with one row per
specification: spec, the three penalties lambda_time/lambda_unit/
lambda_nn, estimate, std.error, conf.low/conf.high, and rank.
When rmse = TRUE it also has rmse and rmse_rel (RMSE relative to the
full TROP).
References
Athey, S., Imbens, G. W., Qu, Z., & Viviano, D. (2026). Triply Robust Panel Estimators. Journal of Applied Econometrics. doi:10.1002/jae.70061.
See Also
trop(); format() for paste-ready LaTeX/Markdown output
Examples
df <- sim_panel(N = 14, T = 8, n_treated = 3, t0 = 5, att = 2, seed = 1)
# fixed base penalties keep the example fast; omit `lambda_full` to select
# them by cross-validation
ab <- trop_ablation(df, "y", "w", "id", "t",
lambda_full = list(time = 0.1, unit = 0.5, nn = 2))
ab # clean console table
cat(format(ab, "latex"), sep = "\n") # paste-ready LaTeX (booktabs)
Control settings for trop()
Description
Control settings for trop()
Usage
trop_control(
max_iter = 2000L,
tol = 1e-06,
n_cv_cells = 120L,
cv_cycles = 2L,
max_cells = 60L,
time_center = c("midpoint", "python"),
pooled_treat_distance = NULL,
conf_level = 0.95,
n_boot = 200L,
boot_resample = c("stratified", "controls"),
boot_ci = c("percentile", "normal"),
svd = c("truncated", "full"),
cv_method = c("loocv", "placebo"),
n_placebo = 30L,
nn_floor = "auto",
nn_floor_scale = 0.001,
workers = 1L,
seed = NULL
)
Arguments
max_iter |
Maximum solver iterations. Higher by default because small
|
tol |
Reference solver convergence tolerance. The applied tolerance is
tightened automatically as |
n_cv_cells |
Number of control cells sampled for the CV criterion. Set
to |
cv_cycles |
Number of coordinate-descent cycles in penalty selection. |
max_cells |
Threshold for |
time_center |
Where the pooled time weights
|
pooled_treat_distance |
Optional overrides for the distances of a
pooled group to itself, as a list with elements |
conf_level |
Confidence level for intervals. |
n_boot |
Number of replications for the bootstrap standard error
( |
boot_resample |
Which units the bootstrap resamples: |
boot_ci |
Bootstrap confidence-interval type: |
svd |
Singular-value decomposition used by the soft-impute solver:
|
cv_method |
Penalty cross-validation criterion. |
n_placebo |
Number of placebo assignments drawn when
|
nn_floor |
Small non-negative stabilising constant added to every
finite |
nn_floor_scale |
Relative constant used when |
workers |
Number of parallel workers for the embarrassingly parallel
loops (cross-validation cells, and the bootstrap / jackknife
replicates). |
seed |
Optional seed for CV cell sampling (reproducibility). |
Value
A list of control parameters.
Per-period (event-study) effects from a TROP fit
Description
Decomposes a fitted trop() ATT into per-period treatment effects indexed by
event time (periods relative to each unit's first treated period) and attaches
pointwise standard errors and confidence intervals. The same resampling used
for the overall SE is reused: each resample's per-cell effects are aggregated
by event time rather than into a single mean, so no extra modelling
assumptions are introduced. With pre_periods = TRUE the pre-treatment gaps
are returned as placebo / pre-trend points (a flat, near-zero pre-period
profile supports the design).
Usage
trop_event_study(
object,
se = c("bootstrap", "jackknife", "none"),
pre_periods = TRUE,
control = NULL,
...
)
Arguments
object |
A |
se |
Standard-error method for the per-period effects: |
pre_periods |
Logical; include pre-treatment event times as placebo /
pre-trend points (default |
control |
A list of solver/CV/bootstrap settings from |
... |
Unused. |
Details
Because the treated composition varies across bootstrap resamples and each event time draws on relatively few cells, the per-period intervals are wider than the overall ATT interval and cover each point individually (they are not simultaneous bands).
Value
An object of class trop_event_study: a list whose estimates is a
data.frame with one row per event time (event_time, estimate,
std.error, conf.low, conf.high, n_cells, period), plus the overall
att, the se.method, and metadata.
References
Athey, S., Imbens, G. W., Qu, Z., & Viviano, D. (2026). Triply Robust Panel Estimators. Journal of Applied Econometrics. doi:10.1002/jae.70061.
See Also
autoplot.trop_event_study() to plot the result.
Examples
df <- sim_panel(N = 16, T = 9, n_treated = 4, t0 = 6, att = 3, seed = 1)
# fixed penalties keep the example fast; omit `lambda` to cross-validate
fit <- trop(df, "y", "w", "id", "t", se = "none",
lambda = list(time = 0.1, unit = 0.5, nn = 2))
es <- trop_event_study(fit, se = "bootstrap", pre_periods = TRUE,
control = trop_control(n_boot = 20L, seed = 1))
es
TROP estimate on matrix input
Description
A thin, matrix-in wrapper around the TROP working model, written independently
from the paper. The matrix-in form is convenient for numerically comparing
trop() against other matrix-based implementations on identical inputs. For
data-frame input, cross-validated penalty selection, inference and the
multi-estimator comparison, use trop() and panel_compare() instead.
Usage
trop_matrix(
Y,
W,
treated_units = NULL,
lambda_unit,
lambda_time,
lambda_nn,
treated_periods = NULL,
control = trop_control()
)
Arguments
Y |
N x T outcome matrix. |
W |
N x T 0/1 treatment matrix (1 = actively treated cell). |
treated_units |
Integer row indices of the treated units, used to anchor
the unit weights. Defaults to |
lambda_unit, lambda_time |
Non-negative decay parameters for the unit and time weights. |
lambda_nn |
Nuclear-norm penalty; use |
treated_periods |
Number of final columns treated as the post block, used
to build the pre-period mask and the time-distance centre. Defaults to
|
control |
A list of solver controls from |
Details
The untreated outcome model is fitted on the control cells with the supplied
weights, and the returned effect is the average over treated cells of
Y - alpha - beta - L, exactly as in the paper's eq. (2). With
lambda_nn = Inf the low-rank term is dropped and the fit is weighted two-way
fixed effects, matching the reference to numerical tolerance. With a finite
lambda_nn the nuclear-norm term is solved by this package's
proximal-gradient / soft-impute routine, which targets the same convex program
as eq. (2) (loss sum_js w (Y - alpha - beta - L)^2, with no 1/2 factor);
lambda_nn is therefore on the same scale as the official Python and Stata
packages, and finite-penalty results agree with them to solver tolerance.
trop_matrix() always uses the pooled weighting scheme anchored to the centre
of the treated block: it takes no anchor argument and has no per-cell mode.
It therefore computes the same pooled, block-centre estimator as the official
Python package's TROP_TWFE_average (implemented independently), and exists as
a like-for-like matrix-in cross-check against that pooled reference.
trop(..., anchor = "pooled") uses the same convention (unit distances to the
average treated trajectory, time distances to the centre of the treated
block), so on a trailing-block design the two agree to solver tolerance for
the same penalties. For the per-cell estimator (the paper's eq. (12) /
Algorithm 2), use trop(..., anchor = "per_cell"), which shares the
data-frame core .trop_att().
Value
A single numeric ATT estimate.
References
Athey, S., Imbens, G. W., Qu, Z., & Viviano, D. (2026). Triply Robust Panel Estimators. Journal of Applied Econometrics. doi:10.1002/jae.70061.
See Also
Examples
df <- sim_panel(N = 14, T = 9, n_treated = 3, t0 = 6, att = 2, seed = 1)
Y <- matrix(0, max(df$id), max(df$t)); W <- Y
for (k in seq_len(nrow(df))) { Y[df$id[k], df$t[k]] <- df$y[k]
W[df$id[k], df$t[k]] <- df$w[k] }
tu <- which(rowSums(W) > 0)
trop_matrix(Y, W, tu, 0.1, 0.1, Inf, treated_periods = 4)
# treated_units and treated_periods are inferred from W when omitted, so the
# matrices produced by panel_matrices() can be fed in directly.
pm <- panel_matrices(df, "y", "w", "id", "t")
trop_matrix(pm$Y, pm$W, lambda_unit = 0.1, lambda_time = 0.1, lambda_nn = Inf)
Placebo (randomisation) test for a TROP ATT
Description
Assesses whether a fitted trop() ATT is distinguishable from zero by
comparing it to a placebo null distribution. Control units are repeatedly
relabelled as pseudo-treated — using the same number of treated units and
the same treatment timing as the real design, so block, staggered and
non-absorbing patterns are all reproduced faithfully — and the estimator is
refit at the fit's cross-validated penalties (the true effect is zero by
construction). The observed ATT is then placed against that null.
Usage
trop_placebo_test(
object,
B = 500L,
alternative = c("two.sided", "greater", "less"),
control = NULL
)
## S3 method for class 'trop_placebo_test'
print(x, digits = 4, ...)
Arguments
object |
A |
B |
Number of placebo assignments to draw. |
alternative |
Direction of the test: |
control |
A |
x |
A |
digits |
Number of significant digits to print. |
... |
Unused. |
Details
This is a randomisation/permutation test, not a standard error: it answers
"could a TROP design like mine produce an effect this large when there is none?"
For interval estimates use the bootstrap or jackknife (see trop() and
compare_se_modes()).
Value
An object of class trop_placebo_test (a list) with the observed
ATT, the vector of placebo ATTs, the p.value, the placebo-null mean
and sd, a central null interval null.low/null.high, alternative,
B, and n_treated_units. It has print() and autoplot() methods.
See Also
Examples
df <- sim_panel(N = 16, T = 9, n_treated = 3, t0 = 6, att = 3, seed = 1)
# fixed penalties keep the example fast; omit `lambda` to cross-validate
fit <- trop(df, "y", "w", "id", "t", se = "none",
lambda = list(time = 0.1, unit = 0.5, nn = 2))
pt <- trop_placebo_test(fit, B = 30)
pt
TROP penalty-sensitivity grid (heatmap data)
Description
Sweeps any two of the three TROP penalties — \lambda_{time},
\lambda_{unit} and \lambda_{nn} — against each other while
holding the third fixed, and records at each grid point both the resulting
ATT estimate and the leave-one-out cross-validation loss. This is the data
behind the diagnostic heatmap: colour the cells by CV loss to see where the
data-driven choice lands, and read the ATT off each cell to judge how
sensitive the estimate is to the penalties. Use axes to choose which two
penalties go on the x and y axes; the remaining (fixed) penalty is reported
in the heatmap subtitle.
Usage
trop_sensitivity(
data,
outcome,
treatment,
unit,
time,
lambda_time = NULL,
lambda_nn = NULL,
lambda_unit = NULL,
axes = c("nn", "time"),
anchor = "pooled",
control = trop_control(),
seed = NULL,
verbose = FALSE
)
Arguments
data |
A long |
outcome, treatment, unit, time |
Column names (strings). |
lambda_time, lambda_nn, lambda_unit |
Penalty inputs. For a penalty that
is on an axis (see |
axes |
Length-2 character vector naming the two penalties to sweep, as
|
anchor |
Estimation anchor passed to the ATT computation
( |
control |
A list of solver/CV controls from |
seed |
Optional integer seed for CV-cell sampling. |
verbose |
Logical; print progress. |
Value
A cf_trop_grid (a data.frame) with columns lambda_time,
lambda_nn, lambda_unit, att, cv_loss. The two swept penalties vary
across rows; the fixed penalty is constant. The grid point minimising
cv_loss (the data-driven choice) is stored in attr(., "selected"), and
the chosen layout in attr(., "axes") (named x/y) and
attr(., "fixed").
See Also
trop(), autoplot.cf_trop_grid()
Examples
df <- sim_panel(N = 14, T = 9, n_treated = 3, t0 = 6, att = 2, seed = 1)
g <- trop_sensitivity(df, "y", "w", "id", "t",
lambda_time = c(0, 0.1, 0.5), lambda_nn = c(2, 5),
control = trop_control(n_cv_cells = 5L, cv_cycles = 1L,
tol = 1e-3, seed = 1L))
autoplot(g)
# Sweep the unit penalty against the nuclear-norm penalty instead, holding
# the time penalty fixed (chosen by CV):
g2 <- trop_sensitivity(df, "y", "w", "id", "t", axes = c("nn", "unit"),
control = trop_control(n_cv_cells = 5L, cv_cycles = 1L,
tol = 1e-3, seed = 1L))
autoplot(g2)
True ATT of a (semi-)synthetic panel
Description
Convenience accessor for the ground-truth average treatment effect on the
treated of a simulated panel. It reads the per-cell true effect — the tau
column produced by sim_semisynthetic(), or y - y0 for a sim_panel()
panel — and averages it over the actively treated cells, giving the number
estimators are trying to recover. Pair it with panel_compare() or
panel_rmse() to score estimates against the truth without re-deriving it by
hand.
Usage
true_att(data, treatment = "w", tau = "tau")
Arguments
data |
A long |
treatment |
Name of the 0/1 treatment column (default |
tau |
Name of the true-effect column (default |
Value
A single numeric: the true ATT averaged over treated cells.
See Also
sim_semisynthetic(), sim_panel(), panel_compare()
Examples
real <- sim_panel(N = 16, T = 10, n_treated = 0L, att = 0, seed = 1)
ss <- sim_semisynthetic(real, "y", "id", "t",
n_treated = 6, t0 = 8, att = 3, seed = 2)
true_att(ss) # 3