tf-inference¶
tf-inference computes tF critical values and confidence intervals for instrumental-variable (IV) models with one endogenous regressor and one excluded instrument. It provides generated tables for common confidence levels and numerical calibration for other levels.
The tF method and its theoretical results are due to David S. Lee, Justin McCrary, Marcelo J. Moreira and Jack Porter, authors of Valid t-Ratio Inference for IV (2022). This package provides an independent Python implementation of their Appendix B.3 construction and reusable numerical tables. Critical values depend on the first-stage F statistic and a two-sided significance level. The code constructs the curve from its boundary expansion and recurrence; the published rounded tables serve as test references.
This is an initial numerical implementation. Its calibration uses deterministic integration, a finite nuisance-parameter grid and local searches. These checks are documented and reproducible; they are not a proof of a uniform size bound. The inferential framework is asymptotic under the single-instrument IV model, not a finite-sample coverage guarantee or a remedy for invalid instruments.
Installation¶
Install with Python 3.12 or later:
python -m pip install tf-inference
NumPy, SciPy and PyArrow are required at runtime. No Stata, external data download, API key or network access is needed to evaluate or generate critical values.
Use the generated tables¶
from tf_inference import critical_value, confidence_interval
critical_value([3.8, 3.9, 10, 1000], alpha=0.05)
# Approximately [inf, 30.93, 3.43035, 1.95996]
confidence_interval(coefficient=1, se=0.4, f_stat=10, alpha=0.05)
# Approximately [-0.37214, 2.37214]
alpha is the significance level: 0.05 means 95% confidence. The return
value of critical_value is the threshold for absolute t, not t
squared or a standard-error multiplier. Divide it by the normal
two-sided critical value to obtain the corresponding standard-error
multiplier for that alpha.
Inputs broadcast as NumPy arrays. Confidence intervals put lower and upper bounds on the final axis. Complex inputs, nonfinite or negative F statistics, nonfinite coefficients, and nonpositive standard errors are rejected.
The 95% boundary is about 3.84145882. F at or below the boundary returns
an infinite critical value; F strictly above it has a finite critical
value. There is no artificial cutoff at F=4. The boundary is computed
separately for each alpha as scipy.special.ndtri(alpha / 2)**2. Very
close to the boundary, rounding in the input F itself can materially
affect the result.
Bundled tables cover alpha values 0.0001, 0.0005, 0.001, 0.002, 0.005,
0.01, 0.02, 0.025, 0.03, 0.04, 0.05, 0.06, 0.075, 0.1, 0.125, 0.15, 0.2
and 0.25. Each contains at least 16,385 rows, concentrated near the
boundary. List the installed levels with available_alphas(). Tables
are never interpolated across alpha values.
The Python convenience functions and CLI default to method="table". A
missing level raises an error; numerical calibration requires an
explicit method="compute" or a direct call to calibrate. The auto
method is not supported.
Calculate an unconventional level¶
from tf_inference import calibrate
calibration = calibrate(alpha=0.037)
calibration.critical_value([4.5, 10, 100])
calibration.confidence_interval(1, 0.4, 10)
print(calibration.diagnostics)
The bundled Parquet dataset is read and validated once; interpolators are
cached per alpha and shared by load_table,
critical_value and confidence_interval. Repeated lookup does not
read files, verify checksums again or rebuild the interpolator. Explicit
method="compute" calls use a separate cache of up to 32 calibrations.
Direct calibrate calls always compute afresh. Tables loaded from a
custom directory are read and checksum-verified on each call so changes
are detected.
For large inputs, pass a NumPy array in one call. For repeated work at
one level, retain load_table(alpha) or the result of
calibrate(alpha) and call its methods directly.
The numerical alpha domain is [0.0001, 0.25]. It covers confidence levels from 75% to 99.99%. This is an explicit implementation range, not a theoretical limit of tF. Calculations outside it are rejected rather than extrapolated.
calibrate accepts epsilon, seeds, size_tolerance,
strength_limit, strengths and correlations. Smaller initialization
epsilon and more seeds allow convergence checks. These settings are not
claims of certified error. Failed recurrence, integration or
optimization raises ConvergenceError. Inspect the returned diagnostics
when doing new calibrations, and validate beyond the search settings
before relying on an unconventional level for research.
Generate constants for other software¶
The command-line interface exposes the same calculation:
tf-inference critical 3.9 10 100 --alpha 0.05
tf-inference critical 10 --alpha 0.037 --method compute
tf-inference generate --alpha 0.05 0.01 0.037 --output tables
The generator writes one calibrations.parquet dataset and a
calibrations.json manifest. Adding or replacing one alpha preserves the other
levels in that directory. The Parquet schema has three nonnullable float64
columns: alpha, f_stat and regularized_squared. Calibration settings,
plateaus and diagnostics are embedded as JSON in the Arrow schema metadata.
The schema-versioned manifest records the dataset’s SHA-256 checksum.
Loading validates the checksum, schema, finite values, ordered grids,
boundary and plateau invariants, and agreement with the metadata.
export_table(calibration, directory, points=16385) and
load_table(alpha, directory=directory) provide the same operations in Python.
For a portable gzip CSV, add --csv to the generation command or pass
include_csv=True to export_table. CSV files are optional exports, not
packaged runtime data.
For accurate boundary evaluation, interpolate the regularized quantity
Z(F) = (F - boundary) * critical(F)**2 using a monotone cubic interpolator.
It is the Parquet regularized_squared column (and the optional CSV’s
branch_regularized_squared column). Then compute
sqrt(Z(F) / (F - boundary)). Cap F at the metadata’s plateau_f before
evaluation and return infinity when F is at or below the boundary. The raw
branch extends to the Property (C) endpoint; at large F use the selected
plateau. The CSV’s first critical value is infinite and must not be
interpolated as an ordinary finite row.
The export measures relative interpolation differences at every cell midpoint against the computed curve, and rejects tables exceeding 1e-6 there. This is a measured check, not an interval-arithmetic error bound. The shipped validation also compares randomly chosen F values against a separately refined curve.
How the calculation works¶
Compute the boundary q and initialize the squared critical value near it using the expansion in Appendix B.3, step 1.
Apply the inverse map in step 2 to extend the decreasing curve. The code interpolates Z(F), whose finite boundary limit is q cubed, rather than interpolating a divergent critical value.
Locate the tangency that limits the decreasing segment under Property (C). Its admissible plateau is also constrained to be at least the normal two-sided critical value.
Integrate null rejection probabilities in the Gaussian weak-IV experiment. Search the nuisance parameters and move the plateau earlier if it over-rejects beyond the requested numerical tolerance.
The size search can change the plateau. For example, alpha=0.037 requires an adjustment to the first candidate. For alpha=0.01 the plateau is about 2.72549, above the normal 99% critical value of about 2.57583. Replacing the 95% normal quantile with another quantile does not implement general tF.
The default size search covers strength f0 from 0 to 80 and absolute correlation rho from 0 to 1. Here E[F] = 1 + f0 squared; f0 is not F. It includes singular rho=1 calculations, correlations approaching one, and local searches between grid points. The strong-instrument limit is checked separately by the normal critical-value constraint. The unbounded nuisance domain is not exhaustively certified. The recorded integration error does not include curve approximation or missed extrema in the parameter search.
Performance¶
Table lookup avoids numerical calibration. For large inputs, evaluate an array
in one call. make benchmark records separate timings for warm scalar and
array lookup, loading the complete Parquet dataset with empty package caches,
and fresh calibration. Imports and operating-system file-cache effects are
excluded. Results and environment details are in benchmarks/results.json;
benchmarks/baseline.json preserves the earlier implementation’s measurements.
These are local observations, not CI timing assertions. Tests enforce that
default calls never calibrate and warm calls avoid file access and interpolator
construction.
Validation and development¶
Install development dependencies and run the checks:
uv sync --all-groups
make ci
Tests cover the published rounded-table values and plateau landmarks,
initialization and grid refinement, the defining two-intersection
equations, monotonicity and the exact boundary, near-singular numerical
integration, independent Gaussian Monte Carlo calculations,
unconventional-alpha plateau adjustment, interval broadcasting, table
roundtrips and corrupted files, Parquet schema violations, complex inputs and
large-strength numerical stability. make ci runs Ruff, Pyright, pydoclint,
tests with a 90% coverage floor (including branch measurement), documentation
and distribution checks.
preen check --strict checks adoption of the shared py-canon standard.
validation/validate.py evaluates bundled tables against finer
recurrence calculations and held-out nuisance-parameter grids extending
to f0=160. validation/results.json records all cases and the
environment. Reproduce it:
uv run python validation/validate.py --output validation/results.json
Rebuild the bundled constants from scratch:
make tables
make ci-docker uses the standard python:3.12 image. Documentation
includes this README directly; it is not maintained as a second copy.
Scope and integration¶
This package takes summary quantities. It does not fit an IV model or verify that an instrument is valid. For use with an estimator, require one endogenous regressor and one excluded instrument, and use the appropriate first-stage squared t statistic and compatible covariance estimates. It supplies the numerical calibration; estimator-specific eligibility checks belong in the calling package.
This implements the 2022 tF construction. VtF is a different procedure using additional information and is outside the present implementation. No generic p-value API is provided: numerical nesting across arbitrary alpha levels would need separate validation before inverting the family of tests.
Citation and sources¶
Please cite the paper when using this package for research:
Lee, David S., Justin McCrary, Marcelo J. Moreira, and Jack Porter. 2022. Valid t-Ratio Inference for IV. American Economic Review 112 (10): 3260-3290.
@article{lee2022valid,
author = {Lee, David S. and McCrary, Justin and Moreira, Marcelo J. and Porter, Jack},
title = {Valid {t}-Ratio Inference for {IV}},
journal = {American Economic Review},
year = {2022},
volume = {112},
number = {10},
pages = {3260--3290},
doi = {10.1257/aer.20211063}
}
The authors provide the construction and supporting materials:
Author manuscript with Appendix B.3, February 16, 2022.
Data and code for the paper, the authors’ replication package.
AI assistance was used in this package’s development and validation. The numerical limitations and executed checks are recorded above and in the repository’s validation results.
Python API¶
.. py:module:: tf_inference
Numerically constructed tF inference for single-instrument IV models.
.. py:function:: confidence_interval(coefficient, se, f_stat, alpha=0.05, *, method=’table’) :module: tf_inference
Return tF confidence intervals with lower/upper bounds on the last axis.
.. py:function:: critical_value(f_stat, alpha=0.05, *, method=’table’) :module: tf_inference
Return tF critical values for absolute t using a table or numerical construction.
The default requires an exact matching bundled alpha. Explicitly select
method="compute" for cached numerical calibration at another level.
No interpolation across significance levels is performed.
For custom numerical tolerances use calibrate and inspect its diagnostics.
.. py:module:: tf_inference.calibration
Numerical plateau selection, audit records, and inference.
.. py:class:: CalibrationDiagnostics(alpha, plateau_f, plateau_critical, property_c_endpoint, maximum_rejection, worst_strength, worst_rho, integration_error, size_tolerance, strength_limit, evaluations, adjustments, correlations, strengths) :module: tf_inference.calibration
Finite numerical size search; this is not a uniform mathematical bound.
.. py:method:: CalibrationDiagnostics.to_dict() :module: tf_inference.calibration
Return serializable diagnostics.
.. py:class:: Calibration(curve, plateau_f, diagnostics) :module: tf_inference.calibration
A tF curve and a plateau checked by a finite numerical size search.
.. py:property:: Calibration.alpha :module: tf_inference.calibration
Two-sided significance level.
.. py:property:: Calibration.boundary :module: tf_inference.calibration
F at or below which the confidence interval is unbounded.
.. py:property:: Calibration.plateau :module: tf_inference.calibration
Critical value for absolute t on the plateau.
.. py:method:: Calibration.critical_value(f_stat) :module: tf_inference.calibration
Evaluate critical values, with infinity at and below the boundary.
.. py:method:: Calibration.confidence_interval(coefficient, se, f_stat) :module: tf_inference.calibration
Construct intervals; broadcast inputs and put lower/upper bounds last.
.. py:function:: calibrate(alpha=0.05, *, epsilon=0.0001, seeds=256, size_tolerance=2e-07, strength_limit=80.0, correlations=None, strengths=None, optimize=True) :module: tf_inference.calibration
Construct and numerically calibrate a tF curve at a requested alpha.
The initial plateau satisfies Property (C) and is at least the normal critical value. A grid and local searches check rejection probabilities; violations move the plateau earlier on the decreasing branch. Diagnostics report the finite search domain and numerical tolerances. Neither a finite grid nor local optimization certifies the supremum over all nuisance values.
.. py:module:: tf_inference.curve
The Appendix B.3 inverse map and the Property (C) endpoint.
.. py:exception:: ConvergenceError :module: tf_inference.curve
A numerical construction or calibration did not converge.
.. py:class:: Curve(alpha, boundary, f, regularized, epsilon, seeds, property_c_endpoint, minimum_plateau_f, _spline) :module: tf_inference.curve
A numerically constructed decreasing branch, before plateau calibration.
critical returns the threshold for absolute t, not for t squared.
The interpolated quantity is (F - boundary) * critical(F)**2.
.. py:method:: Curve.regularized_squared(f_stat) :module: tf_inference.curve
Evaluate the finite regularized squared critical value.
:param f_stat: Real F values between the boundary and the branch endpoint.
:returns: Values of (F - boundary) times the squared critical value.
:raises ValueError: If F is non-real, nonfinite, or outside the branch.
.. py:method:: Curve.critical(f_stat) :module: tf_inference.curve
Evaluate the decreasing branch within its constructed domain.
.. py:function:: construct_curve(alpha=0.05, *, epsilon=0.0001, seeds=256) :module: tf_inference.curve
Construct Appendix B.3’s decreasing branch and its admissible endpoint.
:param alpha: Two-sided significance level, between 0.0001 and 0.25. :param epsilon: Initialization distance from the chi-square boundary. :param seeds: Number of starting points across one inverse-map interval.
:returns: Candidate decreasing curve and its admissible endpoint. Use calibrate
to check a plateau before using the curve for inference.
:raises ValueError: If the significance level or construction settings are invalid. :raises ConvergenceError: If the recurrence or tangency calculation fails.
.. py:module:: tf_inference.probability
Independent integration of the weak-IV Gaussian null experiment.
.. py:class:: RejectionProbability(probability, error) :module: tf_inference.probability
Null rejection probability with quadrature and omitted-tail error estimates.
.. py:function:: rejection_probability(curve, strength, rho, *, plateau_f=None, atol=1e-09) :module: tf_inference.probability
Integrate null rejection probability in the weak-IV limit experiment.
strength is f0 (the mean first-stage t), not E[F]; E[F] = 1 + f0**2.
rho is the correlation of the Gaussian AR and first-stage statistics.
Symmetry allows their absolute values. This is asymptotic size, not a
finite-sample coverage guarantee. The error excludes curve approximation.
.. py:module:: tf_inference.tables
Schema-validated Parquet calibration tables and optional CSV export.
.. py:function:: available_alphas() :module: tf_inference.tables
List significance levels with bundled precomputed calibrations.
:returns: Sorted significance levels from the validated table metadata.
.. py:function:: export_table(calibration, directory, *, points=16385, include_csv=False) :module: tf_inference.tables
Export a calibration into a schema-validated Parquet dataset.
:param calibration: Numerically calibrated tF curve. :param directory: Output directory. Other significance levels are preserved. :param points: Number of grid points, at least 128. :param include_csv: Also write a gzip CSV for use in other software.
:returns: Calibration metadata including measured midpoint interpolation error.
:raises ValueError: If points are invalid or interpolation error exceeds 1e-6.
.. py:function:: load_table(alpha=0.05, *, directory=None) :module: tf_inference.tables
Load a validated calibration, caching bundled tables by significance level.
:param alpha: Two-sided significance level with a precomputed table. :param directory: Custom dataset directory, read and validated on every call.
:returns: Calibration reconstructed from the stored curve and diagnostics.
:raises ValueError: If the table is missing, corrupted, or inconsistent.