| Title: | Sequential Threshold-Spatial-Quantile Panel Estimation |
|---|---|
| Description: | Implements a sequential panel estimation protocol for regional economic panels that combines three estimation layers in a fixed order. The first layer applies a two-way fixed effects baseline. The second layer applies the panel threshold regression method of Hansen (1999) <doi:10.1016/S0304-4076(99)00025-1> to identify structural breaks at an unknown threshold of a moderating variable, with bootstrap inference following Hansen (2000) <doi:10.1111/1468-0262.00124>. The third layer applies a spatial Durbin model with an impact decomposition following LeSage and Pace (2009, ISBN:978-1-4200-6424-7) to quantify direct and indirect spillover effects. The fourth layer applies the two-step panel quantile estimator of Canay (2011) <doi:10.1111/j.1368-423X.2011.00349.x> to document distributional heterogeneity in the outcome. The threshold identified in the second layer defines a subsample used as structured input to the fourth layer, and a consistency check evaluates whether the three sets of results are jointly compatible with a common underlying structural relationship. An illustrative panel of 33 districts of the state of Maharashtra, India, observed over 10 agricultural years, is included with the package. |
| Authors: | Prakash Vhankade [aut, cre] (ORCID: <https://orcid.org/0009-0005-2436-0603>) |
| Maintainer: | Prakash Vhankade <[email protected]> |
| License: | GPL-3 |
| Version: | 0.2.0 |
| Built: | 2026-10-17 09:58:35 UTC |
| Source: | https://github.com/cran/tsqr |
Implements the Canay (2011) two-step panel quantile estimator. In the first step, unit and time fixed effects are estimated from a two-way fixed effects model and subtracted from the outcome. In the second step, standard quantile regression is applied at each requested quantile.
canay_quantile( formula, data, index, taus = c(0.1, 0.25, 0.5, 0.75, 0.9), n_boot = 500, seed = 42, ptr_result = NULL, threshold_var = NULL, verbose = TRUE )canay_quantile( formula, data, index, taus = c(0.1, 0.25, 0.5, 0.75, 0.9), n_boot = 500, seed = 42, ptr_result = NULL, threshold_var = NULL, verbose = TRUE )
formula |
A formula of the form |
data |
A data frame. |
index |
A character vector of length 2: |
taus |
Numeric vector of quantile levels. Default is
|
n_boot |
Integer. Bootstrap replications for standard errors.
Default is |
seed |
Integer or NULL. Random seed for reproducibility. Set to
|
ptr_result |
Optional object of class |
threshold_var |
Character string naming the threshold variable.
Required if |
verbose |
Logical. Default is |
A list of class canay_quantile with elements coefs, se,
slope_tests, gradient, gradient_highstress, fe_corrected,
taus, key_var, and call.
Canay IA (2011). "A Simple Approach to Quantile Regression for Panel Data." Econometrics Journal, 14(3), 368-386. doi:10.1111/j.1368-423X.2011.00349.x
data(maharashtra_panel) result <- canay_quantile( formula = ln_GDVA ~ ln_HYV + MCDS_days + Irrigation_pct, data = maharashtra_panel, index = c("district", "year"), taus = c(0.25, 0.50, 0.75), n_boot = 50, seed = 42, verbose = FALSE ) print(result) coef(result)data(maharashtra_panel) result <- canay_quantile( formula = ln_GDVA ~ ln_HYV + MCDS_days + Irrigation_pct, data = maharashtra_panel, index = c("district", "year"), taus = c(0.25, 0.50, 0.75), n_boot = 50, seed = 42, verbose = FALSE ) print(result) coef(result)
A balanced panel dataset covering 33 districts of the state of Maharashtra, India, over 10 agricultural years from 2014-15 to 2023-24 (330 observations, 30 variables).
data(maharashtra_panel)data(maharashtra_panel)
A data frame with 330 rows and 30 variables:
District name.
District numeric identifier, 1 to 33.
Calendar year, 2014 to 2023.
Agricultural year label, e.g. "2014-15".
Gross District Value Added from Agriculture and Allied Sectors, constant 2011-12 prices, INR crore.
Natural log of GDVA_agri_crore. Dependent variable.
Farm household income, INR thousands.
Area under high-yielding varieties, 000 ha.
Natural log of HYV_area_000ha. Key regressor.
Annual rainfall, mm.
Natural log of Rainfall_mm.
Mean annual temperature, degrees Celsius.
Standardised within-district temperature anomaly.
Maximum Consecutive Dry Spell, days. Primary threshold variable.
Standardised within-district rainfall anomaly.
Pesticide consumption, metric tonnes.
NPK fertiliser consumption, metric tonnes.
Share of gross cropped area irrigated.
Variable input cost per hectare, INR.
Agricultural training intensity.
Agricultural training expenditure, INR lakhs.
Extension workers per 1000 farm households.
Number of primary agricultural credit societies.
Number of agricultural market yards.
Rural road density.
Average holding size, hectares.
Share of holdings below 2 hectares.
Cropping intensity index.
Crop concentration index, 0 to 1.
Share of gross cropped area that is rainfed.
Compiled from public government and research institution sources for Maharashtra, India. Deposited at Mendeley Data: doi:10.17632/sr2gbfxk2d.2.
data(maharashtra_panel) head(maharashtra_panel)data(maharashtra_panel) head(maharashtra_panel)
Estimates a two-regime panel threshold regression model following Hansen (1999, 2000), with two-way fixed effects, grid search for the optimal threshold, bootstrap likelihood ratio test, and confidence interval construction by likelihood ratio inversion.
ptr_hansen( formula, data, threshold_var, index, trim = c(0.15, 0.85), n_boot = 300, seed = 42, verbose = TRUE )ptr_hansen( formula, data, threshold_var, index, trim = c(0.15, 0.85), n_boot = 300, seed = 42, verbose = TRUE )
formula |
A formula of the form |
data |
A data frame. |
threshold_var |
Character string naming the threshold variable. |
index |
A character vector of length 2: |
trim |
Numeric vector of length 2 giving the lower and upper
percentile trimming bounds for the threshold grid. Default is
|
n_boot |
Integer. Bootstrap replications for the likelihood ratio
test. Default is |
seed |
Integer or NULL. Random seed for reproducibility. Set to
|
verbose |
Logical. If |
A list of class ptr_hansen with elements threshold,
ci, LR_stat, p_value, cv, coef_regime1, coef_regime2,
regime_shift, ssr_grid, LR_boot, fit_regime1, fit_regime2,
and call.
Hansen BE (1999). "Threshold Effects in Non-Dynamic Panels: Estimation, Testing, and Inference." Journal of Econometrics, 93(2), 345-368. doi:10.1016/S0304-4076(99)00025-1
Hansen BE (2000). "Sample Splitting and Threshold Estimation." Econometrica, 68(3), 575-603. doi:10.1111/1468-0262.00124
data(maharashtra_panel) result <- ptr_hansen( formula = ln_GDVA ~ ln_HYV + MCDS_days + Irrigation_pct, data = maharashtra_panel, threshold_var = "MCDS_days", index = c("district", "year"), n_boot = 50, seed = 42, verbose = FALSE ) print(result) summary(result) coef(result) confint(result)data(maharashtra_panel) result <- ptr_hansen( formula = ln_GDVA ~ ln_HYV + MCDS_days + Irrigation_pct, data = maharashtra_panel, threshold_var = "MCDS_days", index = c("district", "year"), n_boot = 50, seed = 42, verbose = FALSE ) print(result) summary(result) coef(result) confint(result)
Constructs a k-nearest neighbour spatial weights matrix, runs Moran's I pre-tests for spatial dependence, estimates a panel Spatial Durbin Model, and computes the direct, indirect, and total impact decomposition following LeSage and Pace (2009).
sdm_impact(formula, data, index, coords, k = 4, verbose = TRUE)sdm_impact(formula, data, index, coords, k = 4, verbose = TRUE)
formula |
A formula of the form |
data |
A data frame. |
index |
A character vector of length 2: |
coords |
A matrix or data frame with two columns giving the longitude
and latitude coordinates of each unique cross-sectional unit in the same
order as |
k |
Integer. Number of nearest neighbours. Default is |
verbose |
Logical. Default is |
A list of class sdm_impact with elements W, impacts,
spillover_ratio, rho, moran_y, moran_x, fit, and call.
LeSage J, Pace RK (2009). Introduction to Spatial Econometrics. CRC Press, Boca Raton. ISBN 978-1-4200-6424-7.
# Requires coordinate columns not in the bundled maharashtra_panel. # Replace with real district centroids in your application. data(maharashtra_panel) units <- sort(unique(maharashtra_panel$district)) coords <- data.frame( longitude = stats::runif(length(units), 73, 80), latitude = stats::runif(length(units), 16, 22) ) result <- sdm_impact( formula = ln_GDVA ~ ln_HYV + MCDS_days + Irrigation_pct, data = maharashtra_panel, index = c("district", "year"), coords = coords, k = 4 ) print(result) coef(result)# Requires coordinate columns not in the bundled maharashtra_panel. # Replace with real district centroids in your application. data(maharashtra_panel) units <- sort(unique(maharashtra_panel$district)) coords <- data.frame( longitude = stats::runif(length(units), 73, 80), latitude = stats::runif(length(units), 16, 22) ) result <- sdm_impact( formula = ln_GDVA ~ ln_HYV + MCDS_days + Irrigation_pct, data = maharashtra_panel, index = c("district", "year"), coords = coords, k = 4 ) print(result) coef(result)
Evaluates whether the regime shift from ptr_hansen(), the spillover
ratio from sdm_impact(), and the distributional gradient from
canay_quantile() are mutually consistent with a common structural
relationship.
tsq_consistency_check(ptr_result, sdm_result, qr_result, verbose = TRUE)tsq_consistency_check(ptr_result, sdm_result, qr_result, verbose = TRUE)
ptr_result |
Object of class |
sdm_result |
Object of class |
qr_result |
Object of class |
verbose |
Logical. Default is |
A list of class tsq_consistency with elements criterion1,
criterion2, criterion3, all_pass, and summary_table.
ptr_mock <- structure( list(threshold = 23, regime_shift = 0.30, coef_regime1 = 0.346, coef_regime2 = 0.646), class = "ptr_hansen" ) sdm_mock <- structure( list(spillover_ratio = 0.467, rho = 0.31), class = "sdm_impact" ) qr_mock <- structure( list(gradient = c(Q10 = 0.57, Q50 = 0.55, Q90 = 0.52), gradient_highstress = c(Q10 = 0.62, Q50 = 0.56, Q90 = 0.50), taus = c(0.10, 0.50, 0.90), key_var = "ln_HYV"), class = "canay_quantile" ) result <- tsq_consistency_check(ptr_mock, sdm_mock, qr_mock) print(result)ptr_mock <- structure( list(threshold = 23, regime_shift = 0.30, coef_regime1 = 0.346, coef_regime2 = 0.646), class = "ptr_hansen" ) sdm_mock <- structure( list(spillover_ratio = 0.467, rho = 0.31), class = "sdm_impact" ) qr_mock <- structure( list(gradient = c(Q10 = 0.57, Q50 = 0.55, Q90 = 0.52), gradient_highstress = c(Q10 = 0.62, Q50 = 0.56, Q90 = 0.50), taus = c(0.10, 0.50, 0.90), key_var = "ln_HYV"), class = "canay_quantile" ) result <- tsq_consistency_check(ptr_mock, sdm_mock, qr_mock) print(result)
Wrapper that runs the full four-step TSQ protocol: a two-way fixed
effects baseline, ptr_hansen(), sdm_impact(), and
canay_quantile(), followed by tsq_consistency_check().
tsq_panel( formula, data, index, threshold_var, coords, k = 4, taus = c(0.1, 0.25, 0.5, 0.75, 0.9), trim = c(0.15, 0.85), n_boot_ptr = 300, n_boot_qr = 500, seed = 42, verbose = TRUE )tsq_panel( formula, data, index, threshold_var, coords, k = 4, taus = c(0.1, 0.25, 0.5, 0.75, 0.9), trim = c(0.15, 0.85), n_boot_ptr = 300, n_boot_qr = 500, seed = 42, verbose = TRUE )
formula |
A formula of the form |
data |
A data frame. |
index |
A character vector of length 2: |
threshold_var |
Character string naming the threshold variable. |
coords |
A matrix or data frame with coordinate columns, one row per unique cross-sectional unit in sorted order. |
k |
Integer. Nearest neighbours for the spatial weights matrix.
Default is |
taus |
Numeric vector of quantile levels. Default is
|
trim |
Numeric vector of length 2 for threshold grid trimming.
Default is |
n_boot_ptr |
Integer. Bootstrap replications for the threshold
test. Default is |
n_boot_qr |
Integer. Bootstrap replications for quantile standard
errors. Default is |
seed |
Integer or NULL. Random seed. Set to |
verbose |
Logical. Default is |
A list of class tsq_panel with elements step1_baseline,
step2_ptr, step3_sdm, step4_qr, consistency, and call.
# Requires coordinate data; replace with real district centroids. data(maharashtra_panel) units <- sort(unique(maharashtra_panel$district)) coords <- data.frame( longitude = stats::runif(length(units), 73, 80), latitude = stats::runif(length(units), 16, 22) ) result <- tsq_panel( formula = ln_GDVA ~ ln_HYV + MCDS_days + Irrigation_pct, data = maharashtra_panel, index = c("district", "year"), threshold_var = "MCDS_days", coords = coords, n_boot_ptr = 50, n_boot_qr = 50 ) print(result) coef(result)# Requires coordinate data; replace with real district centroids. data(maharashtra_panel) units <- sort(unique(maharashtra_panel$district)) coords <- data.frame( longitude = stats::runif(length(units), 73, 80), latitude = stats::runif(length(units), 16, 22) ) result <- tsq_panel( formula = ln_GDVA ~ ln_HYV + MCDS_days + Irrigation_pct, data = maharashtra_panel, index = c("district", "year"), threshold_var = "MCDS_days", coords = coords, n_boot_ptr = 50, n_boot_qr = 50 ) print(result) coef(result)
Produces diagnostic plots for TSQ protocol results.
tsq_plot(x, type = "all", ...)tsq_plot(x, type = "all", ...)
x |
An object of class |
type |
Character. One of |
... |
Currently unused. |
A ggplot object or named list of ggplot objects.
data(maharashtra_panel) ptr <- ptr_hansen( formula = ln_GDVA ~ ln_HYV + MCDS_days + Irrigation_pct, data = maharashtra_panel, threshold_var = "MCDS_days", index = c("district", "year"), n_boot = 50, verbose = FALSE ) p <- tsq_plot(ptr, type = "threshold") print(p)data(maharashtra_panel) ptr <- ptr_hansen( formula = ln_GDVA ~ ln_HYV + MCDS_days + Irrigation_pct, data = maharashtra_panel, threshold_var = "MCDS_days", index = c("district", "year"), n_boot = 50, verbose = FALSE ) p <- tsq_plot(ptr, type = "threshold") print(p)