<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Spatial Analysis | Carlos Mendez</title><link>https://carlos-mendez.org/category/spatial-analysis/</link><atom:link href="https://carlos-mendez.org/category/spatial-analysis/index.xml" rel="self" type="application/rss+xml"/><description>Spatial Analysis</description><generator>Wowchemy (https://wowchemy.com)</generator><language>en-us</language><copyright>Carlos Mendez</copyright><lastBuildDate>Fri, 27 Mar 2026 00:00:00 +0000</lastBuildDate><image><url>https://carlos-mendez.org/media/icon_huedfae549300b4ca5d201a9bd09a3ecd5_79625_512x512_fill_lanczos_center_3.png</url><title>Spatial Analysis</title><link>https://carlos-mendez.org/category/spatial-analysis/</link></image><item><title>Spatial Dynamic Panel Data Modeling in R: Cigarette Demand Across US States</title><link>https://carlos-mendez.org/post/r_sdpdmod/</link><pubDate>Fri, 27 Mar 2026 00:00:00 +0000</pubDate><guid>https://carlos-mendez.org/post/r_sdpdmod/</guid><description>&lt;h2 id="1-overview">1. Overview&lt;/h2>
&lt;p>When a state raises its cigarette tax, smokers near the border may simply drive to a neighboring state with lower prices. This cross-border shopping effect means that cigarette consumption in one state depends not only on its own prices and income but also on the prices and consumption patterns of its neighbors. Ignoring these &lt;strong>spatial spillovers&lt;/strong> leads to biased estimates of how prices and income affect cigarette demand &amp;mdash; a problem that standard panel data methods cannot address.&lt;/p>
&lt;p>The &lt;a href="https://cran.r-project.org/package=SDPDmod" target="_blank" rel="noopener">SDPDmod&lt;/a> R package (Simonovska, 2025) provides an integrated workflow for spatial panel data modeling. It offers three core capabilities: (1) &lt;strong>Bayesian model comparison&lt;/strong> across six spatial specifications using log-marginal posterior probabilities, (2) &lt;strong>maximum likelihood estimation&lt;/strong> of spatial autoregressive (SAR) and spatial Durbin (SDM) models with optional Lee-Yu bias correction for fixed effects, and (3) &lt;strong>impact decomposition&lt;/strong> into direct, indirect (spillover), and total effects &amp;mdash; including short-run and long-run effects for dynamic models. This tutorial applies all three capabilities to the classic Cigar dataset: cigarette consumption across 46 US states from 1963 to 1992.&lt;/p>
&lt;p>The tutorial follows a progressive approach. We start with the simplest spatial model (SAR) and build toward the most general specification (dynamic SDM with Lee-Yu correction). At each step, we interpret the results in terms of the cigarette market and compare them to simpler models. By the end, you will see how spatial spillovers and habit persistence jointly shape cigarette demand &amp;mdash; and why models that ignore either one can produce misleading policy conclusions.&lt;/p>
&lt;p>&lt;strong>Learning objectives:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Load and row-normalize the &lt;code>usa46&lt;/code> binary contiguity matrix from SDPDmod&lt;/li>
&lt;li>Prepare the Cigar panel dataset with log-transformed real prices and income&lt;/li>
&lt;li>Use &lt;code>blmpSDPD()&lt;/code> for Bayesian model comparison across OLS, SAR, SDM, SEM, SDEM, and SLX specifications&lt;/li>
&lt;li>Estimate static SAR and SDM models using &lt;code>SDPDm()&lt;/code> with individual and two-way fixed effects&lt;/li>
&lt;li>Apply the Lee-Yu transformation to correct incidental parameter bias in spatial panels&lt;/li>
&lt;li>Estimate dynamic spatial models with temporal and spatiotemporal lags&lt;/li>
&lt;li>Decompose effects into direct, indirect, and total using &lt;code>impactsSDPDm()&lt;/code>, distinguishing short-run from long-run effects&lt;/li>
&lt;/ul>
&lt;h2 id="2-the-modeling-pipeline">2. The Modeling Pipeline&lt;/h2>
&lt;p>The tutorial follows a six-stage pipeline, moving from data preparation through increasingly rich spatial panel models:&lt;/p>
&lt;pre>&lt;code class="language-mermaid">graph LR
A[&amp;quot;Data &amp;amp; W&amp;lt;br/&amp;gt;(Section 3-4)&amp;quot;] --&amp;gt; B[&amp;quot;Bayesian&amp;lt;br/&amp;gt;Comparison&amp;lt;br/&amp;gt;(Section 5)&amp;quot;]
B --&amp;gt; B2[&amp;quot;Non-Spatial&amp;lt;br/&amp;gt;Baseline&amp;lt;br/&amp;gt;(Section 6)&amp;quot;]
B2 --&amp;gt; C[&amp;quot;Static SAR&amp;lt;br/&amp;gt;(Section 7)&amp;quot;]
C --&amp;gt; D[&amp;quot;Static SDM&amp;lt;br/&amp;gt;(Section 8)&amp;quot;]
D --&amp;gt; E[&amp;quot;Dynamic SDM&amp;lt;br/&amp;gt;(Section 9)&amp;quot;]
E --&amp;gt; F[&amp;quot;Impact&amp;lt;br/&amp;gt;Decomposition&amp;lt;br/&amp;gt;(Section 10)&amp;quot;]
style A fill:#6a9bcc,stroke:#141413,color:#fff
style B fill:#d97757,stroke:#141413,color:#fff
style B2 fill:#141413,stroke:#141413,color:#fff
style C fill:#6a9bcc,stroke:#141413,color:#fff
style D fill:#6a9bcc,stroke:#141413,color:#fff
style E fill:#d97757,stroke:#141413,color:#fff
style F fill:#00d4c8,stroke:#141413,color:#fff
&lt;/code>&lt;/pre>
&lt;p>Each stage builds on the previous one. The Bayesian comparison tells us &lt;em>which&lt;/em> model family fits the data best. The static models establish baseline spatial effects. The dynamic models add habit persistence and separate short-run from long-run responses. The impact decomposition translates all of this into policy-relevant direct and spillover effects.&lt;/p>
&lt;h2 id="3-setup-and-data-preparation">3. Setup and Data Preparation&lt;/h2>
&lt;h3 id="31-install-and-load-packages">3.1 Install and load packages&lt;/h3>
&lt;p>The analysis requires five packages: &lt;code>SDPDmod&lt;/code> for spatial panel modeling, &lt;code>plm&lt;/code> for the Cigar dataset, &lt;code>ggplot2&lt;/code> and &lt;code>reshape2&lt;/code> for visualization, and &lt;code>dplyr&lt;/code> for data manipulation.&lt;/p>
&lt;pre>&lt;code class="language-r"># Install packages if needed
cran_packages &amp;lt;- c(&amp;quot;SDPDmod&amp;quot;, &amp;quot;plm&amp;quot;, &amp;quot;ggplot2&amp;quot;, &amp;quot;reshape2&amp;quot;, &amp;quot;dplyr&amp;quot;)
missing &amp;lt;- cran_packages[!sapply(cran_packages, requireNamespace, quietly = TRUE)]
if (length(missing) &amp;gt; 0) install.packages(missing)
library(SDPDmod)
library(plm)
library(ggplot2)
library(reshape2)
library(dplyr)
&lt;/code>&lt;/pre>
&lt;h3 id="32-load-and-prepare-the-cigar-dataset">3.2 Load and prepare the Cigar dataset&lt;/h3>
&lt;p>The &lt;a href="https://cran.r-project.org/web/packages/plm/vignettes/A_plmPackage.html" target="_blank" rel="noopener">Cigar dataset&lt;/a> (Baltagi, 1992) contains panel data on cigarette consumption in 46 US states from 1963 to 1992. The key variables are &lt;code>sales&lt;/code> (packs per capita), &lt;code>price&lt;/code> (average price per pack in cents), &lt;code>ndi&lt;/code> (per capita disposable income), &lt;code>pimin&lt;/code> (minimum price in adjoining states), and &lt;code>cpi&lt;/code> (consumer price index). We create log-transformed real values to work with &lt;strong>elasticities&lt;/strong> &amp;mdash; in a log-log model, each coefficient represents the percentage change in consumption for a one-percent change in the corresponding variable.&lt;/p>
&lt;pre>&lt;code class="language-r"># Load Cigar dataset
data(&amp;quot;Cigar&amp;quot;, package = &amp;quot;plm&amp;quot;)
data1 &amp;lt;- Cigar
# Create log-transformed variables
data1$logc &amp;lt;- log(data1$sales) # log cigarette packs per capita
data1$logp &amp;lt;- log(data1$price / data1$cpi) # log real price
data1$logy &amp;lt;- log(data1$ndi / data1$cpi) # log real per capita income
# Inspect panel structure
cat(&amp;quot;States:&amp;quot;, length(unique(data1$state)), &amp;quot;\n&amp;quot;)
cat(&amp;quot;Years:&amp;quot;, length(unique(data1$year)), &amp;quot;\n&amp;quot;)
cat(&amp;quot;Observations:&amp;quot;, nrow(data1), &amp;quot;\n&amp;quot;)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">States: 46
Years: 30
Observations: 1380
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-r">head(data1[, c(&amp;quot;state&amp;quot;, &amp;quot;year&amp;quot;, &amp;quot;sales&amp;quot;, &amp;quot;price&amp;quot;, &amp;quot;ndi&amp;quot;, &amp;quot;logc&amp;quot;, &amp;quot;logp&amp;quot;, &amp;quot;logy&amp;quot;)])
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text"> state year sales price ndi logc logp logy
1 1 63 93.9 28.6 1558.305 4.542230 -0.06759329 3.930354
2 1 64 95.4 29.8 1684.073 4.558079 -0.03947881 3.994983
3 1 65 98.5 29.8 1809.842 4.590057 -0.05547915 4.051007
4 1 66 96.4 31.5 1915.160 4.568506 -0.02817088 4.079398
5 1 67 95.5 31.6 2023.546 4.559126 -0.05539878 4.104051
6 1 68 88.4 35.6 2202.486 4.481872 0.02272825 4.147724
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-r">summary(data1[, c(&amp;quot;logc&amp;quot;, &amp;quot;logp&amp;quot;, &amp;quot;logy&amp;quot;)])
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text"> logc logp logy
Min. :3.978 Min. :-0.60981 Min. :3.766
1st Qu.:4.681 1st Qu.:-0.20492 1st Qu.:4.423
Median :4.797 Median :-0.10079 Median :4.557
Mean :4.793 Mean :-0.10642 Mean :4.545
3rd Qu.:4.892 3rd Qu.:-0.01225 3rd Qu.:4.686
Max. :5.697 Max. : 0.36399 Max. :5.117
&lt;/code>&lt;/pre>
&lt;p>The panel is balanced with 46 states observed over 30 years (1,380 total observations). Log cigarette consumption (&lt;code>logc&lt;/code>) has a mean of 4.793, corresponding to about 121 packs per capita per year. Real prices (&lt;code>logp&lt;/code>) average -0.106 in log terms, and real per capita income (&lt;code>logy&lt;/code>) averages 4.545. The variation across states and over time in both prices and income is what allows us to identify price and income elasticities &amp;mdash; and the spatial structure across neighboring states is what motivates the spatial models.&lt;/p>
&lt;p>The dataset also includes &lt;code>pimin&lt;/code>, the minimum cigarette price in adjoining states. This variable is inherently spatial &amp;mdash; it measures price competition from neighbors. We do not include &lt;code>pimin&lt;/code> directly in our models because the SDM&amp;rsquo;s spatially lagged price term &lt;code>W*logp&lt;/code> captures the same channel more flexibly. To see why, note that &lt;code>log(pimin/cpi)&lt;/code> and the spatial lag of &lt;code>logp&lt;/code> have a correlation of 0.92 &amp;mdash; they measure essentially the same thing, but the spatial lag uses the full contiguity structure rather than just the cheapest neighbor.&lt;/p>
&lt;h3 id="33-exploratory-visualization">3.3 Exploratory visualization&lt;/h3>
&lt;p>Before building models, the spaghetti plot below shows cigarette sales per capita for all 46 states over time, with five states highlighted for comparison.&lt;/p>
&lt;pre>&lt;code class="language-r"># Highlight selected states
highlight_states &amp;lt;- c(&amp;quot;CA&amp;quot;, &amp;quot;NY&amp;quot;, &amp;quot;NC&amp;quot;, &amp;quot;KY&amp;quot;, &amp;quot;UT&amp;quot;)
ggplot(data1, aes(x = year + 1900, y = sales, group = state_abbr)) +
geom_line(data = subset(data1, !(state_abbr %in% highlight_states)),
color = &amp;quot;gray80&amp;quot;, linewidth = 0.3) +
geom_line(data = subset(data1, state_abbr %in% highlight_states),
aes(color = state_abbr), linewidth = 1) +
labs(title = &amp;quot;Cigarette Sales per Capita Across 46 US States (1963-1992)&amp;quot;,
x = &amp;quot;Year&amp;quot;, y = &amp;quot;Packs per Capita&amp;quot;, color = &amp;quot;State&amp;quot;) +
theme_minimal()
&lt;/code>&lt;/pre>
&lt;p>&lt;img src="r_SDPDmod_fig4_eda_spaghetti.png" alt="Cigarette sales per capita across 46 US states from 1963 to 1992, with five states highlighted">&lt;/p>
&lt;p>Two patterns jump out. First, &lt;strong>temporal persistence is striking&lt;/strong>: states that consumed heavily in the 1960s (like Kentucky, a major tobacco-producing state with over 150 packs per capita) remained high consumers throughout the period, while low-consumption states like Utah stayed low. This visual persistence foreshadows the dominant role of the lagged dependent variable ($\tau \approx 0.86$) in the dynamic models. Second, there is a &lt;strong>general downward trend&lt;/strong> after the late 1970s, visible across nearly all states, reflecting the cumulative effect of anti-smoking campaigns, health awareness, and rising taxes. Time fixed effects in our panel models will absorb this common trend, isolating the within-state, within-year variation that identifies price and income elasticities.&lt;/p>
&lt;h3 id="34-load-and-row-normalize-the-spatial-weight-matrix">3.4 Load and row-normalize the spatial weight matrix&lt;/h3>
&lt;p>A spatial weight matrix $W$ encodes which states are neighbors. The &lt;code>usa46&lt;/code> matrix included in SDPDmod is a binary contiguity matrix: $w_{ij} = 1$ if states $i$ and $j$ share a border, and $w_{ij} = 0$ otherwise. Row-normalization converts these binary entries into weights that sum to one for each row, so the spatial lag $Wy$ equals the &lt;em>weighted average&lt;/em> of neighboring states' values.&lt;/p>
&lt;pre>&lt;code class="language-r"># Load binary contiguity matrix of 46 US states
data(&amp;quot;usa46&amp;quot;, package = &amp;quot;SDPDmod&amp;quot;)
cat(&amp;quot;Dimensions:&amp;quot;, dim(usa46), &amp;quot;\n&amp;quot;)
cat(&amp;quot;Non-zero entries:&amp;quot;, sum(usa46 != 0), &amp;quot;\n&amp;quot;)
cat(&amp;quot;Average neighbors per state:&amp;quot;, round(mean(rowSums(usa46)), 2), &amp;quot;\n&amp;quot;)
# Row-normalize
W &amp;lt;- rownor(usa46)
cat(&amp;quot;Row-normalized:&amp;quot;, isrownor(W), &amp;quot;\n&amp;quot;)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Dimensions: 46 46
Non-zero entries: 188
Average neighbors per state: 4.09
Row-normalized: TRUE
&lt;/code>&lt;/pre>
&lt;p>The matrix has 188 non-zero entries out of 2,116 possible pairs (8.9% density), meaning the average state shares a border with about 4 neighbors. After row-normalization, the spatial lag of any variable equals the simple average of that variable across a state&amp;rsquo;s contiguous neighbors. For example, the spatial lag of cigarette consumption for a state with 4 neighbors equals the average consumption in those 4 neighboring states.&lt;/p>
&lt;h2 id="4-visualizing-the-spatial-weight-matrix">4. Visualizing the Spatial Weight Matrix&lt;/h2>
&lt;p>Before estimating spatial models, it helps to visualize the neighborhood structure. The heatmap below shows the binary contiguity matrix, with each colored cell indicating a pair of neighboring states.&lt;/p>
&lt;pre>&lt;code class="language-r"># Use state abbreviations for the axes
rownames(usa46) &amp;lt;- state_abbr
colnames(usa46) &amp;lt;- state_abbr
usa46_df &amp;lt;- melt(usa46)
colnames(usa46_df) &amp;lt;- c(&amp;quot;State_i&amp;quot;, &amp;quot;State_j&amp;quot;, &amp;quot;Connection&amp;quot;)
usa46_df$Connection &amp;lt;- factor(usa46_df$Connection, levels = c(0, 1),
labels = c(&amp;quot;Not neighbors&amp;quot;, &amp;quot;Neighbors&amp;quot;))
ggplot(usa46_df, aes(x = State_j, y = State_i, fill = Connection)) +
geom_tile(color = &amp;quot;white&amp;quot;, linewidth = 0.1) +
scale_fill_manual(values = c(&amp;quot;Not neighbors&amp;quot; = &amp;quot;gray95&amp;quot;,
&amp;quot;Neighbors&amp;quot; = &amp;quot;#6a9bcc&amp;quot;)) +
labs(title = &amp;quot;Binary Contiguity Matrix of 46 US States&amp;quot;,
x = &amp;quot;State j&amp;quot;, y = &amp;quot;State i&amp;quot;) +
theme_minimal()
&lt;/code>&lt;/pre>
&lt;p>&lt;img src="r_SDPDmod_fig1_weight_matrix.png" alt="Binary contiguity matrix heatmap showing neighborhood structure of 46 US states">&lt;/p>
&lt;p>The sparse pattern confirms that most state pairs are &lt;em>not&lt;/em> neighbors &amp;mdash; only 8.9% of cells are colored. With state abbreviations on the axes, you can verify specific neighborhood relationships: California (CA) neighbors Arizona (AZ), Nevada (NV), and Oregon (OR); Missouri (MO) has the most neighbors at 8. The sparsity is typical of contiguity-based weight matrices and means that spatial effects operate through a relatively small number of direct neighbor relationships. The row-normalized version ensures that each state&amp;rsquo;s spatial lag is an equally weighted average of its neighbors, regardless of whether a state has 2 neighbors or 8.&lt;/p>
&lt;h3 id="42-alternative-weight-matrices">4.2 Alternative weight matrices&lt;/h3>
&lt;p>The SDPDmod package provides several functions for constructing weight matrices from scratch: &lt;code>mOrdNbr()&lt;/code> for higher-order contiguity from shapefiles, &lt;code>mNearestN()&lt;/code> for k-nearest neighbors, &lt;code>InvDistMat()&lt;/code> for inverse distance, and &lt;code>DistWMat()&lt;/code> as a unified wrapper. Since our results may depend on the choice of $W$, we construct a &lt;strong>2nd-order contiguity matrix&lt;/strong> as a robustness check. This matrix treats states as neighbors if they share a border &lt;em>or&lt;/em> share a common neighbor (friends-of-friends).&lt;/p>
&lt;pre>&lt;code class="language-r"># 2nd-order contiguity: states reachable in 2 steps
W2_raw &amp;lt;- (usa46 %*% usa46) &amp;gt; 0 # indicator for 2-step reachability
W2_combined &amp;lt;- W2_raw * 1
diag(W2_combined) &amp;lt;- 0 # remove self-connections
W2 &amp;lt;- rownor(W2_combined)
cat(&amp;quot;Original W non-zero entries:&amp;quot;, sum(usa46 != 0), &amp;quot;\n&amp;quot;)
cat(&amp;quot;2nd-order W non-zero entries:&amp;quot;, sum(W2_combined != 0), &amp;quot;\n&amp;quot;)
cat(&amp;quot;Avg neighbors (original):&amp;quot;, round(mean(rowSums(usa46)), 2), &amp;quot;\n&amp;quot;)
cat(&amp;quot;Avg neighbors (2nd-order):&amp;quot;, round(mean(rowSums(W2_combined)), 2), &amp;quot;\n&amp;quot;)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Original W non-zero entries: 188
2nd-order W non-zero entries: 486
Avg neighbors (original): 4.09
Avg neighbors (2nd-order): 10.57
&lt;/code>&lt;/pre>
&lt;p>The 2nd-order matrix is much denser: 486 non-zero entries versus 188, with an average of 10.6 neighbors per state instead of 4.1. This broader definition of &amp;ldquo;neighbor&amp;rdquo; captures indirect spatial relationships &amp;mdash; for example, Illinois and Kentucky are not direct contiguous neighbors, but they share Indiana as a common neighbor. We will use this alternative $W$ for a robustness check in Section 11.&lt;/p>
&lt;h2 id="5-bayesian-model-comparison-with-blmpsdpd">5. Bayesian Model Comparison with &lt;code>blmpSDPD()&lt;/code>&lt;/h2>
&lt;h3 id="51-the-spatial-model-family">5.1 The spatial model family&lt;/h3>
&lt;p>Before estimating any single model, we use Bayesian model comparison to let the data tell us which spatial specification fits best. The SDPDmod package supports six models that differ in &lt;em>where&lt;/em> spatial dependence enters the equation. The general spatial panel model takes the form:&lt;/p>
&lt;p>$$y_t = \rho W y_t + X_t \beta + W X_t \theta + u_t, \quad u_t = \lambda W u_t + \epsilon_t$$&lt;/p>
&lt;p>In words, the outcome $y_t$ can depend on neighbors' outcomes (through $\rho$), on spatially lagged covariates (through $\theta$), and spatial correlation can appear in the error term (through $\lambda$). Different restrictions on these parameters yield different models:&lt;/p>
&lt;pre>&lt;code class="language-mermaid">graph TD
GNS[&amp;quot;General Nesting&amp;lt;br/&amp;gt;ρ, θ, λ&amp;quot;] --&amp;gt;|&amp;quot;λ = 0&amp;quot;| SDM[&amp;quot;SDM&amp;lt;br/&amp;gt;ρ, θ&amp;quot;]
GNS --&amp;gt;|&amp;quot;θ = 0&amp;quot;| SAC[&amp;quot;SAC&amp;lt;br/&amp;gt;ρ, λ&amp;quot;]
GNS --&amp;gt;|&amp;quot;ρ = 0&amp;quot;| SDEM[&amp;quot;SDEM&amp;lt;br/&amp;gt;θ, λ&amp;quot;]
SDM --&amp;gt;|&amp;quot;θ = 0&amp;quot;| SAR[&amp;quot;SAR&amp;lt;br/&amp;gt;ρ&amp;quot;]
SDM --&amp;gt;|&amp;quot;ρ = 0&amp;quot;| SLX[&amp;quot;SLX&amp;lt;br/&amp;gt;θ&amp;quot;]
SAC --&amp;gt;|&amp;quot;λ = 0&amp;quot;| SAR
SDEM --&amp;gt;|&amp;quot;ρ = 0&amp;quot;| SEM[&amp;quot;SEM&amp;lt;br/&amp;gt;λ&amp;quot;]
SDEM --&amp;gt;|&amp;quot;λ = 0&amp;quot;| SLX
SAR --&amp;gt;|&amp;quot;ρ = 0&amp;quot;| OLS[&amp;quot;OLS&amp;lt;br/&amp;gt;No spatial&amp;quot;]
SEM --&amp;gt;|&amp;quot;λ = 0&amp;quot;| OLS
SLX --&amp;gt;|&amp;quot;θ = 0&amp;quot;| OLS
style SDM fill:#d97757,stroke:#141413,color:#fff
style SAR fill:#6a9bcc,stroke:#141413,color:#fff
style SEM fill:#6a9bcc,stroke:#141413,color:#fff
style SDEM fill:#6a9bcc,stroke:#141413,color:#fff
style SLX fill:#6a9bcc,stroke:#141413,color:#fff
style OLS fill:#141413,stroke:#141413,color:#fff
style GNS fill:#00d4c8,stroke:#141413,color:#fff
style SAC fill:#00d4c8,stroke:#141413,color:#fff
&lt;/code>&lt;/pre>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Model&lt;/th>
&lt;th>Equation&lt;/th>
&lt;th>Key Parameters&lt;/th>
&lt;th>Interpretation&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>OLS&lt;/td>
&lt;td>$y_t = X_t \beta + \epsilon_t$&lt;/td>
&lt;td>None spatial&lt;/td>
&lt;td>No spatial dependence&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SAR&lt;/td>
&lt;td>$y_t = \rho W y_t + X_t \beta + \epsilon_t$&lt;/td>
&lt;td>$\rho$&lt;/td>
&lt;td>Neighbors' outcomes affect own outcome&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SEM&lt;/td>
&lt;td>$y_t = X_t \beta + u_t$, $u_t = \lambda W u_t + \epsilon_t$&lt;/td>
&lt;td>$\lambda$&lt;/td>
&lt;td>Spatial correlation in unobservables&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SLX&lt;/td>
&lt;td>$y_t = X_t \beta + W X_t \theta + \epsilon_t$&lt;/td>
&lt;td>$\theta$&lt;/td>
&lt;td>Neighbors' covariates affect own outcome&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SDM&lt;/td>
&lt;td>$y_t = \rho W y_t + X_t \beta + W X_t \theta + \epsilon_t$&lt;/td>
&lt;td>$\rho, \theta$&lt;/td>
&lt;td>Both neighbors' outcomes and covariates matter&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SDEM&lt;/td>
&lt;td>$y_t = X_t \beta + W X_t \theta + u_t$, $u_t = \lambda W u_t + \epsilon_t$&lt;/td>
&lt;td>$\theta, \lambda$&lt;/td>
&lt;td>Spatially lagged X plus spatial errors&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The &lt;a href="https://rdrr.io/cran/SDPDmod/man/blmpSDPD.html" target="_blank" rel="noopener">&lt;code>blmpSDPD()&lt;/code>&lt;/a> function computes Bayesian log-marginal posterior probabilities for each model. Unlike classical hypothesis tests that compare models pairwise, this approach assigns a probability to every candidate model simultaneously, making it straightforward to assess which specification the data favors.&lt;/p>
&lt;h3 id="52-static-comparison-with-individual-fixed-effects">5.2 Static comparison with individual fixed effects&lt;/h3>
&lt;p>We begin by comparing all six models under a static specification with individual (state) fixed effects only. This controls for time-invariant differences across states &amp;mdash; such as tobacco culture or geographic remoteness &amp;mdash; but does not control for common time trends like federal tax changes.&lt;/p>
&lt;pre>&lt;code class="language-r">res_ind &amp;lt;- blmpSDPD(formula = logc ~ logp + logy, data = data1, W = W,
index = c(&amp;quot;state&amp;quot;, &amp;quot;year&amp;quot;),
model = list(&amp;quot;ols&amp;quot;, &amp;quot;sar&amp;quot;, &amp;quot;sdm&amp;quot;, &amp;quot;sem&amp;quot;, &amp;quot;sdem&amp;quot;, &amp;quot;slx&amp;quot;),
effect = &amp;quot;individual&amp;quot;)
res_ind
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Log-marginal posteriors:
ols sar sdm sem sdem slx
1 884.7551 938.6934 1046.487 993.192 1039.671 930.0585
Model probabilities:
ols sar sdm sem sdem slx
1 0 0 0.9989 0 0.0011 0
&lt;/code>&lt;/pre>
&lt;p>With individual fixed effects, the SDM receives a posterior probability of 99.89%, dominating all other specifications. The SDEM gets only 0.11%, and the remaining models receive essentially zero probability. This overwhelming support for the SDM indicates that both the spatial lag of the dependent variable ($\rho W y$) and the spatial lags of covariates ($W X \theta$) are important for explaining cigarette consumption &amp;mdash; neighbors' prices and income matter above and beyond neighbors' consumption levels.&lt;/p>
&lt;h3 id="53-static-comparison-with-two-way-fixed-effects">5.3 Static comparison with two-way fixed effects&lt;/h3>
&lt;p>Adding time fixed effects controls for common shocks that affect all states simultaneously, such as national anti-smoking campaigns or federal excise tax changes. This typically absorbs much of the cross-sectional variation, so we might expect the model rankings to shift.&lt;/p>
&lt;pre>&lt;code class="language-r">res_tw &amp;lt;- blmpSDPD(formula = logc ~ logp + logy, data = data1, W = W,
index = c(&amp;quot;state&amp;quot;, &amp;quot;year&amp;quot;),
model = list(&amp;quot;ols&amp;quot;, &amp;quot;sar&amp;quot;, &amp;quot;sdm&amp;quot;, &amp;quot;sem&amp;quot;, &amp;quot;sdem&amp;quot;, &amp;quot;slx&amp;quot;),
effect = &amp;quot;twoways&amp;quot;,
prior = &amp;quot;beta&amp;quot;) # beta prior concentrates probability near moderate rho values
res_tw
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Log-marginal posteriors:
ols sar sdm sem sdem slx
1 1076.602 1095.993 1100.727 1099.415 1100.621 1080.323
Model probabilities:
ols sar sdm sem sdem slx
1 0 0.004 0.4592 0.1237 0.4131 0
&lt;/code>&lt;/pre>
&lt;p>With two-way fixed effects and a beta prior, the race tightens considerably. The SDM still leads with 45.92% probability, but the SDEM is close behind at 41.31%. The SEM receives 12.37%, while the SAR drops to just 0.4%. This tells us that spatial effects in the covariates ($\theta$) remain important, but there is genuine uncertainty about whether the spatial lag of the dependent variable ($\rho$) or the spatial error term ($\lambda$) best captures the remaining spatial dependence.&lt;/p>
&lt;h3 id="54-dynamic-comparison-with-two-way-fixed-effects">5.4 Dynamic comparison with two-way fixed effects&lt;/h3>
&lt;p>Cigarette consumption is highly persistent over time &amp;mdash; smokers who consumed heavily last year tend to do so again this year. Dynamic models add the lagged dependent variable $y_{t-1}$ and potentially its spatial lag $W y_{t-1}$ to capture this habit persistence.&lt;/p>
&lt;pre>&lt;code class="language-r">res_dyn &amp;lt;- blmpSDPD(formula = logc ~ logp + logy, data = data1, W = W,
index = c(&amp;quot;state&amp;quot;, &amp;quot;year&amp;quot;),
model = list(&amp;quot;sar&amp;quot;, &amp;quot;sdm&amp;quot;, &amp;quot;sem&amp;quot;, &amp;quot;sdem&amp;quot;, &amp;quot;slx&amp;quot;),
effect = &amp;quot;twoways&amp;quot;,
ldet = &amp;quot;mc&amp;quot;, # Monte Carlo approximation for the log-determinant (faster for dynamic models)
dynamic = TRUE,
prior = &amp;quot;uniform&amp;quot;) # uniform prior assigns equal weight to all valid rho values
res_dyn
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Log-marginal posteriors:
sar sdm sem sdem slx
1 1987.651 1986.906 1987.799 1986.924 1987.388
Model probabilities:
sar sdm sem sdem slx
1 0.2573 0.1221 0.2984 0.1243 0.1979
&lt;/code>&lt;/pre>
&lt;p>The dynamic comparison produces a dramatically different picture: all five models receive similar probabilities, with the SEM slightly ahead at 29.84%, followed by SAR at 25.73% and SLX at 19.79%. The log-marginal posteriors are nearly identical (within 1 unit), reflecting the fact that once temporal dynamics are included, the remaining spatial signal is much weaker. The lagged dependent variable absorbs much of the persistence that spatial models previously captured.&lt;/p>
&lt;h3 id="55-summary-of-model-comparison">5.5 Summary of model comparison&lt;/h3>
&lt;p>The figure below summarizes the posterior probabilities across all three specification comparisons (see &lt;code>analysis.R&lt;/code> for the full figure code).&lt;/p>
&lt;p>&lt;img src="r_SDPDmod_fig2_model_comparison.png" alt="Bayesian model probabilities across three specifications: static individual FE, static two-way FE, and dynamic two-way FE">&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Specification&lt;/th>
&lt;th>Top Model&lt;/th>
&lt;th>Probability&lt;/th>
&lt;th>Runner-up&lt;/th>
&lt;th>Probability&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Static, Individual FE&lt;/td>
&lt;td>SDM&lt;/td>
&lt;td>99.89%&lt;/td>
&lt;td>SDEM&lt;/td>
&lt;td>0.11%&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Static, Two-way FE&lt;/td>
&lt;td>SDM&lt;/td>
&lt;td>45.92%&lt;/td>
&lt;td>SDEM&lt;/td>
&lt;td>41.31%&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Dynamic, Two-way FE&lt;/td>
&lt;td>SEM&lt;/td>
&lt;td>29.84%&lt;/td>
&lt;td>SAR&lt;/td>
&lt;td>25.73%&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The Bayesian comparison reveals three key insights. First, spatial dependence is unambiguously present &amp;mdash; OLS and SLX never win. Second, the SDM is the preferred static model, which means both the spatial lag of $y$ and the spatial lags of $X$ contribute to explaining cigarette consumption. Third, adding dynamics substantially weakens the ability to discriminate among spatial specifications, because the lagged dependent variable captures much of the temporal persistence that spatial lags previously absorbed. Given that the SDM leads in two of three comparisons and nests the SAR as a special case, we will estimate both the SAR and SDM in the sections that follow, with and without dynamics.&lt;/p>
&lt;h2 id="6-non-spatial-baseline">6. Non-Spatial Baseline&lt;/h2>
&lt;p>Before introducing spatial models, we establish a benchmark using a standard &lt;strong>two-way fixed effects&lt;/strong> panel regression with no spatial terms. This is the model that most applied researchers would start with &amp;mdash; it controls for state-specific and year-specific unobserved heterogeneity but assumes that each state&amp;rsquo;s consumption depends only on its own prices and income, with no spillovers from neighbors.&lt;/p>
&lt;pre>&lt;code class="language-r">pdata &amp;lt;- pdata.frame(data1, index = c(&amp;quot;state&amp;quot;, &amp;quot;year&amp;quot;))
mod_fe &amp;lt;- plm(logc ~ logp + logy, data = pdata, model = &amp;quot;within&amp;quot;,
effect = &amp;quot;twoways&amp;quot;)
summary(mod_fe)$coefficients
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text"> Estimate Std. Error t-value Pr(&amp;gt;|t|)
logp -1.0348844 0.04151906 -24.92553 1.881060e-112
logy 0.5285428 0.04658276 11.34632 1.603837e-28
&lt;/code>&lt;/pre>
&lt;p>The non-spatial two-way FE model estimates a price elasticity of -1.035 and an income elasticity of 0.529, both highly significant. The within R-squared is 0.394, meaning that price and income explain about 39% of the within-state, within-year variation in cigarette consumption after removing fixed effects. These estimates serve as the benchmark against which we measure the value added by spatial models. As we will see, the SAR and SDM models produce similar &lt;em>direct&lt;/em> price effects (around -1.00) but reveal substantial &lt;em>indirect&lt;/em> (spillover) effects that the non-spatial model entirely misses &amp;mdash; the total price elasticity in the SDM is -1.23, about 19% larger than the non-spatial estimate.&lt;/p>
&lt;h2 id="7-static-sar-model-estimation">7. Static SAR Model Estimation&lt;/h2>
&lt;h3 id="71-sar-with-individual-fixed-effects">7.1 SAR with individual fixed effects&lt;/h3>
&lt;p>The Spatial Autoregressive (SAR) model adds a single spatial parameter $\rho$ that captures how much a state&amp;rsquo;s cigarette consumption depends on the weighted average of its neighbors' consumption. The model is:&lt;/p>
&lt;p>$$y_t = \rho W y_t + X_t \beta + \mu_i + \epsilon_t$$&lt;/p>
&lt;p>In words, cigarette consumption in state $i$ depends on (1) the average consumption of neighboring states (weighted by $W$, with strength $\rho$), (2) the state&amp;rsquo;s own price and income ($X_t \beta$), and (3) a state-specific intercept ($\mu_i$). The &lt;a href="https://rdrr.io/cran/SDPDmod/man/SDPDm.html" target="_blank" rel="noopener">&lt;code>SDPDm()&lt;/code>&lt;/a> function estimates this model by maximum likelihood. The &lt;code>index&lt;/code> argument specifies the panel identifiers, &lt;code>model = &amp;quot;sar&amp;quot;&lt;/code> selects the spatial lag specification, and &lt;code>effect = &amp;quot;individual&amp;quot;&lt;/code> includes state fixed effects.&lt;/p>
&lt;pre>&lt;code class="language-r">mod_sar_ind &amp;lt;- SDPDm(formula = logc ~ logp + logy, data = data1, W = W,
index = c(&amp;quot;state&amp;quot;, &amp;quot;year&amp;quot;),
model = &amp;quot;sar&amp;quot;,
effect = &amp;quot;individual&amp;quot;)
summary(mod_sar_ind)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">sar panel model with individual fixed effects
Spatial autoregressive coefficient:
Estimate Std. Error t-value Pr(&amp;gt;|t|)
rho 0.297576 0.028444 10.462 &amp;lt; 2.2e-16 ***
Coefficients:
Estimate Std. Error t-value Pr(&amp;gt;|t|)
logp -0.5320053 0.0254445 -20.9085 &amp;lt;2e-16 ***
logy -0.0007088 0.0152139 -0.0466 0.9628
&lt;/code>&lt;/pre>
&lt;p>The spatial autoregressive coefficient $\rho = 0.298$ is highly significant ($t = 10.46$), confirming strong spatial dependence in cigarette consumption. A state&amp;rsquo;s consumption is positively influenced by its neighbors' consumption levels. The price elasticity is -0.532 ($t = -20.91$), meaning a 1% increase in real price reduces consumption by about 0.53%. However, the income coefficient is essentially zero (-0.001, $p = 0.96$), suggesting that with only state fixed effects, income variation does not significantly predict consumption &amp;mdash; likely because state fixed effects absorb cross-sectional income differences, while the within-state time variation in income is confounded with common time trends.&lt;/p>
&lt;h3 id="72-sar-with-two-way-fixed-effects">7.2 SAR with two-way fixed effects&lt;/h3>
&lt;p>Adding time fixed effects controls for year-specific shocks common to all states and typically changes the coefficient estimates substantially.&lt;/p>
&lt;pre>&lt;code class="language-r">mod_sar_tw &amp;lt;- SDPDm(formula = logc ~ logp + logy, data = data1, W = W,
index = c(&amp;quot;state&amp;quot;, &amp;quot;year&amp;quot;),
model = &amp;quot;sar&amp;quot;,
effect = &amp;quot;twoways&amp;quot;)
summary(mod_sar_tw)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">sar panel model with twoways fixed effects
Spatial autoregressive coefficient:
Estimate Std. Error t-value Pr(&amp;gt;|t|)
rho 0.18659 0.02863 6.5173 7.159e-11 ***
Coefficients:
Estimate Std. Error t-value Pr(&amp;gt;|t|)
logp -0.994860 0.039906 -24.930 &amp;lt; 2.2e-16 ***
logy 0.463555 0.046019 10.073 &amp;lt; 2.2e-16 ***
&lt;/code>&lt;/pre>
&lt;p>With two-way fixed effects, three things change. First, the spatial coefficient drops from 0.298 to 0.187 &amp;mdash; still highly significant but weaker, because time fixed effects absorb some of the common spatial trends. Second, the price elasticity nearly doubles from -0.53 to -0.99, suggesting that the individual-FE-only model was biased by confounding time trends with prices. Third, income becomes strongly significant (0.464, $t = 10.07$): once common time trends are removed, higher real income is associated with &lt;em>more&lt;/em> cigarette consumption, consistent with cigarettes being a normal good at the state level.&lt;/p>
&lt;h3 id="73-impact-decomposition-for-static-sar">7.3 Impact decomposition for static SAR&lt;/h3>
&lt;p>In spatial models, the raw coefficients $\beta$ do not directly tell us how a change in one state&amp;rsquo;s price affects its own consumption. Because of the spatial feedback loop &amp;mdash; my consumption affects my neighbor&amp;rsquo;s, which in turn affects mine &amp;mdash; the actual effect is larger than $\beta$ alone. The &lt;a href="https://rdrr.io/cran/SDPDmod/man/impactsSDPDm.html" target="_blank" rel="noopener">&lt;code>impactsSDPDm()&lt;/code>&lt;/a> function decomposes the total effect into a &lt;strong>direct effect&lt;/strong> (impact on own state) and an &lt;strong>indirect effect&lt;/strong> (spillover to and from neighbors).&lt;/p>
&lt;pre>&lt;code class="language-r">imp_sar_tw &amp;lt;- impactsSDPDm(mod_sar_tw)
summary(imp_sar_tw)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Impact estimates for spatial (static) model
Direct:
Estimate Std. Error t-value Pr(&amp;gt;|t|)
logp -1.001155 0.038855 -25.767 &amp;lt; 2.2e-16 ***
logy 0.465947 0.044678 10.429 &amp;lt; 2.2e-16 ***
Indirect:
Estimate Std. Error t-value Pr(&amp;gt;|t|)
logp -0.223484 0.040877 -5.4672 4.571e-08 ***
logy 0.103540 0.018939 5.4670 4.578e-08 ***
Total:
Estimate Std. Error t-value Pr(&amp;gt;|t|)
logp -1.224639 0.060815 -20.137 &amp;lt; 2.2e-16 ***
logy 0.569487 0.052965 10.752 &amp;lt; 2.2e-16 ***
&lt;/code>&lt;/pre>
&lt;p>The impact decomposition reveals that a 1% increase in a state&amp;rsquo;s own real price reduces its consumption by 1.00% directly, plus an additional 0.22% through spatial feedback &amp;mdash; for a total price elasticity of -1.22. Think of it this way: when one state raises prices, its consumption drops, which in turn reduces the &amp;ldquo;pull&amp;rdquo; on neighboring states' consumption through the spatial lag, creating a ripple effect that feeds back to the original state. Similarly, a 1% income increase raises own-state consumption by 0.47% directly and by 0.10% through neighbors, for a total income elasticity of 0.57. The indirect effects are about 18% of the total effect, indicating economically meaningful spatial spillovers.&lt;/p>
&lt;h2 id="8-static-sdm-with-lee-yu-correction">8. Static SDM with Lee-Yu Correction&lt;/h2>
&lt;h3 id="81-sdm-with-two-way-fixed-effects">8.1 SDM with two-way fixed effects&lt;/h3>
&lt;p>The Spatial Durbin Model (SDM) extends the SAR by adding spatially lagged covariates $W X$, allowing neighbors' prices and income to directly affect a state&amp;rsquo;s consumption (beyond the indirect channel through $\rho W y$):&lt;/p>
&lt;p>$$y_t = \rho W y_t + X_t \beta + W X_t \theta + \mu_i + \gamma_t + \epsilon_t$$&lt;/p>
&lt;p>In words, this says that cigarette consumption depends on neighbors' consumption ($\rho$), own prices and income ($\beta$), &lt;em>and&lt;/em> neighbors' prices and income ($\theta$). Here $\mu_i$ captures state fixed effects and $\gamma_t$ captures time fixed effects. The SDM is the natural model when we believe that cross-border shopping responds directly to neighboring states' prices &amp;mdash; not just indirectly through neighbors' consumption levels.&lt;/p>
&lt;pre>&lt;code class="language-r">mod_sdm_tw &amp;lt;- SDPDm(formula = logc ~ logp + logy, data = data1, W = W,
index = c(&amp;quot;state&amp;quot;, &amp;quot;year&amp;quot;),
model = &amp;quot;sdm&amp;quot;,
effect = &amp;quot;twoways&amp;quot;)
summary(mod_sdm_tw)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">sdm panel model with twoways fixed effects
Spatial autoregressive coefficient:
Estimate Std. Error t-value Pr(&amp;gt;|t|)
rho 0.222591 0.032825 6.7812 1.192e-11 ***
Coefficients:
Estimate Std. Error t-value Pr(&amp;gt;|t|)
logp -1.002878 0.040094 -25.0134 &amp;lt; 2.2e-16 ***
logy 0.600876 0.057207 10.5036 &amp;lt; 2.2e-16 ***
W*logp 0.048490 0.080807 0.6001 0.5484546
W*logy -0.292794 0.078158 -3.7462 0.0001795 ***
&lt;/code>&lt;/pre>
&lt;p>The SDM reveals an interesting asymmetry. The spatial lag of price (&lt;code>W*logp = 0.049&lt;/code>) is not significant ($p = 0.55$), meaning that neighboring states' prices do not directly affect own consumption once the spatial lag of consumption ($\rho = 0.223$) is accounted for. However, the spatial lag of income (&lt;code>W*logy = -0.293&lt;/code>) is highly significant ($t = -3.75$): when neighboring states become richer, own-state consumption &lt;em>decreases&lt;/em>. This negative spillover in income may reflect a substitution effect &amp;mdash; as neighbors' incomes rise, their consumers may shift toward premium or out-of-state purchasing channels, reducing the spatial demand that pulls up consumption in the focal state.&lt;/p>
&lt;h3 id="82-sdm-with-lee-yu-bias-correction">8.2 SDM with Lee-Yu bias correction&lt;/h3>
&lt;p>Fixed effects in spatial panels create an &lt;strong>incidental parameter problem&lt;/strong>: the large number of fixed effects (46 states + 30 years = 76 parameters) introduces a small-sample bias in the maximum likelihood estimator, particularly for the spatial autoregressive coefficient $\rho$ and the variance $\sigma^2$. The Lee-Yu transformation (Lee and Yu, 2010) corrects this bias by orthogonally transforming the data to concentrate out the fixed effects before estimation.&lt;/p>
&lt;pre>&lt;code class="language-r">mod_sdm_ly &amp;lt;- SDPDm(formula = logc ~ logp + logy, data = data1, W = W,
index = c(&amp;quot;state&amp;quot;, &amp;quot;year&amp;quot;),
model = &amp;quot;sdm&amp;quot;,
effect = &amp;quot;twoways&amp;quot;,
LYtrans = TRUE)
summary(mod_sdm_ly)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">sdm panel model with twoways fixed effects
Spatial autoregressive coefficient:
Estimate Std. Error t-value Pr(&amp;gt;|t|)
rho 0.262211 0.032081 8.1735 2.996e-16 ***
Coefficients:
Estimate Std. Error t-value Pr(&amp;gt;|t|)
logp -1.001334 0.041121 -24.3509 &amp;lt; 2.2e-16 ***
logy 0.602729 0.058673 10.2726 &amp;lt; 2.2e-16 ***
W*logp 0.090779 0.082185 1.1046 0.2693
W*logy -0.313251 0.079982 -3.9165 8.983e-05 ***
&lt;/code>&lt;/pre>
&lt;p>The Lee-Yu correction increases $\rho$ from 0.223 to 0.262 &amp;mdash; a 17% upward correction, indicating that the uncorrected estimator underestimated spatial dependence. The slope coefficients change only marginally (the price coefficient moves from -1.003 to -1.001), which is expected with $T = 30$ years. For short panels ($T &amp;lt; 10$), the Lee-Yu correction would matter much more. We will use the Lee-Yu corrected version as our preferred static SDM.&lt;/p>
&lt;h3 id="83-comparison-sar-vs-sdm">8.3 Comparison: SAR vs. SDM&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Parameter&lt;/th>
&lt;th>FE (no spatial)&lt;/th>
&lt;th>SAR (Ind FE)&lt;/th>
&lt;th>SAR (TW FE)&lt;/th>
&lt;th>SDM (TW FE)&lt;/th>
&lt;th>SDM (TW FE, LY)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>$\rho$&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>0.298&lt;/td>
&lt;td>0.187&lt;/td>
&lt;td>0.223&lt;/td>
&lt;td>0.262&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>logp&lt;/td>
&lt;td>-1.035&lt;/td>
&lt;td>-0.532&lt;/td>
&lt;td>-0.995&lt;/td>
&lt;td>-1.003&lt;/td>
&lt;td>-1.001&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>logy&lt;/td>
&lt;td>0.529&lt;/td>
&lt;td>-0.001&lt;/td>
&lt;td>0.464&lt;/td>
&lt;td>0.601&lt;/td>
&lt;td>0.603&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>W*logp&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>0.049&lt;/td>
&lt;td>0.091&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>W*logy&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>-0.293&lt;/td>
&lt;td>-0.313&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$\hat{\sigma}^2$&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>0.0067&lt;/td>
&lt;td>0.0051&lt;/td>
&lt;td>0.0050&lt;/td>
&lt;td>0.0052&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Two patterns stand out. First, the price coefficient is remarkably stable across the SDM specifications (around -1.00), while it was biased in the SAR with individual FE only (-0.53). Second, adding the SDM terms increases the income coefficient from 0.46 (SAR) to 0.60 (SDM), because the negative spatial lag of income (&lt;code>W*logy&lt;/code> $\approx$ -0.31) absorbs part of the spatial income effect that the SAR was attributing to the spatial lag $\rho$.&lt;/p>
&lt;h3 id="84-impact-decomposition-for-static-sdm">8.4 Impact decomposition for static SDM&lt;/h3>
&lt;p>The impact decomposition for the SDM differs fundamentally from the SAR because the $W X$ terms create additional channels for indirect effects.&lt;/p>
&lt;pre>&lt;code class="language-r">imp_sdm_ly &amp;lt;- impactsSDPDm(mod_sdm_ly)
summary(imp_sdm_ly)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Impact estimates for spatial (static) model
Direct:
Estimate Std. Error t-value Pr(&amp;gt;|t|)
logp -1.010329 0.040149 -25.164 &amp;lt; 2.2e-16 ***
logy 0.588471 0.054940 10.711 &amp;lt; 2.2e-16 ***
Indirect:
Estimate Std. Error t-value Pr(&amp;gt;|t|)
logp -0.21925 0.09439 -2.3228 0.02019 *
logy -0.19721 0.09108 -2.1652 0.03037 *
Total:
Estimate Std. Error t-value Pr(&amp;gt;|t|)
logp -1.229575 0.105631 -11.6403 &amp;lt; 2.2e-16 ***
logy 0.391262 0.086184 4.5398 5.63e-06 ***
&lt;/code>&lt;/pre>
&lt;p>The SDM impact decomposition tells a richer story than the SAR. For price, the results are similar: a direct effect of -1.01 and an indirect (spillover) effect of -0.22, summing to a total price elasticity of -1.23. However, for income, the SDM flips the sign of the indirect effect: it is now &lt;em>negative&lt;/em> (-0.20) instead of positive (0.10 in the SAR). This means that when neighboring states' incomes rise, the focal state&amp;rsquo;s consumption actually &lt;em>decreases&lt;/em> &amp;mdash; consistent with the significant negative &lt;code>W*logy&lt;/code> coefficient we saw earlier. The total income elasticity in the SDM (0.39) is therefore lower than in the SAR (0.57), because the positive direct effect (0.59) is partially offset by the negative spillover (-0.20). This sign reversal of the income spillover is an important finding that the SAR cannot detect.&lt;/p>
&lt;h2 id="9-dynamic-spatial-panel-models">9. Dynamic Spatial Panel Models&lt;/h2>
&lt;h3 id="91-why-dynamics-habit-persistence-in-cigarette-consumption">9.1 Why dynamics? Habit persistence in cigarette consumption&lt;/h3>
&lt;p>Cigarette consumption is strongly habit-forming. Nicotine addiction creates a direct link between past and present consumption: last year&amp;rsquo;s smokers are very likely to be this year&amp;rsquo;s smokers. Ignoring this temporal persistence in a static model means that the spatial coefficient $\rho$ must absorb &lt;em>both&lt;/em> spatial spillovers and the serial correlation in consumption patterns, leading to biased estimates of the true spatial effect. Dynamic models explicitly include the lagged dependent variable $y_{t-1}$ (with coefficient $\tau$, capturing &lt;strong>habit persistence&lt;/strong>) and optionally its spatial lag $W y_{t-1}$ (with coefficient $\eta$, capturing &lt;strong>spatiotemporal diffusion&lt;/strong>):&lt;/p>
&lt;p>$$y_t = \rho W y_t + \tau y_{t-1} + \eta W y_{t-1} + X_t \beta + W X_t \theta + \mu_i + \gamma_t + \epsilon_t$$&lt;/p>
&lt;p>In words, this equation says that today&amp;rsquo;s cigarette consumption depends on: neighbors' current consumption ($\rho$), own past consumption ($\tau$, habit persistence), neighbors' past consumption ($\eta$, spatiotemporal diffusion), own prices and income ($\beta$), and neighbors' prices and income ($\theta$). Here $y_{t-1}$ corresponds to &lt;code>logc(t-1)&lt;/code> in the output, and $Wy_{t-1}$ corresponds to &lt;code>W*logc(t-1)&lt;/code>.&lt;/p>
&lt;h3 id="92-dynamic-sar-with-temporal-lag-only">9.2 Dynamic SAR with temporal lag only&lt;/h3>
&lt;p>We start by adding only the temporal lag $y_{t-1}$ without the spatiotemporal lag $W y_{t-1}$, to isolate the effect of habit persistence on the spatial coefficient.&lt;/p>
&lt;pre>&lt;code class="language-r">mod_dsar_tl &amp;lt;- SDPDm(formula = logc ~ logp + logy, data = data1, W = W,
index = c(&amp;quot;state&amp;quot;, &amp;quot;year&amp;quot;),
model = &amp;quot;sar&amp;quot;,
effect = &amp;quot;twoways&amp;quot;,
LYtrans = TRUE,
dynamic = TRUE,
tlaginfo = list(ind = NULL, tl = TRUE, stl = FALSE))
summary(mod_dsar_tl)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">sar dynamic panel model with twoways fixed effects
Spatial autoregressive coefficient:
Estimate Std. Error t-value Pr(&amp;gt;|t|)
rho 0.0095932 0.0169929 0.5645 0.5724
Coefficients:
Estimate Std. Error t-value Pr(&amp;gt;|t|)
logc(t-1) 0.866212 0.012785 67.7523 &amp;lt; 2.2e-16 ***
logp -0.254617 0.023047 -11.0478 &amp;lt; 2.2e-16 ***
logy 0.084437 0.023719 3.5598 0.0003711 ***
&lt;/code>&lt;/pre>
&lt;p>This result is striking. The temporal lag coefficient $\tau = 0.866$ is enormous ($t = 67.75$), confirming that cigarette consumption is extremely persistent &amp;mdash; about 87% of last year&amp;rsquo;s consumption carries over to this year. More remarkably, the spatial autoregressive coefficient $\rho$ collapses from 0.262 (static SDM) to just 0.010 and becomes &lt;em>non-significant&lt;/em> ($p = 0.57$). This suggests that what appeared to be contemporaneous spatial dependence in the static model was largely a proxy for temporal persistence: states that consumed heavily in the past continue to do so, and neighboring states happen to share similar histories. The short-run price elasticity also drops sharply from -1.00 to -0.25, because the lagged dependent variable now captures the cumulative effect of past prices.&lt;/p>
&lt;h3 id="93-dynamic-sar-with-temporal-and-spatiotemporal-lags">9.3 Dynamic SAR with temporal and spatiotemporal lags&lt;/h3>
&lt;p>Adding the spatiotemporal lag $W y_{t-1}$ allows us to test whether neighboring states' &lt;em>past&lt;/em> consumption patterns affect current consumption.&lt;/p>
&lt;pre>&lt;code class="language-r">mod_dsar_full &amp;lt;- SDPDm(formula = logc ~ logp + logy, data = data1, W = W,
index = c(&amp;quot;state&amp;quot;, &amp;quot;year&amp;quot;),
model = &amp;quot;sar&amp;quot;,
effect = &amp;quot;twoways&amp;quot;,
LYtrans = TRUE,
dynamic = TRUE,
tlaginfo = list(ind = NULL, tl = TRUE, stl = TRUE))
summary(mod_dsar_full)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">sar dynamic panel model with twoways fixed effects
Spatial autoregressive coefficient:
Estimate Std. Error t-value Pr(&amp;gt;|t|)
rho 0.703004 0.021363 32.907 &amp;lt; 2.2e-16 ***
Coefficients:
Estimate Std. Error t-value Pr(&amp;gt;|t|)
logc(t-1) 0.882056 0.013012 67.789 &amp;lt; 2e-16 ***
W*logc(t-1) -0.727317 0.026033 -27.938 &amp;lt; 2e-16 ***
logp -0.243591 0.023337 -10.438 &amp;lt; 2e-16 ***
logy 0.055595 0.023933 2.323 0.02018 *
&lt;/code>&lt;/pre>
&lt;p>Adding the spatiotemporal lag dramatically changes the picture. The spatial coefficient $\rho$ jumps to 0.703, and the spatiotemporal lag $\eta = -0.727$ is strongly negative ($t = -27.94$). The temporal lag $\tau = 0.882$ remains dominant. The large $\rho$ combined with the nearly equal-and-opposite $\eta$ suggests a complex dynamic pattern: states with high &lt;em>current&lt;/em> neighbor consumption tend to have higher own consumption ($\rho &amp;gt; 0$), but states whose neighbors consumed heavily &lt;em>last year&lt;/em> tend to have &lt;em>lower&lt;/em> current consumption ($\eta &amp;lt; 0$). However, the near-cancellation of $\rho$ and $\eta$ may also indicate multicollinearity between $Wy_t$ and $Wy_{t-1}$, making the individual coefficients hard to interpret reliably. The dynamic SDM in Section 9.4, which adds covariates' spatial lags, provides a more stable decomposition.&lt;/p>
&lt;h3 id="94-dynamic-sdm-with-both-lags-and-lee-yu-correction">9.4 Dynamic SDM with both lags and Lee-Yu correction&lt;/h3>
&lt;p>The most general model combines all elements: spatial lag of $y$, temporal lag, spatiotemporal lag, and spatial lags of $X$, all with Lee-Yu bias correction.&lt;/p>
&lt;pre>&lt;code class="language-r">mod_dsdm &amp;lt;- SDPDm(formula = logc ~ logp + logy, data = data1, W = W,
index = c(&amp;quot;state&amp;quot;, &amp;quot;year&amp;quot;),
model = &amp;quot;sdm&amp;quot;,
effect = &amp;quot;twoways&amp;quot;,
LYtrans = TRUE,
dynamic = TRUE,
tlaginfo = list(ind = NULL, tl = TRUE, stl = TRUE))
summary(mod_dsdm)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">sdm dynamic panel model with twoways fixed effects
Spatial autoregressive coefficient:
Estimate Std. Error t-value Pr(&amp;gt;|t|)
rho 0.162189 0.036753 4.4129 1.02e-05 ***
Coefficients:
Estimate Std. Error t-value Pr(&amp;gt;|t|)
logc(t-1) 0.864412 0.012879 67.1163 &amp;lt; 2.2e-16 ***
W*logc(t-1) -0.096270 0.038810 -2.4805 0.0131186 *
logp -0.270872 0.023145 -11.7031 &amp;lt; 2.2e-16 ***
logy 0.104262 0.029783 3.5007 0.0004641 ***
W*logp 0.195595 0.043870 4.4585 8.254e-06 ***
W*logy -0.032464 0.039520 -0.8215 0.4113891
&lt;/code>&lt;/pre>
&lt;p>The dynamic SDM produces the most nuanced picture. Habit persistence remains dominant ($\tau = 0.864$, $t = 67.12$). The spatial coefficient $\rho = 0.162$ is significant but much smaller than in the static model ($\rho = 0.262$), confirming that static models overstate contemporaneous spatial dependence by conflating it with temporal persistence. The spatiotemporal lag is weakly significant ($\eta = -0.096$, $p = 0.013$). Notably, the spatial lag of price (&lt;code>W*logp = 0.196&lt;/code>) is now &lt;em>positive&lt;/em> and significant ($t = 4.46$), a reversal from the static SDM where it was not significant. This positive coefficient means that when neighboring states' prices rise, own-state consumption &lt;em>increases&lt;/em> &amp;mdash; precisely the cross-border shopping effect we hypothesized. Smokers respond to neighbors' price increases by purchasing more in their own (now relatively cheaper) state. The spatial lag of income (&lt;code>W*logy = -0.032&lt;/code>) is no longer significant once dynamics are included.&lt;/p>
&lt;h3 id="95-impact-decomposition-short-run-and-long-run-effects">9.5 Impact decomposition: short-run and long-run effects&lt;/h3>
&lt;p>For dynamic models, &lt;code>impactsSDPDm()&lt;/code> separates effects into &lt;strong>short-run&lt;/strong> (immediate, one-period) and &lt;strong>long-run&lt;/strong> (cumulative, steady-state) impacts. The long-run effects account for the feedback loop through the lagged dependent variable: a price change today affects consumption today, which affects consumption next year (through $\tau$), which feeds back again, and so on until a new equilibrium is reached.&lt;/p>
&lt;pre>&lt;code class="language-r">imp_dsdm &amp;lt;- impactsSDPDm(mod_dsdm)
summary(imp_dsdm)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Impact estimates for spatial dynamic model
========================================================
Short-term
Direct:
Estimate Std. Error t-value Pr(&amp;gt;|t|)
logp -0.261569 0.022830 -11.457 &amp;lt; 2.2e-16 ***
logy 0.101759 0.029667 3.430 0.0006035 ***
Indirect:
Estimate Std. Error t-value Pr(&amp;gt;|t|)
logp 0.178932 0.046861 3.8183 0.0001344 ***
logy -0.015109 0.042210 -0.3579 0.7203812
Total:
Estimate Std. Error t-value Pr(&amp;gt;|t|)
logp -0.082637 0.052143 -1.5848 0.1130
logy 0.086650 0.037890 2.2868 0.0222 *
========================================================
Long-term
Direct:
Estimate Std. Error t-value Pr(&amp;gt;|t|)
logp -1.92836 0.20580 -9.3702 &amp;lt; 2.2e-16 ***
logy 0.80149 0.22655 3.5378 0.0004034 ***
Indirect:
Estimate Std. Error t-value Pr(&amp;gt;|t|)
logp 0.91054 0.58271 1.5626 0.1181
logy 0.48361 1.54612 0.3128 0.7544
Total:
Estimate Std. Error t-value Pr(&amp;gt;|t|)
logp -1.01783 0.66733 -1.5252 0.1272
logy 1.28510 1.59825 0.8041 0.4214
&lt;/code>&lt;/pre>
&lt;p>The gap between short-run and long-run effects is dramatic. The &lt;strong>short-run direct price elasticity&lt;/strong> is only -0.26, meaning that a 1% price increase immediately reduces consumption by just 0.26%. But the &lt;strong>long-run direct price elasticity&lt;/strong> is -1.93 &amp;mdash; more than seven times larger &amp;mdash; because the habit persistence mechanism ($\tau = 0.864$) amplifies the initial shock over time. Think of it as a snowball effect: a small reduction today accumulates year after year because lower consumption this year leads to lower consumption next year, and so on.&lt;/p>
&lt;p>The short-run indirect (spillover) effect of price is &lt;em>positive&lt;/em> (0.179): when a state raises its prices, neighboring states' consumption increases in the short run, consistent with cross-border shopping. This positive spillover partly offsets the direct negative effect, making the short-run &lt;em>total&lt;/em> price elasticity (-0.083) small and statistically non-significant. In the long run, the indirect price effect remains positive (0.911) but becomes imprecisely estimated and non-significant, while the direct effect (-1.928) dominates. The long-run total effects for both price and income are estimated with large standard errors, reflecting the uncertainty inherent in extrapolating dynamic effects to the steady state. The non-significance of these long-run totals means that, despite large point estimates, we cannot reliably predict the net cumulative impact of price or income changes across the full spatial system. Note that the long-run effects assume the system reaches a stable equilibrium, which requires the stationarity condition $|\tau + \rho \eta| &amp;lt; 1$ to hold.&lt;/p>
&lt;h3 id="96-comparison-of-dynamic-specifications">9.6 Comparison of dynamic specifications&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Parameter&lt;/th>
&lt;th>Static SDM (LY)&lt;/th>
&lt;th>Dyn SAR (tl)&lt;/th>
&lt;th>Dyn SAR (tl+stl)&lt;/th>
&lt;th>Dyn SDM (LY)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>$\rho$&lt;/td>
&lt;td>0.262&lt;/td>
&lt;td>0.010&lt;/td>
&lt;td>0.703&lt;/td>
&lt;td>0.162&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$\tau$ (logc_{t-1})&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>0.866&lt;/td>
&lt;td>0.882&lt;/td>
&lt;td>0.864&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$\eta$ (W*logc_{t-1})&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>-0.727&lt;/td>
&lt;td>-0.096&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>logp&lt;/td>
&lt;td>-1.001&lt;/td>
&lt;td>-0.255&lt;/td>
&lt;td>-0.244&lt;/td>
&lt;td>-0.271&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>logy&lt;/td>
&lt;td>0.603&lt;/td>
&lt;td>0.084&lt;/td>
&lt;td>0.056&lt;/td>
&lt;td>0.104&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>W*logp&lt;/td>
&lt;td>0.091&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>0.196&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>W*logy&lt;/td>
&lt;td>-0.313&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>-0.032&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$\hat{\sigma}^2$&lt;/td>
&lt;td>0.0052&lt;/td>
&lt;td>0.0012&lt;/td>
&lt;td>0.0012&lt;/td>
&lt;td>0.0012&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The table reveals that temporal dynamics fundamentally reshape the spatial story. The temporal lag coefficient ($\tau \approx 0.86$) is remarkably stable across all dynamic specifications, confirming that habit persistence is the dominant force. The spatial coefficient $\rho$ varies widely depending on whether the spatiotemporal lag is included, highlighting the sensitivity of spatial inference to the dynamic specification. The short-run price and income elasticities in the dynamic models are roughly one-quarter the size of the static estimates, because the lagged dependent variable now carries the cumulative effect.&lt;/p>
&lt;h2 id="10-effect-decomposition-summary">10. Effect Decomposition Summary&lt;/h2>
&lt;p>The figure below compares the direct, indirect, and total effects of price and income across three model-horizon combinations: the static SDM, and the short-run and long-run effects from the dynamic SDM.&lt;/p>
&lt;pre>&lt;code class="language-r"># See analysis.R for the full figure code
&lt;/code>&lt;/pre>
&lt;p>&lt;img src="r_SDPDmod_fig3_impact_decomposition.png" alt="Effect decomposition comparing direct, indirect, and total effects for price and income across the static SDM and the dynamic SDM (short-run and long-run)">&lt;/p>
&lt;p>Four patterns stand out from this comparison. First, the &lt;strong>static SDM overstates the short-run response&lt;/strong> to price changes: its direct price effect (-1.01) is nearly four times larger than the dynamic short-run direct effect (-0.26). A policymaker using the static estimate to predict the immediate revenue impact of a cigarette tax increase would be far too optimistic about consumption reductions.&lt;/p>
&lt;p>Second, &lt;strong>spatial spillovers change sign between static and dynamic models&lt;/strong>. In the static SDM, the indirect price effect is negative (-0.22), meaning price increases reduce neighbors' consumption. In the dynamic SDM&amp;rsquo;s short run, it is &lt;em>positive&lt;/em> (0.18), consistent with cross-border shopping: when one state raises prices, its neighbors' sales increase as smokers cross the border. This sign reversal underscores the importance of properly specifying temporal dynamics.&lt;/p>
&lt;p>Third, &lt;strong>long-run effects are much larger but imprecisely estimated&lt;/strong>. The long-run direct price elasticity (-1.93) is the largest estimate in the analysis, reflecting decades of accumulated habit adjustments. However, the wide confidence intervals on long-run total effects mean that precise long-run predictions require caution.&lt;/p>
&lt;p>Fourth, &lt;strong>income effects are more robust&lt;/strong>. The direct income elasticity is positive and significant in all specifications (ranging from 0.10 in the short run to 0.80 in the long run), confirming that cigarettes behave as a normal good. The indirect income effects are less stable and generally not significant in the dynamic specification.&lt;/p>
&lt;h2 id="11-discussion">11. Discussion&lt;/h2>
&lt;p>This tutorial demonstrates three key findings about spatial dynamics in cigarette demand. First, &lt;strong>spatial dependence is real and economically meaningful&lt;/strong>, but its magnitude depends critically on the model specification. The Bayesian comparison (Section 5) unanimously rejects non-spatial models, and the total price elasticity in the static SDM (-1.23) is 22% larger than the direct effect alone (-1.01). A state that ignores spatial spillovers when evaluating a cigarette tax increase will underestimate both the consumption reduction in its own state and the cross-border effects on neighbors.&lt;/p>
&lt;p>Second, &lt;strong>habit persistence dominates the dynamic structure&lt;/strong>. The temporal lag coefficient ($\tau \approx 0.86$) is by far the largest and most precisely estimated parameter in every dynamic model. Once dynamics are included, the contemporaneous spatial coefficient weakens dramatically, and what appeared to be spatial dependence in the static model is revealed to be largely temporal persistence. This does not mean spatial effects are absent &amp;mdash; they remain significant at $\rho = 0.16$ in the dynamic SDM &amp;mdash; but they are much smaller than the static model suggests.&lt;/p>
&lt;p>Third, &lt;strong>the dynamic SDM uncovers a cross-border shopping effect&lt;/strong> that the static model misses. The positive and significant &lt;code>W*logp&lt;/code> coefficient (0.196) in the dynamic SDM means that when neighboring states raise prices, own-state consumption &lt;em>increases&lt;/em> in the short run. This is the signature of cross-border purchasing. The effect is masked in the static model because the spatial lag $\rho Wy$ absorbs it, and it only emerges when the temporal dynamics are properly specified.&lt;/p>
&lt;p>A fourth finding relates to &lt;strong>robustness to the weight matrix&lt;/strong>. Re-estimating the static SDM with a 2nd-order contiguity matrix (which expands the average number of neighbors from 4.1 to 10.6) yields a stronger spatial coefficient ($\rho = 0.449$ vs. 0.262) and a significant &lt;code>W*logp&lt;/code> coefficient (0.337, $p = 0.009$) that was not significant with the 1st-order matrix. This suggests that cross-border shopping effects may extend beyond immediately adjacent states, and that the choice of spatial weight matrix matters substantively for policy conclusions.&lt;/p>
&lt;p>From a software perspective, the SDPDmod package provides a streamlined R workflow that covers the complete spatial panel modeling pipeline &amp;mdash; from Bayesian model selection through estimation to impact decomposition &amp;mdash; in a coherent framework. The &lt;code>blmpSDPD()&lt;/code> function is particularly valuable for applied researchers, as it replaces the ad hoc sequence of Wald tests with a principled, simultaneous comparison of all candidate models.&lt;/p>
&lt;h2 id="12-summary-and-next-steps">12. Summary and Next Steps&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>Spatial models matter for tobacco policy:&lt;/strong> the total price elasticity (-1.23 in the static SDM) is 22% larger than the direct effect alone, meaning unilateral state tax increases generate spillovers to neighboring states that standard panel models miss.&lt;/li>
&lt;li>&lt;strong>Bayesian model comparison provides principled model selection:&lt;/strong> the SDM is overwhelmingly preferred in static specifications (99.89% probability with individual FE), but adding dynamics reduces the ability to discriminate among spatial models, with all specifications receiving similar posterior probabilities.&lt;/li>
&lt;li>&lt;strong>Habit persistence is the dominant dynamic force:&lt;/strong> the temporal lag coefficient $\tau \approx 0.86$ dwarfs the contemporaneous spatial effect ($\rho = 0.16$), and static models conflate short-run and long-run responses. The short-run price elasticity (-0.26) is one-quarter of the static estimate (-1.01).&lt;/li>
&lt;li>&lt;strong>Cross-border shopping emerges in the dynamic SDM:&lt;/strong> the positive spatial lag of price (&lt;code>W*logp = 0.20&lt;/code>) means that neighboring states' price increases boost own consumption in the short run &amp;mdash; the clearest evidence of border-crossing behavior.&lt;/li>
&lt;/ul>
&lt;p>For further study, see the companion &lt;a href="https://carlos-mendez.org/post/stata_sp_regression_panel/">Stata spatial panel tutorial&lt;/a> that applies &lt;code>xsmle&lt;/code> to the same dataset, and the &lt;a href="https://carlos-mendez.org/post/stata_sp_regression_cross_section/">Stata cross-sectional spatial tutorial&lt;/a> for a simpler introduction to spatial models without the temporal dimension. The SDPDmod package is documented in Simonovska (2025) and available on &lt;a href="https://cran.r-project.org/package=SDPDmod" target="_blank" rel="noopener">CRAN&lt;/a>.&lt;/p>
&lt;h2 id="13-exercises">13. Exercises&lt;/h2>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Build your own W.&lt;/strong> In Section 4.2 we constructed a 2nd-order contiguity matrix. Re-run &lt;code>blmpSDPD()&lt;/code> with this alternative &lt;code>W2&lt;/code> instead of the original &lt;code>W&lt;/code>. Does the Bayesian model comparison still favor the SDM? How do the model probabilities change when the definition of &amp;ldquo;neighbor&amp;rdquo; is broader?&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Include pimin directly.&lt;/strong> Add &lt;code>lpm = log(pimin/cpi)&lt;/code> as an additional covariate in the SAR model: &lt;code>logc ~ logp + logy + lpm&lt;/code>. Compare the results to the SDM&amp;rsquo;s &lt;code>W*logp&lt;/code> coefficient. Does &lt;code>lpm&lt;/code> remain significant alongside the spatial lag of the dependent variable? Why or why not?&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>SAR vs. SDM indirect effects.&lt;/strong> Compare the impact decomposition from the static SAR (Section 7.3) and static SDM (Section 8.4). The indirect income effect &lt;em>reverses sign&lt;/em> (positive in SAR, negative in SDM). Write a paragraph explaining this reversal in terms of the cross-border shopping mechanism.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Subsample analysis.&lt;/strong> Split the data into two periods (1963&amp;ndash;1977 and 1978&amp;ndash;1992). Re-estimate the dynamic SDM for each period. Does the habit persistence coefficient ($\tau$) change over time? Has the spatial coefficient ($\rho$) strengthened or weakened as anti-smoking policies intensified?&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="14-references">14. References&lt;/h2>
&lt;ol>
&lt;li>&lt;a href="https://doi.org/10.1007/s10614-025-11056-2" target="_blank" rel="noopener">Simonovska, R. (2025). SDPDmod: An R Package for Spatial Dynamic Panel Data Modeling. &lt;em>Computational Economics&lt;/em>.&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://doi.org/10.1016/0954-349X%2892%2990010-4" target="_blank" rel="noopener">Baltagi, B. H. &amp;amp; Levin, D. (1992). Cigarette Taxation: Raising Revenues and Reducing Consumption. &lt;em>Structural Change and Economic Dynamics&lt;/em>, 3(2), 321&amp;ndash;335.&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://doi.org/10.1016/j.jeconom.2009.08.001" target="_blank" rel="noopener">Lee, L.-F. &amp;amp; Yu, J. (2010). Estimation of Spatial Autoregressive Panel Data Models with Fixed Effects. &lt;em>Journal of Econometrics&lt;/em>, 154(2), 165&amp;ndash;185.&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://doi.org/10.1016/j.spasta.2014.02.002" target="_blank" rel="noopener">LeSage, J. P. (2014). Spatial Econometric Panel Data Model Specification: A Bayesian Approach. &lt;em>Spatial Statistics&lt;/em>, 9, 122&amp;ndash;145.&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://doi.org/10.1007/978-3-642-40340-8" target="_blank" rel="noopener">Elhorst, J. P. (2014). &lt;em>Spatial Econometrics: From Cross-Sectional Data to Spatial Panels.&lt;/em> Springer.&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://doi.org/10.1201/9781420064254" target="_blank" rel="noopener">LeSage, J. P. &amp;amp; Pace, R. K. (2009). &lt;em>Introduction to Spatial Econometrics.&lt;/em> Chapman &amp;amp; Hall/CRC.&lt;/a>&lt;/li>
&lt;/ol></description></item><item><title>Spatial Dynamic Panels with Common Factors in Stata: Credit Risk in US Banking</title><link>https://carlos-mendez.org/post/stata_spxtivdfreg/</link><pubDate>Fri, 27 Mar 2026 00:00:00 +0000</pubDate><guid>https://carlos-mendez.org/post/stata_spxtivdfreg/</guid><description>&lt;h2 id="1-overview">1. Overview&lt;/h2>
&lt;p>The 2007&amp;ndash;2009 Global Financial Crisis revealed that credit risk does not stay contained within individual banks. Non-performing loans surged across the US banking system through two distinct channels &amp;mdash; &lt;strong>spatial spillovers&lt;/strong> from balance-sheet interdependencies among interconnected banks, and &lt;strong>common factors&lt;/strong> from macroeconomic shocks (interest rate changes, housing market collapses, unemployment spikes) that hit all banks simultaneously. Ignoring either channel leads to biased estimates of credit risk determinants and misleading policy prescriptions. Standard spatial panel packages in Stata &amp;mdash; such as &lt;code>xsmle&lt;/code> and &lt;code>spxtregress&lt;/code> &amp;mdash; can model spatial spillovers but cannot account for unobserved common factors, leaving a critical gap in the econometrician&amp;rsquo;s toolkit.&lt;/p>
&lt;p>The &lt;code>spxtivdfreg&lt;/code> package (Kripfganz &amp;amp; Sarafidis, 2025) fills this gap by implementing a &lt;strong>defactored instrumental variables&lt;/strong> estimator that simultaneously handles four sources of endogeneity: spatial lags of the dependent variable, temporal lags (dynamic persistence), endogenous regressors, and unobserved common factors. The estimator first removes common factors from the data using a principal-components-based defactoring procedure, then applies IV/GMM estimation to the defactored model. This approach avoids the incidental parameters bias that plagues maximum likelihood methods and does not require bias corrections like the Lee-Yu adjustment used in &lt;code>xsmle&lt;/code>.&lt;/p>
&lt;p>This tutorial replicates the empirical application from Kripfganz and Sarafidis (2025), which models non-performing loan ratios across 350 US commercial banks over the period 2006:Q1 to 2014:Q4 &amp;mdash; a sample that spans the entire GFC episode. We estimate the full spatial dynamic panel model with common factors, demonstrate what happens when common factors or the spatial lag are omitted, compute short-run and long-run spillover effects, and compare homogeneous and heterogeneous slope specifications.&lt;/p>
&lt;h3 id="learning-objectives">Learning objectives&lt;/h3>
&lt;ul>
&lt;li>Understand the four sources of endogeneity in spatial dynamic panel models: spatial lag, temporal lag, endogenous regressors, and common factors&lt;/li>
&lt;li>Estimate the full spatial dynamic panel model with common factors using &lt;code>spxtivdfreg&lt;/code>&lt;/li>
&lt;li>Compare estimation results with and without common factors to assess the consequences of ignoring latent macroeconomic shocks&lt;/li>
&lt;li>Compare estimation results with and without the spatial lag to evaluate the importance of bank interconnectedness&lt;/li>
&lt;li>Compute and interpret short-run and long-run direct, indirect, and total effects using &lt;code>estat impact&lt;/code>&lt;/li>
&lt;li>Estimate heterogeneous slope models with the mean-group (MG) estimator to assess cross-bank parameter heterogeneity&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="2-the-modeling-framework">2. The modeling framework&lt;/h2>
&lt;p>Credit risk in a banking system is shaped by forces operating at three different levels: the individual bank (its own financial ratios and management quality), the network of interconnected banks (spatial spillovers through lending relationships, common borrowers, and contagion), and the macroeconomy (interest rates, GDP growth, and other aggregate shocks that affect all banks). The spatial dynamic panel model with common factors captures all three levels in a single equation.&lt;/p>
&lt;p>The diagram below illustrates the four sources of endogeneity that the &lt;code>spxtivdfreg&lt;/code> estimator must address simultaneously.&lt;/p>
&lt;pre>&lt;code class="language-mermaid">graph TD
Y[&amp;quot;&amp;lt;b&amp;gt;NPL&amp;lt;sub&amp;gt;it&amp;lt;/sub&amp;gt;&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;Non-performing&amp;lt;br/&amp;gt;loan ratio&amp;quot;]
WY[&amp;quot;&amp;lt;b&amp;gt;W · NPL&amp;lt;sub&amp;gt;t&amp;lt;/sub&amp;gt;&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;Spatial lag&amp;lt;br/&amp;gt;&amp;lt;i&amp;gt;Bank interdependence&amp;lt;/i&amp;gt;&amp;quot;]
LY[&amp;quot;&amp;lt;b&amp;gt;NPL&amp;lt;sub&amp;gt;i,t-1&amp;lt;/sub&amp;gt;&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;Temporal lag&amp;lt;br/&amp;gt;&amp;lt;i&amp;gt;Risk persistence&amp;lt;/i&amp;gt;&amp;quot;]
X[&amp;quot;&amp;lt;b&amp;gt;INEFF&amp;lt;sub&amp;gt;it&amp;lt;/sub&amp;gt;&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;Endogenous&amp;lt;br/&amp;gt;regressor&amp;quot;]
F[&amp;quot;&amp;lt;b&amp;gt;f&amp;lt;sub&amp;gt;t&amp;lt;/sub&amp;gt;&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;Common factors&amp;lt;br/&amp;gt;&amp;lt;i&amp;gt;Macro shocks&amp;lt;/i&amp;gt;&amp;quot;]
Z[&amp;quot;&amp;lt;b&amp;gt;Z&amp;lt;sub&amp;gt;it&amp;lt;/sub&amp;gt;&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;Instruments&amp;lt;br/&amp;gt;&amp;lt;i&amp;gt;INTEREST, lags&amp;lt;/i&amp;gt;&amp;quot;]
WY --&amp;gt;|&amp;quot;ψ&amp;quot;| Y
LY --&amp;gt;|&amp;quot;ρ&amp;quot;| Y
X --&amp;gt;|&amp;quot;β&amp;quot;| Y
F -.-&amp;gt;|&amp;quot;λ&amp;lt;sub&amp;gt;i&amp;lt;/sub&amp;gt;&amp;quot;| Y
Z -.-&amp;gt;|&amp;quot;IV&amp;quot;| X
style Y fill:#d97757,stroke:#141413,color:#fff
style WY fill:#6a9bcc,stroke:#141413,color:#fff
style LY fill:#6a9bcc,stroke:#141413,color:#fff
style X fill:#00d4c8,stroke:#141413,color:#141413
style F fill:#141413,stroke:#d97757,color:#fff
style Z fill:#6a9bcc,stroke:#141413,color:#fff
&lt;/code>&lt;/pre>
&lt;p>The spatial lag ($W \cdot NPL$) creates endogeneity because bank $i$&amp;rsquo;s credit risk depends on bank $j$&amp;rsquo;s credit risk, and vice versa &amp;mdash; a simultaneity problem. The temporal lag ($NPL_{i,t-1}$) is endogenous because it correlates with the bank-specific fixed effect. The endogenous regressor (operational inefficiency, $INEFF$) is correlated with the error term. And the common factors ($f_t$) enter both the regressors and the error, inducing cross-sectional dependence and omitted variable bias.&lt;/p>
&lt;p>The model is specified as:&lt;/p>
&lt;p>$$NPL_{it} = \psi \sum_{j=1}^{N} w_{ij} \, NPL_{jt} + \rho \, NPL_{i,t-1} + x_{it} \beta + \alpha_i + \lambda_i' f_t + \varepsilon_{it}$$&lt;/p>
&lt;p>In words, this equation says that the non-performing loan ratio of bank $i$ at time $t$ depends on: the &lt;strong>spatial lag&lt;/strong> $\psi W \cdot NPL$ (the weighted average NPL of interconnected banks), the &lt;strong>temporal lag&lt;/strong> $\rho \, NPL_{i,t-1}$ (the bank&amp;rsquo;s own past credit risk, capturing persistence), the &lt;strong>bank-specific covariates&lt;/strong> $x_{it} \beta$ (financial ratios like capital adequacy, profitability, and liquidity), the &lt;strong>individual fixed effect&lt;/strong> $\alpha_i$ (time-invariant bank characteristics), and the &lt;strong>interactive fixed effect&lt;/strong> $\lambda_i' f_t$ (unobserved common factors with heterogeneous loadings).&lt;/p>
&lt;h3 id="variable-mapping">Variable mapping&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Symbol&lt;/th>
&lt;th>Meaning&lt;/th>
&lt;th>Stata variable&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>$NPL_{it}$&lt;/td>
&lt;td>Non-performing loans / total loans (%)&lt;/td>
&lt;td>&lt;code>NPL&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$\psi$&lt;/td>
&lt;td>Spatial autoregressive parameter&lt;/td>
&lt;td>&lt;code>[W]NPL&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$\rho$&lt;/td>
&lt;td>Temporal autoregressive parameter&lt;/td>
&lt;td>&lt;code>L1.NPL&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$x_{it}$&lt;/td>
&lt;td>Bank-specific covariates&lt;/td>
&lt;td>&lt;code>INEFF&lt;/code>, &lt;code>CAR&lt;/code>, &lt;code>SIZE&lt;/code>, &amp;hellip;&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$\alpha_i$&lt;/td>
&lt;td>Bank fixed effect (absorbed)&lt;/td>
&lt;td>&lt;code>absorb(ID)&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$\lambda_i' f_t$&lt;/td>
&lt;td>Interactive fixed effect (defactored)&lt;/td>
&lt;td>estimated by &lt;code>spxtivdfreg&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$w_{ij}$&lt;/td>
&lt;td>Spatial weight (interconnection)&lt;/td>
&lt;td>&lt;code>W.csv&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="comparison-with-existing-stata-packages">Comparison with existing Stata packages&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Feature&lt;/th>
&lt;th>&lt;code>spxtivdfreg&lt;/code>&lt;/th>
&lt;th>&lt;code>xsmle&lt;/code>&lt;/th>
&lt;th>&lt;code>spxtregress&lt;/code>&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Estimation method&lt;/td>
&lt;td>IV/GMM (defactored)&lt;/td>
&lt;td>Maximum likelihood&lt;/td>
&lt;td>Quasi-ML&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Common factors&lt;/td>
&lt;td>Yes (estimated)&lt;/td>
&lt;td>No&lt;/td>
&lt;td>No&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Endogenous regressors&lt;/td>
&lt;td>Yes (IV)&lt;/td>
&lt;td>No&lt;/td>
&lt;td>Limited&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Dynamic (temporal lag)&lt;/td>
&lt;td>Yes&lt;/td>
&lt;td>Yes (&lt;code>dlag&lt;/code>)&lt;/td>
&lt;td>Yes&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Bias correction needed&lt;/td>
&lt;td>No&lt;/td>
&lt;td>Yes (Lee-Yu)&lt;/td>
&lt;td>No&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Heterogeneous slopes (MG)&lt;/td>
&lt;td>Yes (&lt;code>mg&lt;/code> option)&lt;/td>
&lt;td>No&lt;/td>
&lt;td>No&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The key advantage of &lt;code>spxtivdfreg&lt;/code> is its ability to handle unobserved common factors &amp;mdash; latent macroeconomic shocks that affect all banks but with heterogeneous intensity. Maximum likelihood methods in &lt;code>xsmle&lt;/code> assume cross-sectional independence conditional on the spatial weight matrix, which is violated when common factors are present. The defactored IV approach removes these factors before estimation, producing consistent estimates even in the presence of strong cross-sectional dependence.&lt;/p>
&lt;hr>
&lt;h2 id="3-setup-and-data-loading">3. Setup and data loading&lt;/h2>
&lt;p>Before running any spatial dynamic panel models, we need three Stata packages: &lt;code>xtivdfreg&lt;/code> (the core estimation engine), &lt;code>reghdfe&lt;/code> (for absorbing fixed effects), and &lt;code>ftools&lt;/code> (a dependency of &lt;code>reghdfe&lt;/code>). The &lt;code>spxtivdfreg&lt;/code> command is the spatial panel wrapper around &lt;code>xtivdfreg&lt;/code>.&lt;/p>
&lt;pre>&lt;code class="language-stata">* Install packages (if not already installed)
capture which xtivdfreg
if _rc {
ssc install xtivdfreg
}
capture which reghdfe
if _rc {
ssc install reghdfe
}
capture which ftools
if _rc {
ssc install ftools
}
&lt;/code>&lt;/pre>
&lt;h3 id="31-data-loading-and-panel-setup">3.1 Data loading and panel setup&lt;/h3>
&lt;p>The dataset contains quarterly financial ratios for 350 US commercial banks from 2006:Q1 to 2014:Q4, yielding 36 quarters and 12,600 total observations. After absorbing fixed effects and creating lags, the effective estimation sample is 12,250 observations (350 banks times 35 periods).&lt;/p>
&lt;pre>&lt;code class="language-stata">clear all
use &amp;quot;https://github.com/cmg777/starter-academic-v501/raw/master/content/post/stata_spxtivdfreg/references/v113i06.dta&amp;quot;, clear
xtset ID TIME
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Panel variable: ID (strongly balanced)
Time variable: TIME, 1 to 36
Delta: 1 unit
&lt;/code>&lt;/pre>
&lt;p>The panel is strongly balanced &amp;mdash; all 350 banks are observed in all 36 quarters. The &lt;code>xtset&lt;/code> command declares &lt;code>ID&lt;/code> as the bank identifier and &lt;code>TIME&lt;/code> as the quarterly time index.&lt;/p>
&lt;p>The sample period is rich with major macro-financial events that all banks experienced &amp;mdash; precisely the kind of aggregate shocks that common factors are designed to capture:&lt;/p>
&lt;pre>&lt;code class="language-mermaid">graph LR
A[&amp;quot;&amp;lt;b&amp;gt;2006--2007&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;Pre-crisis&amp;lt;br/&amp;gt;Housing bubble&amp;lt;br/&amp;gt;Low NPL ratios&amp;quot;]
B[&amp;quot;&amp;lt;b&amp;gt;2007--2009&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;Global Financial&amp;lt;br/&amp;gt;Crisis&amp;lt;br/&amp;gt;NPL surge&amp;quot;]
C[&amp;quot;&amp;lt;b&amp;gt;2010--2011&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;Dodd-Frank Act&amp;lt;br/&amp;gt;Stress tests&amp;lt;br/&amp;gt;Capital rebuilding&amp;quot;]
D[&amp;quot;&amp;lt;b&amp;gt;2012--2014&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;Recovery&amp;lt;br/&amp;gt;Basel III phase-in&amp;lt;br/&amp;gt;NPL normalization&amp;quot;]
A --&amp;gt; B
B --&amp;gt; C
C --&amp;gt; D
style A fill:#6a9bcc,stroke:#141413,color:#fff
style B fill:#d97757,stroke:#141413,color:#fff
style C fill:#141413,stroke:#d97757,color:#fff
style D fill:#00d4c8,stroke:#141413,color:#141413
&lt;/code>&lt;/pre>
&lt;p>These regime shifts (housing bubble, financial crisis, regulatory tightening, recovery) are exactly the unobserved common factors that the &lt;code>spxtivdfreg&lt;/code> estimator extracts. Standard two-way fixed effects would capture them only if they affected all 350 banks equally &amp;mdash; but the interactive fixed effect structure $\lambda_i' f_t$ allows each bank to respond with different intensity to the same aggregate shock.&lt;/p>
&lt;h3 id="32-summary-statistics">3.2 Summary statistics&lt;/h3>
&lt;pre>&lt;code class="language-stata">summarize NPL INEFF CAR SIZE BUFFER PROFIT QUALITY LIQUIDITY INTEREST
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text"> Variable | Obs Mean Std. dev. Min Max
-------------+---------------------------------------------------------
NPL | 12,600 1.7283 2.1067 0 23.0378
INEFF | 12,600 .6425 .1726 .2007 2.9037
CAR | 12,600 13.5550 5.6198 1.3800 86.8400
SIZE | 12,600 14.6883 1.4234 11.9466 20.4618
BUFFER | 12,600 5.5550 5.2691 -6.6200 78.8400
PROFIT | 12,600 .8001 5.0380 -132.0700 40.9900
QUALITY | 12,600 .2827 .6245 -4.9482 27.8659
LIQUIDITY | 12,600 .7699 .2224 .0122 2.3217
INTEREST | 12,600 -1.9074 .9328 -5.1644 2.5187
&lt;/code>&lt;/pre>
&lt;p>Mean NPL is 1.73%, reflecting the mixture of pre-crisis, crisis, and post-crisis quarters in the sample. The standard deviation of 2.11 percentage points indicates substantial variation both across banks and over time &amp;mdash; some banks had NPL ratios as high as 23%. Mean LIQUIDITY (loan-to-deposit ratio) is 0.77, meaning the average bank lent out 77 cents for every dollar of deposits. The wide range of CAR (1.38% to 86.84%) reflects the heterogeneity in capital structures across US commercial banks.&lt;/p>
&lt;h3 id="33-variables">3.3 Variables&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Variable&lt;/th>
&lt;th>Description&lt;/th>
&lt;th>Mean&lt;/th>
&lt;th>Std. Dev.&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>NPL&lt;/code>&lt;/td>
&lt;td>Non-performing loans / total loans (%)&lt;/td>
&lt;td>1.728&lt;/td>
&lt;td>2.107&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>INEFF&lt;/code>&lt;/td>
&lt;td>Operational inefficiency (endogenous)&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>CAR&lt;/code>&lt;/td>
&lt;td>Capital adequacy ratio&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>SIZE&lt;/code>&lt;/td>
&lt;td>ln(total assets)&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>BUFFER&lt;/code>&lt;/td>
&lt;td>Capital buffer (leverage ratio minus 8%)&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>PROFIT&lt;/code>&lt;/td>
&lt;td>Return on equity, annualized&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>QUALITY&lt;/code>&lt;/td>
&lt;td>Loan loss provisions / assets (%)&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>LIQUIDITY&lt;/code>&lt;/td>
&lt;td>Loan-to-deposit ratio&lt;/td>
&lt;td>0.770&lt;/td>
&lt;td>0.222&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>INTEREST&lt;/code>&lt;/td>
&lt;td>Interest expenses / deposits (instrument for INEFF)&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The dependent variable &lt;code>NPL&lt;/code> measures credit risk as the share of non-performing loans in total loans, expressed in percentage points. Its mean of 1.728% reflects the mixture of pre-crisis, crisis, and post-crisis quarters in the sample, with a standard deviation of 2.107 percentage points indicating substantial variation both across banks and over time. The variable &lt;code>INEFF&lt;/code> (operational inefficiency) is treated as &lt;strong>endogenous&lt;/strong> and instrumented using &lt;code>INTEREST&lt;/code> (interest expenses relative to deposits) along with lagged values of the exogenous regressors.&lt;/p>
&lt;h3 id="33-the-spatial-weight-matrix">3.3 The spatial weight matrix&lt;/h3>
&lt;p>The spatial weight matrix $W$ is a 350-by-350 matrix that defines the network structure among banks. Unlike geographic contiguity matrices used in regional analysis, this matrix is constructed from &lt;strong>economic distance&lt;/strong> &amp;mdash; specifically, Spearman&amp;rsquo;s rank correlation of bank debt-to-asset ratios. Two banks are defined as &amp;ldquo;neighbors&amp;rdquo; if their debt ratio correlation exceeds the 95th percentile of the empirical distribution.&lt;/p>
&lt;pre>&lt;code class="language-stata">* Download the W matrix to the current working directory
copy &amp;quot;https://github.com/cmg777/starter-academic-v501/raw/master/content/post/stata_spxtivdfreg/references/W.csv&amp;quot; &amp;quot;W.csv&amp;quot;, replace
* The W matrix (350 x 350, row-standardized, 6,300 nonzero entries) is loaded
* automatically by spxtivdfreg via the spmatrix(&amp;quot;W.csv&amp;quot;, import) option
&lt;/code>&lt;/pre>
&lt;p>The matrix is row-standardized so that each row sums to one, meaning the spatial lag of a variable equals the &lt;strong>weighted average&lt;/strong> among a bank&amp;rsquo;s neighbors. With 6,300 nonzero entries across 350 banks, the average bank has approximately 18 neighbors &amp;mdash; banks whose debt structures are sufficiently correlated to suggest economic interdependence. To illustrate: suppose Bank A and Bank B have a Spearman rank correlation of 0.92 in their quarterly debt ratios, while the 95th percentile threshold is 0.87. Since 0.92 exceeds 0.87, Bank A and Bank B are classified as neighbors ($w_{AB} &amp;gt; 0$). After row-standardization, $w_{AB}$ equals $1/18$ if Bank A has 18 neighbors. This economic-distance approach captures financial contagion channels that geographic proximity alone would miss, since two banks on opposite coasts can be highly interconnected through similar lending portfolios.&lt;/p>
&lt;hr>
&lt;h2 id="4-full-model-with-common-factors">4. Full model with common factors&lt;/h2>
&lt;p>We now estimate the full spatial dynamic panel model with unobserved common factors. The &lt;code>spxtivdfreg&lt;/code> command takes the dependent variable (&lt;code>NPL&lt;/code>) and the regressors, with options specifying the model structure: &lt;code>absorb(ID)&lt;/code> absorbs bank fixed effects, &lt;code>splag&lt;/code> includes the spatial lag of NPL, &lt;code>tlags(1)&lt;/code> adds the first temporal lag, &lt;code>spmatrix(&amp;quot;W.csv&amp;quot;, import)&lt;/code> loads the weight matrix, and &lt;code>iv(...)&lt;/code> specifies the instrumental variables. The &lt;code>std&lt;/code> option standardizes the variables before extracting principal components for the factor estimation, which improves numerical stability when covariates have very different scales.&lt;/p>
&lt;pre>&lt;code class="language-stata">spxtivdfreg NPL INEFF CAR SIZE BUFFER PROFIT QUALITY LIQUIDITY, ///
absorb(ID) splag tlags(1) spmatrix(&amp;quot;W.csv&amp;quot;, import) ///
iv(INTEREST CAR SIZE BUFFER PROFIT QUALITY LIQUIDITY, splags lag(1)) std
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Defactored instrumental variables estimation
Group variable: ID Number of obs = 12,250
Time variable: TIME Number of groups = 350
Number of instruments = 28 Obs per group:
Number of factors in X = 2 min = 35
Number of factors in u = 1 avg = 35.0
max = 35
Second-stage estimator (model with homogeneous slope coefficients)
--------------------------------------------------------------------------
Robust
NPL | Coefficient std. err. z P&amp;gt;|z| [95% conf. interval]
------+-------------------------------------------------------------------
NPL |
L1. | .2898521 .0543794 5.33 0.000 .1832704 .3964339
|
INEFF | .4473777 .1045636 4.28 0.000 .2424368 .6523186
CAR | .0305078 .0057852 5.27 0.000 .019169 .0418465
SIZE | .2225966 .0941614 2.36 0.018 .0380436 .4071496
BUFFER| -.0545049 .0118678 -4.59 0.000 -.0777653 -.0312445
PROFIT| -.0053351 .0018411 -2.90 0.004 -.0089437 -.0017266
QUALITY| .1830412 .0307657 5.95 0.000 .1227415 .2433408
LIQUIDITY| 2.452391 .2696471 9.09 0.000 1.923892 2.980889
_cons | -4.510715 1.311453 -3.44 0.001 -7.081115 -1.940315
------+-------------------------------------------------------------------
W |
NPL | .3943206 .0848856 4.65 0.000 .2279479 .5606932
------+-------------------------------------------------------------------
sigma_f | .64162366 (std. dev. of factor error component)
sigma_e | .90381799 (std. dev. of idiosyncratic error component)
rho | .33509009 (fraction of variance due to factors)
--------------------------------------------------------------------------
Hansen test: chi2(19) = 18.8250, Prob &amp;gt; chi2 = 0.4681
&lt;/code>&lt;/pre>
&lt;p>The estimator identifies &lt;strong>2 common factors in the regressors&lt;/strong> and &lt;strong>1 common factor in the error term&lt;/strong>, capturing latent macroeconomic forces that drive credit risk across the banking system. These factors represent unobserved aggregate shocks &amp;mdash; such as Federal Reserve interest rate decisions, housing market fluctuations, and changes in regulatory stringency &amp;mdash; that affect all banks simultaneously but with bank-specific intensities (heterogeneous factor loadings $\lambda_i$).&lt;/p>
&lt;p>The &lt;strong>spatial autoregressive parameter&lt;/strong> $\psi = 0.394$ (z = 4.65, p &amp;lt; 0.001) indicates strong positive spatial spillovers: when the average NPL ratio of a bank&amp;rsquo;s neighbors increases by 1 percentage point, the bank&amp;rsquo;s own NPL ratio increases by 0.39 percentage points, holding all else constant. This captures financial contagion through interconnected lending networks &amp;mdash; when one bank&amp;rsquo;s borrowers default, it can trigger a cascade of defaults among economically linked banks.&lt;/p>
&lt;p>The &lt;strong>temporal persistence parameter&lt;/strong> $\rho = 0.290$ (z = 5.33, p &amp;lt; 0.001) shows that credit risk is moderately persistent: about 29% of a bank&amp;rsquo;s current NPL ratio is inherited from the previous quarter. This reflects the gradual resolution of non-performing loans through workout processes, foreclosures, and write-offs.&lt;/p>
&lt;p>Among the covariates, &lt;strong>LIQUIDITY&lt;/strong> has the largest effect at 2.452 (z = 9.09, p &amp;lt; 0.001), meaning that a 1 percentage point increase in the loan-to-deposit ratio is associated with a 2.45 percentage point increase in non-performing loans. Banks that extend more credit relative to their deposit base face higher credit risk. &lt;strong>INEFF&lt;/strong> (operational inefficiency) enters with a coefficient of 0.447 (z = 4.28, p &amp;lt; 0.001), confirming that poorly managed banks experience higher default rates &amp;mdash; a finding consistent with the &amp;ldquo;bad management&amp;rdquo; hypothesis in the banking literature. &lt;strong>BUFFER&lt;/strong> enters negatively at -0.055 (z = -4.59, p &amp;lt; 0.001), indicating that better-capitalized banks (those with larger capital buffers above the 8% regulatory minimum) have lower credit risk.&lt;/p>
&lt;p>The &lt;strong>variance decomposition&lt;/strong> at the bottom of the output reveals that common factors explain a substantial share of the error variance: $\sigma_f = 0.642$ and $\sigma_e = 0.904$, yielding $\rho_{factor} = 0.335$. This means that &lt;strong>33.5% of the residual variance&lt;/strong> is attributable to unobserved common factors &amp;mdash; macroeconomic shocks that a model without factors would absorb into biased coefficient estimates.&lt;/p>
&lt;p>The &lt;strong>Hansen J-test&lt;/strong> for overidentifying restrictions yields chi2(19) = 18.825 with p = 0.468, which &lt;strong>does not reject&lt;/strong> the null hypothesis that the instruments are valid. This provides confidence that the IV strategy &amp;mdash; using &lt;code>INTEREST&lt;/code> and lagged values of exogenous regressors as instruments &amp;mdash; is appropriate.&lt;/p>
&lt;hr>
&lt;h2 id="5-what-happens-without-common-factors">5. What happens without common factors?&lt;/h2>
&lt;p>To assess the consequences of ignoring latent macroeconomic shocks, we re-estimate the model with the &lt;code>factmax(0)&lt;/code> option, which forces the estimator to set the number of common factors to zero. This specification is equivalent to a standard spatial dynamic panel model without interactive fixed effects.&lt;/p>
&lt;pre>&lt;code class="language-stata">spxtivdfreg NPL INEFF CAR SIZE BUFFER PROFIT QUALITY LIQUIDITY, ///
absorb(ID) splag tlags(1) spmatrix(&amp;quot;W.csv&amp;quot;, import) ///
iv(INTEREST CAR SIZE BUFFER PROFIT QUALITY LIQUIDITY, splags lag(1)) std factmax(0)
&lt;/code>&lt;/pre>
&lt;p>The table below compares the coefficient estimates from the full model (with factors) and the restricted model (without factors).&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Variable&lt;/th>
&lt;th style="text-align:center">With factors&lt;/th>
&lt;th style="text-align:center">Without factors&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>$\psi$ (W*NPL)&lt;/td>
&lt;td style="text-align:center">0.394*** (0.085)&lt;/td>
&lt;td style="text-align:center">0.288*** (0.038)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$\rho$ (L1.NPL)&lt;/td>
&lt;td style="text-align:center">0.290*** (0.054)&lt;/td>
&lt;td style="text-align:center">0.594*** (0.034)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>INEFF&lt;/td>
&lt;td style="text-align:center">0.447*** (0.105)&lt;/td>
&lt;td style="text-align:center">0.366*** (0.107)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CAR&lt;/td>
&lt;td style="text-align:center">0.031*** (0.006)&lt;/td>
&lt;td style="text-align:center">0.017*** (0.004)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SIZE&lt;/td>
&lt;td style="text-align:center">0.223** (0.094)&lt;/td>
&lt;td style="text-align:center">0.089 (0.061)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>BUFFER&lt;/td>
&lt;td style="text-align:center">-0.055*** (0.012)&lt;/td>
&lt;td style="text-align:center">-0.025** (0.010)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>PROFIT&lt;/td>
&lt;td style="text-align:center">-0.005*** (0.002)&lt;/td>
&lt;td style="text-align:center">-0.006*** (0.002)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>QUALITY&lt;/td>
&lt;td style="text-align:center">0.183*** (0.031)&lt;/td>
&lt;td style="text-align:center">0.283*** (0.029)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>LIQUIDITY&lt;/td>
&lt;td style="text-align:center">2.452*** (0.270)&lt;/td>
&lt;td style="text-align:center">0.843*** (0.180)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Factors ($r_x$, $r_u$)&lt;/td>
&lt;td style="text-align:center">2, 1&lt;/td>
&lt;td style="text-align:center">0, 0&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>J-test&lt;/td>
&lt;td style="text-align:center">18.825 [0.468]&lt;/td>
&lt;td style="text-align:center">48.151 [0.000]&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The differences are striking and systematic. Without common factors, the &lt;strong>temporal persistence doubles&lt;/strong> from $\rho = 0.290$ to $\rho = 0.594$. This inflation occurs because unobserved common factors are serially correlated (macroeconomic conditions evolve gradually), and when they are excluded from the model, the temporal lag absorbs their persistence. In other words, the model without factors confuses macroeconomic persistence with bank-level credit risk persistence.&lt;/p>
&lt;p>The &lt;strong>spatial autoregressive parameter drops&lt;/strong> from $\psi = 0.394$ to $\psi = 0.288$ &amp;mdash; a 27% decrease. This is counterintuitive at first glance: one might expect omitting factors to inflate the spatial parameter (since common factors create cross-sectional dependence that could be mistaken for spatial spillovers). However, the inflated temporal lag in the no-factor model absorbs some of the spatial dynamics, compressing $\psi$ downward. The lesson is that omitting common factors distorts &lt;strong>all&lt;/strong> coefficient estimates in complex and non-obvious ways.&lt;/p>
&lt;p>The &lt;strong>LIQUIDITY coefficient collapses&lt;/strong> from 2.452 to 0.843 &amp;mdash; a 66% reduction. This suggests that much of the effect of liquidity on credit risk operates through common factors: during the GFC, aggregate liquidity conditions deteriorated system-wide, and banks with high loan-to-deposit ratios were disproportionately affected. Without factors to absorb these aggregate movements, the LIQUIDITY coefficient is biased downward.&lt;/p>
&lt;p>Most critically, the &lt;strong>Hansen J-test rejects&lt;/strong> in the no-factor model: chi2 = 48.151 with p &amp;lt; 0.001. This rejection means that the instruments are not valid under the no-factor specification &amp;mdash; the model is misspecified. The common factors that enter both the regressors and the error term invalidate the exclusion restriction when they are not accounted for. This provides a formal statistical justification for including common factors: the J-test passes (p = 0.468) with factors and fails (p &amp;lt; 0.001) without them.&lt;/p>
&lt;p>&lt;strong>SIZE&lt;/strong> becomes statistically insignificant without factors (coefficient = 0.089, standard error = 0.061), whereas it is significant at the 5% level in the full model (0.223, standard error = 0.094). This reversal illustrates how omitting common factors can mask genuine relationships: larger banks are more exposed to systematic macro shocks (they have larger factor loadings), and without factors in the model, this exposure is incorrectly attributed to noise rather than to bank size.&lt;/p>
&lt;hr>
&lt;h2 id="6-what-happens-without-the-spatial-lag">6. What happens without the spatial lag?&lt;/h2>
&lt;p>To isolate the contribution of spatial spillovers, we now estimate a model that includes common factors but removes the spatially lagged dependent variable. This is done by dropping the &lt;code>splag&lt;/code> option. Without the spatial lag, the model reduces to a dynamic panel with common factors &amp;mdash; equivalent to the &lt;code>xtivdfreg&lt;/code> command.&lt;/p>
&lt;pre>&lt;code class="language-stata">* Without spatial lag (spxtivdfreg without splag option)
spxtivdfreg NPL INEFF CAR SIZE BUFFER PROFIT QUALITY LIQUIDITY, ///
absorb(ID) tlags(1) spmatrix(&amp;quot;W.csv&amp;quot;, import) ///
iv(INTEREST CAR SIZE BUFFER PROFIT QUALITY LIQUIDITY, lag(1)) std
* Equivalent specification with xtivdfreg
xtivdfreg NPL L.NPL INEFF CAR SIZE BUFFER PROFIT QUALITY LIQUIDITY, ///
absorb(ID) ///
iv(INTEREST CAR SIZE BUFFER PROFIT QUALITY LIQUIDITY, lag(1)) std
&lt;/code>&lt;/pre>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Variable&lt;/th>
&lt;th style="text-align:center">Full model&lt;/th>
&lt;th style="text-align:center">Without spatial lag&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>$\psi$ (W*NPL)&lt;/td>
&lt;td style="text-align:center">0.394*** (0.085)&lt;/td>
&lt;td style="text-align:center">&amp;mdash;&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$\rho$ (L1.NPL)&lt;/td>
&lt;td style="text-align:center">0.290*** (0.054)&lt;/td>
&lt;td style="text-align:center">0.323*** (0.055)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>INEFF&lt;/td>
&lt;td style="text-align:center">0.447*** (0.105)&lt;/td>
&lt;td style="text-align:center">0.638*** (0.116)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CAR&lt;/td>
&lt;td style="text-align:center">0.031*** (0.006)&lt;/td>
&lt;td style="text-align:center">0.030*** (0.006)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SIZE&lt;/td>
&lt;td style="text-align:center">0.223** (0.094)&lt;/td>
&lt;td style="text-align:center">0.346*** (0.096)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>BUFFER&lt;/td>
&lt;td style="text-align:center">-0.055*** (0.012)&lt;/td>
&lt;td style="text-align:center">-0.045*** (0.016)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>PROFIT&lt;/td>
&lt;td style="text-align:center">-0.005*** (0.002)&lt;/td>
&lt;td style="text-align:center">-0.004** (0.002)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>QUALITY&lt;/td>
&lt;td style="text-align:center">0.183*** (0.031)&lt;/td>
&lt;td style="text-align:center">0.183*** (0.036)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>LIQUIDITY&lt;/td>
&lt;td style="text-align:center">2.452*** (0.270)&lt;/td>
&lt;td style="text-align:center">2.534*** (0.311)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Factors ($r_x$, $r_u$)&lt;/td>
&lt;td style="text-align:center">2, 1&lt;/td>
&lt;td style="text-align:center">2, 1&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>J-test&lt;/td>
&lt;td style="text-align:center">18.825 [0.468]&lt;/td>
&lt;td style="text-align:center">8.174 [0.226]&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>When the spatial lag is removed, the &lt;strong>temporal persistence increases&lt;/strong> from $\rho = 0.290$ to $\rho = 0.323$ &amp;mdash; the temporal lag partially absorbs the missing spatial dynamics. The &lt;strong>INEFF coefficient inflates&lt;/strong> from 0.447 to 0.638 (a 43% increase), and &lt;strong>SIZE&lt;/strong> rises from 0.223 to 0.346 (a 55% increase). Without the spatial lag to capture bank interdependence, these covariates must do more work to explain the cross-sectional variation in credit risk, leading to upward bias.&lt;/p>
&lt;p>Importantly, both specifications pass the J-test (p = 0.468 and p = 0.226, respectively), meaning that both models have valid instruments. The choice between them must therefore be based on economic reasoning rather than diagnostic tests alone. The full model with the spatial lag is preferred because financial theory predicts bank interdependence, and the spatial autoregressive parameter $\psi = 0.394$ is highly significant (z = 4.65, p &amp;lt; 0.001).&lt;/p>
&lt;hr>
&lt;h2 id="7-short-run-and-long-run-effects">7. Short-run and long-run effects&lt;/h2>
&lt;p>In spatial dynamic panel models, the coefficient on a variable does not directly measure its total effect on the dependent variable. Because of the spatial lag ($\psi W \cdot NPL$) and the temporal lag ($\rho \, NPL_{i,t-1}$), a shock to any covariate propagates through the system both across banks (through the spatial multiplier) and over time (through dynamic accumulation). The &lt;code>estat impact&lt;/code> command decomposes these effects into &lt;strong>direct effects&lt;/strong> (the impact of a bank&amp;rsquo;s own covariate on its own NPL), &lt;strong>indirect effects&lt;/strong> (the impact transmitted through the network of interconnected banks), and &lt;strong>total effects&lt;/strong> (direct plus indirect).&lt;/p>
&lt;p>The long-run effects account for the full dynamic accumulation of a permanent change in a covariate. The long-run multiplier scales the short-run coefficients by $(1 - \rho)^{-1}$ for the direct channel and further by $(1 - \psi)^{-1}$ for the spatial multiplier:&lt;/p>
&lt;p>$$\text{Total LR effect} = \frac{\beta}{(1 - \rho)(1 - \psi)}$$&lt;/p>
&lt;p>In words, this equation says that a permanent 1-unit increase in a covariate has a total long-run effect equal to its short-run coefficient $\beta$ amplified by two multipliers: the temporal multiplier $1/(1-\rho)$, which captures the compounding of the effect over time as it feeds back through lagged NPL, and the spatial multiplier $1/(1-\psi)$, which captures the amplification as the effect spreads through the bank network. The diagram below illustrates this decomposition.&lt;/p>
&lt;pre>&lt;code class="language-mermaid">graph LR
B[&amp;quot;&amp;lt;b&amp;gt;Short-run&amp;lt;br/&amp;gt;coefficient&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;β = 2.452&amp;lt;br/&amp;gt;&amp;lt;i&amp;gt;(LIQUIDITY)&amp;lt;/i&amp;gt;&amp;quot;]
T[&amp;quot;&amp;lt;b&amp;gt;Temporal&amp;lt;br/&amp;gt;multiplier&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;1/(1−ρ)&amp;lt;br/&amp;gt;= 1/(1−0.290)&amp;lt;br/&amp;gt;= 1.408&amp;quot;]
D[&amp;quot;&amp;lt;b&amp;gt;Direct&amp;lt;br/&amp;gt;effect&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;3.547&amp;quot;]
S[&amp;quot;&amp;lt;b&amp;gt;Spatial&amp;lt;br/&amp;gt;multiplier&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;1/(1−ψ)&amp;lt;br/&amp;gt;= 1/(1−0.394)&amp;lt;br/&amp;gt;= 1.650&amp;quot;]
I[&amp;quot;&amp;lt;b&amp;gt;Indirect&amp;lt;br/&amp;gt;effect&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;4.218&amp;quot;]
Tot[&amp;quot;&amp;lt;b&amp;gt;Total&amp;lt;br/&amp;gt;effect&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;7.765&amp;quot;]
B --&amp;gt;|&amp;quot;× temporal&amp;quot;| T
T --&amp;gt;|&amp;quot;= direct&amp;quot;| D
D --&amp;gt;|&amp;quot;× spatial&amp;quot;| S
S --&amp;gt;|&amp;quot;= indirect&amp;quot;| I
D --&amp;gt; Tot
I --&amp;gt; Tot
style B fill:#6a9bcc,stroke:#141413,color:#fff
style T fill:#d97757,stroke:#141413,color:#fff
style D fill:#00d4c8,stroke:#141413,color:#141413
style S fill:#d97757,stroke:#141413,color:#fff
style I fill:#141413,stroke:#d97757,color:#fff
style Tot fill:#6a9bcc,stroke:#141413,color:#fff
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-stata">* Short-run effects (full model with factors)
estat impact, sr
&lt;/code>&lt;/pre>
&lt;h3 id="71-short-run-effects">7.1 Short-run effects&lt;/h3>
&lt;p>The short-run effects capture the immediate one-period impact of a covariate change, including the contemporaneous spatial spillover but not the dynamic accumulation over time.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Variable&lt;/th>
&lt;th style="text-align:center">SR Direct&lt;/th>
&lt;th style="text-align:center">SR Indirect&lt;/th>
&lt;th style="text-align:center">SR Total&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>INEFF&lt;/td>
&lt;td style="text-align:center">0.457&lt;/td>
&lt;td style="text-align:center">0.289&lt;/td>
&lt;td style="text-align:center">0.746&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CAR&lt;/td>
&lt;td style="text-align:center">0.031&lt;/td>
&lt;td style="text-align:center">0.020&lt;/td>
&lt;td style="text-align:center">0.051&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SIZE&lt;/td>
&lt;td style="text-align:center">0.227&lt;/td>
&lt;td style="text-align:center">0.144&lt;/td>
&lt;td style="text-align:center">0.371&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>BUFFER&lt;/td>
&lt;td style="text-align:center">-0.056&lt;/td>
&lt;td style="text-align:center">-0.035&lt;/td>
&lt;td style="text-align:center">-0.091&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>PROFIT&lt;/td>
&lt;td style="text-align:center">-0.005&lt;/td>
&lt;td style="text-align:center">-0.003&lt;/td>
&lt;td style="text-align:center">-0.009&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>QUALITY&lt;/td>
&lt;td style="text-align:center">0.187&lt;/td>
&lt;td style="text-align:center">0.118&lt;/td>
&lt;td style="text-align:center">0.305&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>LIQUIDITY&lt;/td>
&lt;td style="text-align:center">2.505&lt;/td>
&lt;td style="text-align:center">1.585&lt;/td>
&lt;td style="text-align:center">4.090&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>In the short run, indirect effects are roughly 63% of direct effects &amp;mdash; the spatial multiplier $(I - \psi W)^{-1}$ amplifies every shock by about 1.63x. For LIQUIDITY, the short-run total is 4.09 &amp;mdash; already substantially larger than the regression coefficient (2.452) due to spatial amplification alone.&lt;/p>
&lt;pre>&lt;code class="language-stata">* Long-run effects (full model with factors)
estat impact, lr
&lt;/code>&lt;/pre>
&lt;h3 id="72-long-run-effects-with-common-factors">7.2 Long-run effects with common factors&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Variable&lt;/th>
&lt;th style="text-align:center">Direct&lt;/th>
&lt;th style="text-align:center">Indirect&lt;/th>
&lt;th style="text-align:center">Total&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>INEFF&lt;/td>
&lt;td style="text-align:center">0.647*** (0.159)&lt;/td>
&lt;td style="text-align:center">0.769** (0.335)&lt;/td>
&lt;td style="text-align:center">1.417*** (0.427)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CAR&lt;/td>
&lt;td style="text-align:center">0.044*** (0.009)&lt;/td>
&lt;td style="text-align:center">0.052** (0.024)&lt;/td>
&lt;td style="text-align:center">0.097*** (0.029)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SIZE&lt;/td>
&lt;td style="text-align:center">0.322** (0.142)&lt;/td>
&lt;td style="text-align:center">0.383* (0.198)&lt;/td>
&lt;td style="text-align:center">0.705** (0.310)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>BUFFER&lt;/td>
&lt;td style="text-align:center">-0.079*** (0.018)&lt;/td>
&lt;td style="text-align:center">-0.094** (0.043)&lt;/td>
&lt;td style="text-align:center">-0.173*** (0.054)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>PROFIT&lt;/td>
&lt;td style="text-align:center">-0.008*** (0.002)&lt;/td>
&lt;td style="text-align:center">-0.009** (0.005)&lt;/td>
&lt;td style="text-align:center">-0.017*** (0.006)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>QUALITY&lt;/td>
&lt;td style="text-align:center">0.265*** (0.047)&lt;/td>
&lt;td style="text-align:center">0.315** (0.141)&lt;/td>
&lt;td style="text-align:center">0.580*** (0.167)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>LIQUIDITY&lt;/td>
&lt;td style="text-align:center">3.547*** (0.445)&lt;/td>
&lt;td style="text-align:center">4.218** (1.742)&lt;/td>
&lt;td style="text-align:center">7.765*** (1.904)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The long-run effects reveal that &lt;strong>indirect (spillover) effects are comparable to or larger than direct effects&lt;/strong> for every variable. For LIQUIDITY, the direct long-run effect is 3.547 and the indirect effect is 4.218, yielding a total of 7.765 &amp;mdash; meaning that a permanent 1 percentage point increase in the loan-to-deposit ratio across all banks would increase the system-wide NPL ratio by nearly 7.8 percentage points in the long run. The indirect effect exceeds the direct effect because the spatial multiplier amplifies shocks across the network of 18 average neighbors per bank.&lt;/p>
&lt;p>For INEFF (operational inefficiency), the total long-run effect is 1.417 &amp;mdash; more than three times the short-run coefficient of 0.447. A permanent deterioration in management quality cascades through the banking network as inefficient banks generate non-performing loans that spread to their interconnected counterparts through shared borrowers and counterparty risk.&lt;/p>
&lt;p>The BUFFER variable has a total long-run effect of -0.173, meaning that a 1 percentage point increase in capital buffers above the 8% regulatory minimum reduces system-wide NPL by 0.173 percentage points in the long run. Both the direct channel (-0.079, well-capitalized banks absorb losses better) and the indirect channel (-0.094, their stability reduces contagion to neighbors) contribute to this protective effect.&lt;/p>
&lt;h3 id="73-long-run-effects-without-common-factors">7.3 Long-run effects without common factors&lt;/h3>
&lt;p>To see how omitting common factors distorts spillover estimates, we compare the long-run effects from the full model (with factors) to those from the &lt;code>factmax(0)&lt;/code> specification.&lt;/p>
&lt;pre>&lt;code class="language-stata">* Long-run effects (model without factors)
spxtivdfreg NPL INEFF CAR SIZE BUFFER PROFIT QUALITY LIQUIDITY, ///
absorb(ID) splag tlags(1) spmatrix(&amp;quot;W.csv&amp;quot;, import) ///
iv(INTEREST CAR SIZE BUFFER PROFIT QUALITY LIQUIDITY, splags lag(1)) std factmax(0)
estat impact, lr
&lt;/code>&lt;/pre>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Variable&lt;/th>
&lt;th style="text-align:center">With factors (Total)&lt;/th>
&lt;th style="text-align:center">Without factors (Total)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>INEFF&lt;/td>
&lt;td style="text-align:center">1.417***&lt;/td>
&lt;td style="text-align:center">3.117**&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CAR&lt;/td>
&lt;td style="text-align:center">0.097***&lt;/td>
&lt;td style="text-align:center">0.145**&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SIZE&lt;/td>
&lt;td style="text-align:center">0.705**&lt;/td>
&lt;td style="text-align:center">0.756 (n.s.)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>BUFFER&lt;/td>
&lt;td style="text-align:center">-0.173***&lt;/td>
&lt;td style="text-align:center">-0.212*&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>PROFIT&lt;/td>
&lt;td style="text-align:center">-0.017***&lt;/td>
&lt;td style="text-align:center">-0.053***&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>QUALITY&lt;/td>
&lt;td style="text-align:center">0.580***&lt;/td>
&lt;td style="text-align:center">2.407***&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>LIQUIDITY&lt;/td>
&lt;td style="text-align:center">7.765***&lt;/td>
&lt;td style="text-align:center">7.176**&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The comparison reveals &lt;strong>severe distortion&lt;/strong> in the no-factor model&amp;rsquo;s long-run effects. The total effect of QUALITY more than quadruples from 0.580 to 2.407, and INEFF more than doubles from 1.417 to 3.117. These inflated estimates arise because the no-factor model attributes macroeconomic variation to the covariates: when aggregate loan quality deteriorates during a recession, the no-factor model incorrectly assigns this entire movement to the bank-level QUALITY and INEFF variables rather than recognizing the common factor (the recession itself).&lt;/p>
&lt;p>Conversely, SIZE loses statistical significance in the no-factor model (total effect = 0.756, not significant), even though it is significant in the full model (0.705, p &amp;lt; 0.05). The common factors capture macro-financial conditions that disproportionately affect larger banks, and without these factors, the SIZE effect is masked by omitted variable bias.&lt;/p>
&lt;hr>
&lt;h2 id="8-heterogeneous-slopes-the-mean-group-estimator">8. Heterogeneous slopes: the mean-group estimator&lt;/h2>
&lt;p>The models estimated so far assume that all banks share the same slope coefficients &amp;mdash; that is, the effect of LIQUIDITY on NPL is identical for all 350 banks. This is a strong assumption. Banks differ in their business models, geographic markets, and risk management practices, and these differences may translate into heterogeneous responses to the same financial ratios. The &lt;code>mg&lt;/code> (mean-group) option in &lt;code>spxtivdfreg&lt;/code> relaxes this assumption by estimating bank-specific slopes and reporting their cross-sectional average.&lt;/p>
&lt;pre>&lt;code class="language-stata">spxtivdfreg NPL INEFF CAR SIZE BUFFER PROFIT QUALITY LIQUIDITY, ///
absorb(ID) splag tlags(1) spmatrix(&amp;quot;W.csv&amp;quot;, import) ///
iv(INTEREST CAR SIZE BUFFER PROFIT QUALITY LIQUIDITY, splags lag(1)) std mg
&lt;/code>&lt;/pre>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Variable&lt;/th>
&lt;th style="text-align:center">Homogeneous (pooled)&lt;/th>
&lt;th style="text-align:center">Heterogeneous (MG)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>$\psi$ (W*NPL)&lt;/td>
&lt;td style="text-align:center">0.394*** (0.085)&lt;/td>
&lt;td style="text-align:center">0.032 (0.051)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$\rho$ (L1.NPL)&lt;/td>
&lt;td style="text-align:center">0.290*** (0.054)&lt;/td>
&lt;td style="text-align:center">0.301*** (0.015)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>INEFF&lt;/td>
&lt;td style="text-align:center">0.447*** (0.105)&lt;/td>
&lt;td style="text-align:center">0.759*** (0.158)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CAR&lt;/td>
&lt;td style="text-align:center">0.031*** (0.006)&lt;/td>
&lt;td style="text-align:center">0.218*** (0.026)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SIZE&lt;/td>
&lt;td style="text-align:center">0.223** (0.094)&lt;/td>
&lt;td style="text-align:center">2.004*** (0.339)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>BUFFER&lt;/td>
&lt;td style="text-align:center">-0.055*** (0.012)&lt;/td>
&lt;td style="text-align:center">-0.376*** (0.042)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>PROFIT&lt;/td>
&lt;td style="text-align:center">-0.005*** (0.002)&lt;/td>
&lt;td style="text-align:center">-0.018*** (0.006)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>QUALITY&lt;/td>
&lt;td style="text-align:center">0.183*** (0.031)&lt;/td>
&lt;td style="text-align:center">0.287** (0.139)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>LIQUIDITY&lt;/td>
&lt;td style="text-align:center">2.452*** (0.270)&lt;/td>
&lt;td style="text-align:center">6.330*** (0.506)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>_cons&lt;/td>
&lt;td style="text-align:center">-4.511*** (1.311)&lt;/td>
&lt;td style="text-align:center">-29.013*** (4.167)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The most striking result is that the &lt;strong>spatial autoregressive parameter becomes insignificant&lt;/strong> under the MG estimator: $\psi = 0.032$ (z = 0.62, p = 0.536). This suggests that the strong spatial spillovers found in the pooled model ($\psi = 0.394$) may partly reflect slope heterogeneity rather than genuine bank-to-bank contagion. When each bank is allowed its own coefficient on LIQUIDITY, SIZE, and other variables, the average spatial lag effect shrinks to near zero. This is a common finding in spatial econometrics: imposing homogeneous slopes in the presence of slope heterogeneity can create spurious spatial dependence.&lt;/p>
&lt;p>The &lt;strong>covariate coefficients increase substantially&lt;/strong> under the MG estimator. SIZE jumps from 0.223 to 2.004 (a nine-fold increase), BUFFER from -0.055 to -0.376 (a seven-fold increase), and CAR from 0.031 to 0.218 (a seven-fold increase). These larger MG coefficients suggest that the pooled model&amp;rsquo;s homogeneity restriction attenuates individual bank-level effects toward zero. The MG standard errors are generally smaller than the pooled standard errors for the temporal lag ($\rho$: 0.015 vs. 0.054) but larger for some covariates, reflecting the averaging of heterogeneous bank-specific estimates.&lt;/p>
&lt;p>The &lt;strong>temporal persistence&lt;/strong> remains stable: $\rho = 0.301$ (MG) versus $\rho = 0.290$ (pooled). This robustness suggests that credit risk persistence is a genuine phenomenon shared across all banks, not an artifact of slope heterogeneity. Whether a bank is large or small, well-managed or poorly managed, about 30% of its current NPL ratio is inherited from the previous quarter.&lt;/p>
&lt;p>The MG estimator is only $\sqrt{N}$-consistent (versus $\sqrt{NT}$-consistent for the pooled estimator), making it inherently less efficient and more susceptible to outliers. With 350 banks and 35 time periods, a handful of banks with extreme coefficient estimates can shift the MG average substantially. To investigate, individual bank-specific estimates can be inspected using the &lt;code>mg(101)&lt;/code> option (which displays estimates for the bank with ID 101) or extracted from the &lt;code>e(b_mg)&lt;/code> and &lt;code>e(se_mg)&lt;/code> matrices for further analysis &amp;mdash; for example, to compute trimmed or median estimates that are robust to outlier influence. However, further exploration of individual heterogeneity is beyond the scope of this tutorial.&lt;/p>
&lt;hr>
&lt;h2 id="9-model-comparison-and-specification-guidance">9. Model comparison and specification guidance&lt;/h2>
&lt;p>The following table summarizes the four model specifications estimated in this tutorial, highlighting the key coefficient estimates and diagnostic tests.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;/th>
&lt;th style="text-align:center">Full model&lt;/th>
&lt;th style="text-align:center">No factors&lt;/th>
&lt;th style="text-align:center">No spatial lag&lt;/th>
&lt;th style="text-align:center">Heterogeneous (MG)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>$\psi$ (spatial)&lt;/td>
&lt;td style="text-align:center">0.394***&lt;/td>
&lt;td style="text-align:center">0.288***&lt;/td>
&lt;td style="text-align:center">&amp;mdash;&lt;/td>
&lt;td style="text-align:center">0.032&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$\rho$ (temporal)&lt;/td>
&lt;td style="text-align:center">0.290***&lt;/td>
&lt;td style="text-align:center">0.594***&lt;/td>
&lt;td style="text-align:center">0.323***&lt;/td>
&lt;td style="text-align:center">0.301***&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>LIQUIDITY&lt;/td>
&lt;td style="text-align:center">2.452***&lt;/td>
&lt;td style="text-align:center">0.843***&lt;/td>
&lt;td style="text-align:center">2.534***&lt;/td>
&lt;td style="text-align:center">6.330***&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Factors&lt;/td>
&lt;td style="text-align:center">$r_x$=2, $r_u$=1&lt;/td>
&lt;td style="text-align:center">0, 0&lt;/td>
&lt;td style="text-align:center">$r_x$=2, $r_u$=1&lt;/td>
&lt;td style="text-align:center">$r_x$=2, $r_u$=1&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>J-test p-value&lt;/td>
&lt;td style="text-align:center">0.468&lt;/td>
&lt;td style="text-align:center">0.000&lt;/td>
&lt;td style="text-align:center">0.226&lt;/td>
&lt;td style="text-align:center">&amp;mdash;&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Slopes&lt;/td>
&lt;td style="text-align:center">Homogeneous&lt;/td>
&lt;td style="text-align:center">Homogeneous&lt;/td>
&lt;td style="text-align:center">Homogeneous&lt;/td>
&lt;td style="text-align:center">Heterogeneous&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The decision diagram below provides a practical guide for choosing among these specifications.&lt;/p>
&lt;pre>&lt;code class="language-mermaid">graph TD
START[&amp;quot;&amp;lt;b&amp;gt;Start&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;Spatial dynamic panel&amp;lt;br/&amp;gt;with suspected factors&amp;quot;]
JTEST[&amp;quot;&amp;lt;b&amp;gt;J-test&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;Estimate with factors&amp;lt;br/&amp;gt;and without factors&amp;quot;]
FACTORS[&amp;quot;&amp;lt;b&amp;gt;Include factors&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;J-test fails without&amp;lt;br/&amp;gt;(p &amp;lt; 0.05)&amp;quot;]
NOFACT[&amp;quot;&amp;lt;b&amp;gt;No factors needed&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;J-test passes without&amp;lt;br/&amp;gt;(p ≥ 0.05)&amp;quot;]
SPLAG[&amp;quot;&amp;lt;b&amp;gt;Spatial lag?&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;Is ψ significant?&amp;quot;]
FULL[&amp;quot;&amp;lt;b&amp;gt;Full model&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;spxtivdfreg with&amp;lt;br/&amp;gt;splag + factors&amp;quot;]
NOSPL[&amp;quot;&amp;lt;b&amp;gt;xtivdfreg&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;Dynamic panel&amp;lt;br/&amp;gt;with factors only&amp;quot;]
MG[&amp;quot;&amp;lt;b&amp;gt;MG estimator&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;Test slope&amp;lt;br/&amp;gt;heterogeneity&amp;quot;]
START --&amp;gt; JTEST
JTEST --&amp;gt;|&amp;quot;J rejects without factors&amp;quot;| FACTORS
JTEST --&amp;gt;|&amp;quot;J passes without factors&amp;quot;| NOFACT
FACTORS --&amp;gt; SPLAG
SPLAG --&amp;gt;|&amp;quot;ψ significant&amp;quot;| FULL
SPLAG --&amp;gt;|&amp;quot;ψ not significant&amp;quot;| NOSPL
FULL --&amp;gt; MG
style START fill:#141413,stroke:#d97757,color:#fff
style JTEST fill:#6a9bcc,stroke:#141413,color:#fff
style FACTORS fill:#00d4c8,stroke:#141413,color:#141413
style NOFACT fill:#d97757,stroke:#141413,color:#fff
style SPLAG fill:#6a9bcc,stroke:#141413,color:#fff
style FULL fill:#00d4c8,stroke:#141413,color:#141413
style NOSPL fill:#d97757,stroke:#141413,color:#fff
style MG fill:#6a9bcc,stroke:#141413,color:#fff
&lt;/code>&lt;/pre>
&lt;p>The J-test is the first and most important diagnostic: in our application, it unambiguously rejects the no-factor specification (p &amp;lt; 0.001), confirming that common factors must be included. With factors, the spatial lag is highly significant ($\psi = 0.394$, z = 4.65), supporting the full model. The MG estimator provides a robustness check that reveals potential slope heterogeneity, but its insignificant spatial lag should be interpreted cautiously &amp;mdash; it may indicate genuine absence of spillovers, or it may reflect the difficulty of estimating bank-specific spatial parameters with only 35 time periods.&lt;/p>
&lt;hr>
&lt;h2 id="10-discussion">10. Discussion&lt;/h2>
&lt;h3 id="methodological-implications">Methodological implications&lt;/h3>
&lt;p>The &lt;code>spxtivdfreg&lt;/code> package represents a significant advance in the spatial panel toolkit for Stata. By combining defactored IV estimation with spatial lag modeling, it addresses a long-standing limitation of existing packages: the inability to account for unobserved common factors. The results in this tutorial demonstrate that ignoring common factors leads to three specific problems: (1) inflated temporal persistence ($\rho$ doubling from 0.290 to 0.594), (2) distorted covariate effects (LIQUIDITY falling by 66% from 2.452 to 0.843), and (3) invalid instruments (J-test rejecting at p &amp;lt; 0.001). These are not minor specification issues &amp;mdash; they fundamentally change the economic story that emerges from the analysis.&lt;/p>
&lt;p>Readers who have worked through the companion &lt;a href="https://carlos-mendez.org/post/stata_sp_regression_panel/">spatial panel regression tutorial with &lt;code>xsmle&lt;/code>&lt;/a> may wonder: what would happen if we used &lt;code>xsmle&lt;/code> on this banking dataset? Since &lt;code>xsmle&lt;/code> uses maximum likelihood without common factors, its estimates would resemble the &amp;ldquo;Without factors&amp;rdquo; column in Section 5 &amp;mdash; with temporal persistence inflated to $\rho \approx 0.59$, spatial spillovers compressed to $\psi \approx 0.29$, and the LIQUIDITY effect attenuated by two-thirds. The J-test rejection (p &amp;lt; 0.001) confirms that this ML specification is misspecified. The &lt;code>spxtivdfreg&lt;/code> approach avoids these problems by defactoring the data before estimation.&lt;/p>
&lt;h3 id="empirical-implications">Empirical implications&lt;/h3>
&lt;p>The empirical application reveals that credit risk in US banking operates through multiple interacting channels. The short-run coefficient on LIQUIDITY (2.452) implies that a 10 percentage point increase in the loan-to-deposit ratio increases non-performing loans by about 0.25 percentage points in the current quarter. But the long-run total effect (7.765) is more than three times larger, reflecting the amplification through temporal persistence and spatial contagion. This means that the true cost of excessive lending is far larger than what contemporaneous cross-sectional regressions suggest.&lt;/p>
&lt;p>The common factors that the estimator identifies &amp;mdash; 2 in the regressors and 1 in the error &amp;mdash; capture aggregate forces such as Federal Reserve monetary policy, the collapse of the housing market, and the tightening of interbank lending during the crisis. These factors account for 33.5% of the residual variance, underscoring the importance of modeling macro-financial shocks explicitly rather than assuming they are absorbed by time fixed effects. Traditional two-way fixed effects would capture these factors only if they had &lt;strong>homogeneous&lt;/strong> effects across banks, but the interactive fixed effect structure $\lambda_i' f_t$ allows for &lt;strong>heterogeneous&lt;/strong> loadings &amp;mdash; some banks are more sensitive to interest rate shocks, others to housing market conditions.&lt;/p>
&lt;h3 id="policy-implications">Policy implications&lt;/h3>
&lt;p>For banking regulators, the indirect long-run effects are particularly informative. The total long-run effect of BUFFER on NPL is -0.173, meaning that a system-wide 1 percentage point increase in capital buffers above the 8% minimum would reduce non-performing loans by 0.17 percentage points across the network. This effect is roughly split between the direct channel (banks with more capital absorb losses better) and the indirect channel (their stability reduces contagion to connected banks). This decomposition supports macroprudential policies that target &lt;strong>system-wide&lt;/strong> capital requirements rather than bank-specific ones, since the spillover benefits of higher capital buffers are nearly as large as the direct benefits.&lt;/p>
&lt;hr>
&lt;h2 id="11-summary-and-next-steps">11. Summary and next steps&lt;/h2>
&lt;p>This tutorial demonstrated the complete workflow for estimating spatial dynamic panel models with unobserved common factors in Stata using the &lt;code>spxtivdfreg&lt;/code> package. The key takeaways are:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Common factors are essential.&lt;/strong> The J-test rejects the no-factor model (p &amp;lt; 0.001), and omitting factors inflates temporal persistence from $\rho = 0.290$ to $\rho = 0.594$ &amp;mdash; a doubling that confuses macroeconomic persistence with bank-level credit risk dynamics.&lt;/li>
&lt;li>&lt;strong>Spatial spillovers are economically significant.&lt;/strong> The spatial autoregressive parameter $\psi = 0.394$ implies that a 1 percentage point increase in neighbors' NPL raises a bank&amp;rsquo;s own NPL by 0.39 percentage points. Long-run indirect effects exceed direct effects for most variables.&lt;/li>
&lt;li>&lt;strong>Long-run total effects are large.&lt;/strong> For LIQUIDITY, the total long-run effect is 7.765 &amp;mdash; more than three times the short-run coefficient of 2.452 &amp;mdash; reflecting amplification through both temporal persistence and spatial contagion.&lt;/li>
&lt;li>&lt;strong>Slope heterogeneity matters for interpretation.&lt;/strong> The mean-group estimator drives the spatial lag to insignificance ($\psi = 0.032$, p = 0.536), suggesting that the pooled model&amp;rsquo;s strong spatial spillovers may partly reflect cross-bank heterogeneity in covariate effects.&lt;/li>
&lt;/ul>
&lt;p>For further study, the companion tutorial on &lt;a href="https://carlos-mendez.org/post/stata_sp_regression_panel/">spatial panel regression with xsmle&lt;/a> covers maximum likelihood estimation of static and dynamic spatial panels, including the Spatial Durbin Model with Wald specification tests and the Lee-Yu bias correction. For cross-sectional spatial models, see the &lt;a href="https://carlos-mendez.org/post/stata_sp_regression_cross_section/">cross-sectional spatial regression tutorial&lt;/a>. The original paper by Kripfganz and Sarafidis (2025) provides the full theoretical derivation and Monte Carlo simulations that establish the estimator&amp;rsquo;s properties.&lt;/p>
&lt;hr>
&lt;h2 id="12-exercises">12. Exercises&lt;/h2>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Endogeneity of INEFF.&lt;/strong> The full model treats &lt;code>INEFF&lt;/code> (operational inefficiency) as endogenous and uses &lt;code>INTEREST&lt;/code> (interest expenses / deposits) as an excluded instrument. Re-estimate the model treating &lt;code>INEFF&lt;/code> as exogenous by removing &lt;code>INTEREST&lt;/code> from the &lt;code>iv()&lt;/code> option and adding &lt;code>INEFF&lt;/code> to the exogenous instrument list. Does the coefficient on &lt;code>INEFF&lt;/code> change substantially? What does this tell you about the direction of endogeneity bias?&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Alternative factor structure.&lt;/strong> The estimator automatically selects 2 factors in the regressors and 1 in the error. Use the &lt;code>factmax()&lt;/code> option to constrain the maximum number of factors to 1 or 3 and re-estimate the model. Compare the spatial parameter $\psi$, the J-test statistic, and the variance decomposition ($\rho_{factor}$). How sensitive are the results to the assumed number of common factors?&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Short-run vs. long-run effects.&lt;/strong> Use &lt;code>estat impact, sr&lt;/code> to compute the short-run direct, indirect, and total effects and compare them to the long-run effects in Table 3. For which variable is the ratio of long-run to short-run total effect the largest? What does this ratio tell you about the relative importance of temporal persistence vs. spatial amplification for that variable?&lt;/p>
&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="references">References&lt;/h2>
&lt;ol>
&lt;li>&lt;a href="https://doi.org/10.18637/jss.v113.i06" target="_blank" rel="noopener">Kripfganz, S. &amp;amp; Sarafidis, V. (2025). Estimating spatial dynamic panel data models with unobserved common factors in Stata. &lt;em>Journal of Statistical Software&lt;/em>, 113(6).&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://doi.org/10.1177/1536867X211045558" target="_blank" rel="noopener">Kripfganz, S. &amp;amp; Sarafidis, V. (2021). Instrumental-variable estimation of large-T panel-data models with common factors. &lt;em>Stata Journal&lt;/em>, 21(3), 659&amp;ndash;686.&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://doi.org/10.1080/07474938.2011.611458" target="_blank" rel="noopener">Sarafidis, V. &amp;amp; Wansbeek, T. (2012). Cross-sectional dependence in panel data analysis. &lt;em>Econometric Reviews&lt;/em>, 31(5), 483&amp;ndash;531.&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://doi.org/10.1111/j.1468-0262.2006.00692.x" target="_blank" rel="noopener">Pesaran, M. H. (2006). Estimation and inference in large heterogeneous panels with a multifactor error structure. &lt;em>Econometrica&lt;/em>, 74(4), 967&amp;ndash;1012.&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://link.springer.com/book/10.1007/978-3-642-40340-8" target="_blank" rel="noopener">Elhorst, J. P. (2014). &lt;em>Spatial Econometrics: From Cross-Sectional Data to Spatial Panels&lt;/em>. Springer.&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://doi.org/10.1177/1536867X1701700109" target="_blank" rel="noopener">Belotti, F., Hughes, G., &amp;amp; Mortari, A. P. (2017). Spatial panel-data models using Stata. &lt;em>Stata Journal&lt;/em>, 17(1), 139&amp;ndash;180.&lt;/a>&lt;/li>
&lt;/ol></description></item><item><title>Exploratory Spatial Data Analysis: Spatial Clusters and Dynamics of Human Development in South America</title><link>https://carlos-mendez.org/post/python_esda2/</link><pubDate>Sun, 22 Mar 2026 00:00:00 +0000</pubDate><guid>https://carlos-mendez.org/post/python_esda2/</guid><description>&lt;h2 id="1-overview">1. Overview&lt;/h2>
&lt;p>When we look at a map of human development across South America, a pattern immediately stands out: prosperous regions tend to cluster together, and so do lagging regions. But is this clustering statistically significant, or could it arise by chance? And how have these spatial clusters evolved over time?&lt;/p>
&lt;p>&lt;strong>Exploratory Spatial Data Analysis (ESDA)&lt;/strong> provides the tools to answer these questions. ESDA is a set of techniques for visualizing spatial distributions, identifying patterns of spatial clustering, and detecting spatial outliers. Unlike standard exploratory data analysis, which treats observations as independent, ESDA explicitly accounts for the geographic location of each observation and the relationships between neighbors.&lt;/p>
&lt;p>This tutorial uses the &lt;a href="https://globaldatalab.org/shdi/" target="_blank" rel="noopener">Subnational Human Development Index&lt;/a> (SHDI) from &lt;a href="https://doi.org/10.1038/sdata.2019.38" target="_blank" rel="noopener">Smits and Permanyer (2019)&lt;/a> for &lt;strong>153 sub-national regions across 12 South American countries&lt;/strong> in 2013 and 2019 &amp;mdash; the same dataset from the &lt;a href="https://carlos-mendez.org/post/python_pca2/">Pooled PCA tutorial&lt;/a>. We progress from simple scatter plots and choropleth maps to formal tests of spatial dependence (Moran&amp;rsquo;s I), local cluster identification (LISA maps), and space-time dynamics. By the end, you will be able to answer: &lt;strong>do nearby regions in South America share similar development levels, and how have these spatial clusters evolved between 2013 and 2019?&lt;/strong>&lt;/p>
&lt;p>&lt;strong>Learning objectives:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Understand the concept of spatial autocorrelation and why it matters for regional analysis&lt;/li>
&lt;li>Create choropleth maps and scatter plots to visualize spatial distributions&lt;/li>
&lt;li>Build and interpret a spatial weights matrix using Queen contiguity&lt;/li>
&lt;li>Compute and interpret global Moran&amp;rsquo;s I for spatial dependence testing&lt;/li>
&lt;li>Identify local spatial clusters (HH, LL) and outliers (HL, LH) using LISA statistics&lt;/li>
&lt;li>Explore space-time dynamics of spatial clusters using directional Moran scatter plots&lt;/li>
&lt;li>Compare country-level development trajectories within the spatial framework&lt;/li>
&lt;/ul>
&lt;h2 id="2-the-esda-pipeline">2. The ESDA pipeline&lt;/h2>
&lt;p>The analysis follows a natural progression from visualization to formal testing. Each step builds on the previous one, moving from &amp;ldquo;what does the data look like?&amp;rdquo; to &amp;ldquo;is the spatial pattern statistically significant?&amp;rdquo; to &amp;ldquo;where exactly are the clusters?&amp;rdquo;&lt;/p>
&lt;pre>&lt;code class="language-mermaid">graph LR
A[&amp;quot;&amp;lt;b&amp;gt;Step 1&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;Load &amp;amp;&amp;lt;br/&amp;gt;Explore&amp;quot;] --&amp;gt; B[&amp;quot;&amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;Visualize&amp;lt;br/&amp;gt;Maps&amp;quot;]
B --&amp;gt; C[&amp;quot;&amp;lt;b&amp;gt;Step 3&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;Spatial&amp;lt;br/&amp;gt;Weights&amp;quot;]
C --&amp;gt; D[&amp;quot;&amp;lt;b&amp;gt;Step 4&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;Global&amp;lt;br/&amp;gt;Moran's I&amp;quot;]
D --&amp;gt; E[&amp;quot;&amp;lt;b&amp;gt;Step 5&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;Local&amp;lt;br/&amp;gt;LISA&amp;quot;]
E --&amp;gt; F[&amp;quot;&amp;lt;b&amp;gt;Step 6&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;Space-Time&amp;lt;br/&amp;gt;Dynamics&amp;quot;]
style A fill:#141413,stroke:#6a9bcc,color:#fff
style B fill:#d97757,stroke:#141413,color:#fff
style C fill:#6a9bcc,stroke:#141413,color:#fff
style D fill:#6a9bcc,stroke:#141413,color:#fff
style E fill:#00d4c8,stroke:#141413,color:#fff
style F fill:#1a3a8a,stroke:#141413,color:#fff
&lt;/code>&lt;/pre>
&lt;p>Steps 1&amp;ndash;2 are purely visual &amp;mdash; they build intuition about where high and low values are concentrated. Step 3 formalizes the notion of &amp;ldquo;neighbors&amp;rdquo; through a spatial weights matrix. Steps 4&amp;ndash;5 use that matrix to compute statistics that quantify spatial clustering, first globally (one number for the whole map) and then locally (one number per region). Step 6 connects the spatial and temporal dimensions by tracking how regions move through the Moran scatter plot between periods.&lt;/p>
&lt;h2 id="3-setup-and-imports">3. Setup and imports&lt;/h2>
&lt;p>The analysis uses &lt;a href="https://geopandas.org/" target="_blank" rel="noopener">GeoPandas&lt;/a> for spatial data handling, &lt;a href="https://pysal.org/" target="_blank" rel="noopener">PySAL&lt;/a> for spatial statistics, and &lt;a href="https://splot.readthedocs.io/" target="_blank" rel="noopener">splot&lt;/a> for specialized spatial visualizations.&lt;/p>
&lt;pre>&lt;code class="language-python">import numpy as np
import pandas as pd
import geopandas as gpd
import matplotlib.pyplot as plt
from libpysal.weights import Queen
from libpysal.weights import lag_spatial
from esda.moran import Moran, Moran_Local
from splot.esda import moran_scatterplot, lisa_cluster
from splot.libpysal import plot_spatial_weights
from adjustText import adjust_text
import mapclassify
# Reproducibility
RANDOM_SEED = 42
# Site color palette
STEEL_BLUE = &amp;quot;#6a9bcc&amp;quot;
WARM_ORANGE = &amp;quot;#d97757&amp;quot;
NEAR_BLACK = &amp;quot;#141413&amp;quot;
TEAL = &amp;quot;#00d4c8&amp;quot;
&lt;/code>&lt;/pre>
&lt;details>
&lt;summary>Dark theme figure styling (click to expand)&lt;/summary>
&lt;pre>&lt;code class="language-python"># Dark theme palette (consistent with site navbar/dark sections)
DARK_NAVY = &amp;quot;#0f1729&amp;quot;
GRID_LINE = &amp;quot;#1f2b5e&amp;quot;
LIGHT_TEXT = &amp;quot;#c8d0e0&amp;quot;
WHITE_TEXT = &amp;quot;#e8ecf2&amp;quot;
# Plot defaults — minimal, spine-free, dark background
plt.rcParams.update({
&amp;quot;figure.facecolor&amp;quot;: DARK_NAVY,
&amp;quot;axes.facecolor&amp;quot;: DARK_NAVY,
&amp;quot;axes.edgecolor&amp;quot;: DARK_NAVY,
&amp;quot;axes.linewidth&amp;quot;: 0,
&amp;quot;axes.labelcolor&amp;quot;: LIGHT_TEXT,
&amp;quot;axes.titlecolor&amp;quot;: WHITE_TEXT,
&amp;quot;axes.spines.top&amp;quot;: False,
&amp;quot;axes.spines.right&amp;quot;: False,
&amp;quot;axes.spines.left&amp;quot;: False,
&amp;quot;axes.spines.bottom&amp;quot;: False,
&amp;quot;axes.grid&amp;quot;: True,
&amp;quot;grid.color&amp;quot;: GRID_LINE,
&amp;quot;grid.linewidth&amp;quot;: 0.6,
&amp;quot;grid.alpha&amp;quot;: 0.8,
&amp;quot;xtick.color&amp;quot;: LIGHT_TEXT,
&amp;quot;ytick.color&amp;quot;: LIGHT_TEXT,
&amp;quot;xtick.major.size&amp;quot;: 0,
&amp;quot;ytick.major.size&amp;quot;: 0,
&amp;quot;text.color&amp;quot;: WHITE_TEXT,
&amp;quot;font.size&amp;quot;: 12,
&amp;quot;legend.frameon&amp;quot;: False,
&amp;quot;legend.fontsize&amp;quot;: 11,
&amp;quot;legend.labelcolor&amp;quot;: LIGHT_TEXT,
&amp;quot;figure.edgecolor&amp;quot;: DARK_NAVY,
&amp;quot;savefig.facecolor&amp;quot;: DARK_NAVY,
&amp;quot;savefig.edgecolor&amp;quot;: DARK_NAVY,
})
&lt;/code>&lt;/pre>
&lt;/details>
&lt;h2 id="4-data-loading-and-exploration">4. Data loading and exploration&lt;/h2>
&lt;p>The dataset is a GeoJSON file containing polygon geometries and development indicators for 153 sub-national regions across South America. It is a spatial version of the data from the &lt;a href="https://carlos-mendez.org/post/python_pca2/">Pooled PCA tutorial&lt;/a>, sourced from the &lt;a href="https://globaldatalab.org/shdi/" target="_blank" rel="noopener">Global Data Lab&lt;/a> (&lt;a href="https://doi.org/10.1038/sdata.2019.38" target="_blank" rel="noopener">Smits and Permanyer, 2019&lt;/a>). Each region has the Subnational Human Development Index (SHDI) and its three component indices &amp;mdash; Health, Education, and Income &amp;mdash; for 2013 and 2019.&lt;/p>
&lt;pre>&lt;code class="language-python">DATA_URL = &amp;quot;https://raw.githubusercontent.com/cmg777/starter-academic-v501/master/content/post/python_esda2/data.geojson&amp;quot;
gdf = gpd.read_file(DATA_URL)
print(f&amp;quot;Loaded: {gdf.shape[0]} rows, {gdf.shape[1]} columns&amp;quot;)
print(f&amp;quot;Countries: {gdf['country'].nunique()}&amp;quot;)
print(f&amp;quot;CRS: {gdf.crs}&amp;quot;)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Loaded: 153 rows, 25 columns
Countries: 12
CRS: EPSG:4326
&lt;/code>&lt;/pre>
&lt;p>Before computing change columns, we prepare the data for labeling. Some region names in the raw data are very long (e.g., &amp;ldquo;Chubut, Neuquen, Rio Negro, Santa Cruz, Tierra del Fuego&amp;rdquo;), so we simplify them. We also create a &lt;code>region_country&lt;/code> column that appends the ISO country code to each region name &amp;mdash; this makes labels immediately informative when regions from different countries appear on the same plot.&lt;/p>
&lt;pre>&lt;code class="language-python"># Country name → ISO 3166-1 alpha-3 code
COUNTRY_ISO = {
&amp;quot;Argentina&amp;quot;: &amp;quot;ARG&amp;quot;, &amp;quot;Bolivia&amp;quot;: &amp;quot;BOL&amp;quot;, &amp;quot;Brazil&amp;quot;: &amp;quot;BRA&amp;quot;,
&amp;quot;Chili&amp;quot;: &amp;quot;CHL&amp;quot;, &amp;quot;Colombia&amp;quot;: &amp;quot;COL&amp;quot;, &amp;quot;Ecuador&amp;quot;: &amp;quot;ECU&amp;quot;,
&amp;quot;Guyana&amp;quot;: &amp;quot;GUY&amp;quot;, &amp;quot;Paraguay&amp;quot;: &amp;quot;PRY&amp;quot;, &amp;quot;Peru&amp;quot;: &amp;quot;PER&amp;quot;,
&amp;quot;Suriname&amp;quot;: &amp;quot;SUR&amp;quot;, &amp;quot;Uruguay&amp;quot;: &amp;quot;URY&amp;quot;, &amp;quot;Venezuela&amp;quot;: &amp;quot;VEN&amp;quot;,
}
gdf[&amp;quot;country_iso&amp;quot;] = gdf[&amp;quot;country&amp;quot;].map(COUNTRY_ISO)
# Simplify long region names
RENAME = {
&amp;quot;Catamarca, La Rioja, San Juan&amp;quot;: &amp;quot;Catamarca-La Rioja&amp;quot;,
&amp;quot;Corrientes, Entre Rios, Misiones&amp;quot;: &amp;quot;Corrientes-Misiones&amp;quot;,
&amp;quot;Chubut, Neuquen, Rio Negro, Santa Cruz, Tierra del Fuego&amp;quot;: &amp;quot;Patagonia&amp;quot;,
&amp;quot;La Pampa, San Luis, Mendoza&amp;quot;: &amp;quot;La Pampa-Mendoza&amp;quot;,
&amp;quot;Santiago del Estero, Tucuman&amp;quot;: &amp;quot;Tucuman-Sgo Estero&amp;quot;,
&amp;quot;Tarapaca (incl Arica and Parinacota)&amp;quot;: &amp;quot;Tarapaca&amp;quot;,
&amp;quot;Valparaiso (former Aconcagua)&amp;quot;: &amp;quot;Valparaiso&amp;quot;,
&amp;quot;Los Lagos (incl Los Rios)&amp;quot;: &amp;quot;Los Lagos&amp;quot;,
&amp;quot;Magallanes and La Antartica Chilena&amp;quot;: &amp;quot;Magallanes&amp;quot;,
&amp;quot;Antioquia (incl Medellin)&amp;quot;: &amp;quot;Antioquia&amp;quot;,
&amp;quot;Atlantico (incl Barranquilla)&amp;quot;: &amp;quot;Atlantico&amp;quot;,
&amp;quot;Bolivar (Sur and Norte)&amp;quot;: &amp;quot;Bolivar&amp;quot;,
&amp;quot;Essequibo Islands-West Demerara&amp;quot;: &amp;quot;Essequibo-W Demerara&amp;quot;,
&amp;quot;East Berbice-Corentyne&amp;quot;: &amp;quot;E Berbice-Corentyne&amp;quot;,
&amp;quot;Upper Takutu-Upper Essequibo&amp;quot;: &amp;quot;Upper Takutu-Essequibo&amp;quot;,
&amp;quot;Upper Demerara-Berbice&amp;quot;: &amp;quot;Upper Demerara&amp;quot;,
&amp;quot;Cuyuni-Mazaruni-Upper Essequibo&amp;quot;: &amp;quot;Cuyuni-Mazaruni&amp;quot;,
&amp;quot;Region Metropolitana&amp;quot;: &amp;quot;R. Metropolitana&amp;quot;,
&amp;quot;Federal District&amp;quot;: &amp;quot;Federal Dist.&amp;quot;,
&amp;quot;City of Buenos Aires&amp;quot;: &amp;quot;C. Buenos Aires&amp;quot;,
&amp;quot;Brokopondo and Sipaliwini&amp;quot;: &amp;quot;Brokopondo-Sipaliwini&amp;quot;,
&amp;quot;Montevideo and Metropolitan area&amp;quot;: &amp;quot;Montevideo&amp;quot;,
}
gdf[&amp;quot;region&amp;quot;] = gdf[&amp;quot;region&amp;quot;].replace(RENAME)
# Create region_country label column
gdf[&amp;quot;region_country&amp;quot;] = gdf[&amp;quot;region&amp;quot;] + &amp;quot; (&amp;quot; + gdf[&amp;quot;country_iso&amp;quot;] + &amp;quot;)&amp;quot;
&lt;/code>&lt;/pre>
&lt;p>We then compute the change in SHDI and its components between the two periods.&lt;/p>
&lt;pre>&lt;code class="language-python">gdf[&amp;quot;shdi_change&amp;quot;] = gdf[&amp;quot;shdi2019&amp;quot;] - gdf[&amp;quot;shdi2013&amp;quot;]
gdf[&amp;quot;health_change&amp;quot;] = gdf[&amp;quot;healthindex2019&amp;quot;] - gdf[&amp;quot;healthindex2013&amp;quot;]
gdf[&amp;quot;educ_change&amp;quot;] = gdf[&amp;quot;edindex2019&amp;quot;] - gdf[&amp;quot;edindex2013&amp;quot;]
gdf[&amp;quot;income_change&amp;quot;] = gdf[&amp;quot;incindex2019&amp;quot;] - gdf[&amp;quot;incindex2013&amp;quot;]
print(gdf[[&amp;quot;shdi2013&amp;quot;, &amp;quot;shdi2019&amp;quot;, &amp;quot;shdi_change&amp;quot;]].describe().round(4).to_string())
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text"> shdi2013 shdi2019 shdi_change
count 153.0000 153.0000 153.0000
mean 0.7424 0.7477 0.0053
std 0.0594 0.0613 0.0319
min 0.5540 0.5580 -0.0670
25% 0.7070 0.7150 0.0090
50% 0.7430 0.7440 0.0150
75% 0.7740 0.7840 0.0250
max 0.8780 0.8830 0.0450
&lt;/code>&lt;/pre>
&lt;p>The dataset covers 153 regions across 12 South American countries. Mean SHDI increased modestly from 0.7424 in 2013 to 0.7477 in 2019 (+0.0053), but the change varied widely: from a maximum decline of -0.0670 to a maximum improvement of +0.0450. The standard deviation of SHDI also increased slightly (0.0594 to 0.0613), hinting that regional disparities may have widened.&lt;/p>
&lt;h2 id="5-exploratory-scatter-plots">5. Exploratory scatter plots&lt;/h2>
&lt;h3 id="51-hdi-scatter-2013-vs-2019">5.1 HDI scatter: 2013 vs 2019&lt;/h3>
&lt;p>A scatter plot of SHDI in 2013 against SHDI in 2019 provides a quick overview of temporal dynamics. Points above the 45-degree line represent regions that improved; points below represent regions that declined.&lt;/p>
&lt;pre>&lt;code class="language-python">fig, ax = plt.subplots(figsize=(8, 7))
ax.scatter(gdf[&amp;quot;shdi2013&amp;quot;], gdf[&amp;quot;shdi2019&amp;quot;],
color=STEEL_BLUE, edgecolors=DARK_NAVY, s=45, alpha=0.75, zorder=3)
lims = [min(gdf[&amp;quot;shdi2013&amp;quot;].min(), gdf[&amp;quot;shdi2019&amp;quot;].min()) - 0.01,
max(gdf[&amp;quot;shdi2013&amp;quot;].max(), gdf[&amp;quot;shdi2019&amp;quot;].max()) + 0.01]
ax.plot(lims, lims, color=WARM_ORANGE, linewidth=1.5, linestyle=&amp;quot;--&amp;quot;,
label=&amp;quot;45° line (no change)&amp;quot;, zorder=2)
ax.set_xlabel(&amp;quot;SHDI 2013&amp;quot;)
ax.set_ylabel(&amp;quot;SHDI 2019&amp;quot;)
ax.set_title(&amp;quot;Subnational HDI: 2013 vs 2019&amp;quot;)
ax.legend()
# Label extreme regions (biggest gains, biggest losses, highest, lowest)
residual = gdf[&amp;quot;shdi2019&amp;quot;] - gdf[&amp;quot;shdi2013&amp;quot;]
extremes = set()
extremes.update(residual.nlargest(3).index.tolist())
extremes.update(residual.nsmallest(3).index.tolist())
extremes.update(gdf[&amp;quot;shdi2019&amp;quot;].nlargest(2).index.tolist())
extremes.update(gdf[&amp;quot;shdi2019&amp;quot;].nsmallest(2).index.tolist())
texts = []
for i in extremes:
texts.append(ax.text(gdf.loc[i, &amp;quot;shdi2013&amp;quot;], gdf.loc[i, &amp;quot;shdi2019&amp;quot;],
gdf.loc[i, &amp;quot;region_country&amp;quot;], fontsize=8, color=LIGHT_TEXT))
adjust_text(texts, ax=ax, arrowprops=dict(arrowstyle=&amp;quot;-&amp;quot;, color=LIGHT_TEXT,
alpha=0.5, lw=0.5))
plt.savefig(&amp;quot;esda2_scatter_hdi.png&amp;quot;, dpi=300, bbox_inches=&amp;quot;tight&amp;quot;)
plt.show()
&lt;/code>&lt;/pre>
&lt;p>&lt;img src="esda2_scatter_hdi.png" alt="Scatter plot of SHDI 2013 vs SHDI 2019 with 45-degree reference line and labeled extreme regions.">&lt;/p>
&lt;p>Of 153 regions, &lt;strong>126 improved&lt;/strong> their SHDI between 2013 and 2019, while &lt;strong>27 declined&lt;/strong>. The labels identify key cases: at the top, &lt;strong>C. Buenos Aires (ARG)&lt;/strong> and &lt;strong>R. Metropolitana (CHL)&lt;/strong> lead with SHDI above 0.88. At the bottom, &lt;strong>Potaro-Siparuni (GUY)&lt;/strong> and &lt;strong>Barima-Waini (GUY)&lt;/strong> remain the least developed. The biggest decliners &amp;mdash; &lt;strong>Federal Dist. (VEN)&lt;/strong>, &lt;strong>Carabobo (VEN)&lt;/strong>, and &lt;strong>Aragua (VEN)&lt;/strong> &amp;mdash; are all Venezuelan states, falling well below the 45-degree line. The biggest improvers &amp;mdash; &lt;strong>Meta (COL)&lt;/strong>, &lt;strong>Vichada (COL)&lt;/strong>, and &lt;strong>Brokopondo-Sipaliwini (SUR)&lt;/strong> &amp;mdash; rose above the line, with gains up to +0.045 points.&lt;/p>
&lt;h3 id="52-component-scatter-plots">5.2 Component scatter plots&lt;/h3>
&lt;p>The SHDI is a composite of three sub-indices: Health, Education, and Income. Breaking down the change by component reveals which dimensions drove the aggregate patterns.&lt;/p>
&lt;pre>&lt;code class="language-python">fig, axes = plt.subplots(1, 3, figsize=(18, 5.5))
components = [
(&amp;quot;healthindex2013&amp;quot;, &amp;quot;healthindex2019&amp;quot;, &amp;quot;Health Index&amp;quot;),
(&amp;quot;edindex2013&amp;quot;, &amp;quot;edindex2019&amp;quot;, &amp;quot;Education Index&amp;quot;),
(&amp;quot;incindex2013&amp;quot;, &amp;quot;incindex2019&amp;quot;, &amp;quot;Income Index&amp;quot;),
]
for ax, (col13, col19, label) in zip(axes, components):
ax.scatter(gdf[col13], gdf[col19],
color=STEEL_BLUE, edgecolors=DARK_NAVY, s=40, alpha=0.7, zorder=3)
lims = [min(gdf[col13].min(), gdf[col19].min()) - 0.02,
max(gdf[col13].max(), gdf[col19].max()) + 0.02]
ax.plot(lims, lims, color=WARM_ORANGE, linewidth=1.5, linestyle=&amp;quot;--&amp;quot;, zorder=2)
ax.set_xlabel(f&amp;quot;{label} 2013&amp;quot;)
ax.set_ylabel(f&amp;quot;{label} 2019&amp;quot;)
ax.set_title(label)
# Label extreme regions per component
comp_residual = gdf[col19] - gdf[col13]
comp_extremes = set()
comp_extremes.update(comp_residual.nlargest(2).index.tolist())
comp_extremes.update(comp_residual.nsmallest(2).index.tolist())
texts = []
for i in comp_extremes:
texts.append(ax.text(gdf.loc[i, col13], gdf.loc[i, col19],
gdf.loc[i, &amp;quot;region_country&amp;quot;], fontsize=7, color=LIGHT_TEXT))
adjust_text(texts, ax=ax, arrowprops=dict(arrowstyle=&amp;quot;-&amp;quot;, color=LIGHT_TEXT,
alpha=0.5, lw=0.5))
fig.suptitle(&amp;quot;HDI components: 2013 vs 2019&amp;quot;, fontsize=14, y=1.02)
plt.tight_layout()
plt.savefig(&amp;quot;esda2_scatter_components.png&amp;quot;, dpi=300, bbox_inches=&amp;quot;tight&amp;quot;)
plt.show()
&lt;/code>&lt;/pre>
&lt;p>&lt;img src="esda2_scatter_components.png" alt="Three-panel scatter plot comparing Health, Education, and Income indices between 2013 and 2019.">&lt;/p>
&lt;p>The three components tell very different stories. Health and Education improved almost universally &amp;mdash; the vast majority of points lie above the 45-degree line. Income, however, tells a starkly different story: &lt;strong>71 of 153 regions (46.4%) experienced a decline&lt;/strong> in their income index between 2013 and 2019. This mixed signal &amp;mdash; education and health gains partially offset by income losses &amp;mdash; explains why the aggregate SHDI improvement was so modest (+0.005 on average). The income panel also shows wider scatter, indicating greater heterogeneity in economic trajectories across the continent.&lt;/p>
&lt;h2 id="6-choropleth-maps">6. Choropleth maps&lt;/h2>
&lt;h3 id="61-hdi-levels-across-south-america">6.1 HDI levels across South America&lt;/h3>
&lt;p>The scatter plots tell us &lt;em>what&lt;/em> changed, but not &lt;em>where&lt;/em>. Choropleth maps add the geographic dimension by coloring each region according to its SHDI value. To make the two years directly comparable, we use &lt;a href="https://pysal.org/mapclassify/generated/mapclassify.FisherJenks.html" target="_blank" rel="noopener">Fisher-Jenks natural breaks&lt;/a> computed from 2013 and held constant for 2019. Fisher-Jenks is a classification method that finds natural groupings in data by minimizing within-class variance &amp;mdash; it places break points where the data naturally separates into clusters. This way, a color change between maps reflects a genuine shift in development class, not a shifting classification scheme. The legend shows the number of regions in each class, making it easy to see how the distribution shifted.&lt;/p>
&lt;pre>&lt;code class="language-python">import mapclassify
from matplotlib.patches import Patch
# Fisher-Jenks breaks from 2013 (5 classes)
fj = mapclassify.FisherJenks(gdf[&amp;quot;shdi2013&amp;quot;].values, k=5)
breaks = fj.bins.tolist()
# Extend upper break to cover 2019 max
max_val = max(gdf[&amp;quot;shdi2013&amp;quot;].max(), gdf[&amp;quot;shdi2019&amp;quot;].max())
if max_val &amp;gt; breaks[-1]:
breaks[-1] = float(round(max_val + 0.001, 3))
# Apply same breaks to 2019
fj_2019 = mapclassify.UserDefined(gdf[&amp;quot;shdi2019&amp;quot;].values, bins=breaks)
# Class transitions
classes_2013 = fj.yb
classes_2019 = fj_2019.yb
improved = (classes_2019 &amp;gt; classes_2013).sum()
stayed = (classes_2019 == classes_2013).sum()
declined = (classes_2019 &amp;lt; classes_2013).sum()
print(f&amp;quot;Breaks (from 2013): {[round(b, 3) for b in breaks]}&amp;quot;)
print(f&amp;quot; Improved (moved up): {improved}&amp;quot;)
print(f&amp;quot; Stayed same: {stayed}&amp;quot;)
print(f&amp;quot; Declined (moved down): {declined}&amp;quot;)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Breaks (from 2013): [0.622, 0.693, 0.734, 0.789, 0.884]
Improved (moved up): 43
Stayed same: 86
Declined (moved down): 24
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-python"># Class labels
class_labels = []
lower = round(gdf[&amp;quot;shdi2013&amp;quot;].min(), 2)
for b in breaks:
class_labels.append(f&amp;quot;{lower:.2f} – {b:.2f}&amp;quot;)
lower = round(b, 2)
fig, axes = plt.subplots(1, 2, figsize=(16, 12))
cmap = plt.cm.coolwarm
norm = plt.Normalize(vmin=0, vmax=len(breaks) - 1)
for ax, year_col, title, year_fj in [
(axes[0], &amp;quot;shdi2013&amp;quot;, &amp;quot;SHDI 2013&amp;quot;, fj),
(axes[1], &amp;quot;shdi2019&amp;quot;, &amp;quot;SHDI 2019&amp;quot;, fj_2019),
]:
colors = [cmap(norm(c)) for c in year_fj.yb]
gdf.plot(ax=ax, color=colors, edgecolor=GRID_LINE, linewidth=0.3)
ax.set_title(title, fontsize=14, pad=10)
ax.set_axis_off()
# Legend with region counts per class
counts = np.bincount(year_fj.yb, minlength=len(breaks))
handles = [Patch(facecolor=cmap(norm(i)), edgecolor=GRID_LINE,
label=f&amp;quot;{cl} (n={c})&amp;quot;)
for i, (cl, c) in enumerate(zip(class_labels, counts))]
ax.legend(handles=handles, title=&amp;quot;SHDI Class&amp;quot;, loc=&amp;quot;lower right&amp;quot;,
fontsize=10, title_fontsize=11)
# Label extreme regions on both maps
map_extremes = gdf[&amp;quot;shdi2019&amp;quot;].nlargest(3).index.tolist() + \
gdf[&amp;quot;shdi2019&amp;quot;].nsmallest(3).index.tolist()
for ax_map in axes:
texts = []
for i in map_extremes:
centroid = gdf.geometry.iloc[i].centroid
texts.append(ax_map.text(centroid.x, centroid.y,
gdf.loc[i, &amp;quot;region_country&amp;quot;],
fontsize=7, color=WHITE_TEXT, weight=&amp;quot;bold&amp;quot;))
adjust_text(texts, ax=ax_map, arrowprops=dict(arrowstyle=&amp;quot;-|&amp;gt;&amp;quot;,
color=LIGHT_TEXT, alpha=0.9, lw=1.2, mutation_scale=8))
plt.savefig(&amp;quot;esda2_choropleth_hdi.png&amp;quot;, dpi=300, bbox_inches=&amp;quot;tight&amp;quot;)
plt.show()
&lt;/code>&lt;/pre>
&lt;p>&lt;img src="esda2_choropleth_hdi.png" alt="Side-by-side choropleth maps of SHDI in 2013 and 2019 with Fisher-Jenks classification, showing region counts per class.">&lt;/p>
&lt;p>The Fisher-Jenks classification reveals both persistence and change in South America&amp;rsquo;s development geography. Using the same 2013 breaks for both maps, &lt;strong>43 regions moved up&lt;/strong> at least one class between 2013 and 2019, &lt;strong>86 stayed&lt;/strong> in the same class, and &lt;strong>24 declined&lt;/strong>. The legend counts make the shifts visible: the lowest class shrank from n=6 to n=4, while the middle classes absorbed most of the movement. The Southern Cone and southern Brazil consistently occupy the highest class (red tones), while the Amazon basin, Guyana, and parts of Venezuela anchor the lowest class (blue tones). This visual clustering is precisely what spatial autocorrelation statistics will later quantify &amp;mdash; high values are surrounded by high values, and low values are surrounded by low values.&lt;/p>
&lt;h3 id="62-mapping-hdi-change">6.2 Mapping HDI change&lt;/h3>
&lt;p>A map of SHDI change (2019 minus 2013) reveals the geographic distribution of gains and losses, using a diverging color scale centered at zero.&lt;/p>
&lt;pre>&lt;code class="language-python">fig, ax = plt.subplots(1, 1, figsize=(10, 10))
abs_max = max(abs(gdf[&amp;quot;shdi_change&amp;quot;].min()), abs(gdf[&amp;quot;shdi_change&amp;quot;].max()))
gdf.plot(column=&amp;quot;shdi_change&amp;quot;, cmap=&amp;quot;RdYlGn&amp;quot;, ax=ax, legend=False,
edgecolor=DARK_NAVY, linewidth=0.3, vmin=-abs_max, vmax=abs_max)
ax.set_title(&amp;quot;Change in SHDI (2019 - 2013)&amp;quot;, fontsize=14, pad=10)
ax.set_axis_off()
# Label biggest gainers and losers
change_top = gdf[&amp;quot;shdi_change&amp;quot;].nlargest(3).index.tolist()
change_bot = gdf[&amp;quot;shdi_change&amp;quot;].nsmallest(3).index.tolist()
texts = []
for i in change_top + change_bot:
centroid = gdf.geometry.iloc[i].centroid
texts.append(ax.text(centroid.x, centroid.y, gdf.loc[i, &amp;quot;region&amp;quot;],
fontsize=7, color=WHITE_TEXT, weight=&amp;quot;bold&amp;quot;))
adjust_text(texts, ax=ax, arrowprops=dict(arrowstyle=&amp;quot;-|&amp;gt;&amp;quot;,
color=LIGHT_TEXT, alpha=0.9, lw=1.2,
mutation_scale=8))
sm = plt.cm.ScalarMappable(cmap=&amp;quot;RdYlGn&amp;quot;,
norm=plt.Normalize(vmin=-abs_max, vmax=abs_max))
cbar = fig.colorbar(sm, ax=ax, orientation=&amp;quot;horizontal&amp;quot;,
fraction=0.03, pad=0.02, aspect=40)
cbar.set_label(&amp;quot;SHDI change (2019 - 2013)&amp;quot;)
plt.savefig(&amp;quot;esda2_choropleth_change.png&amp;quot;, dpi=300, bbox_inches=&amp;quot;tight&amp;quot;)
plt.show()
&lt;/code>&lt;/pre>
&lt;p>&lt;img src="esda2_choropleth_change.png" alt="Choropleth map of SHDI change between 2013 and 2019, with diverging red-green color scale and labeled extremes.">&lt;/p>
&lt;p>The change map reveals that &lt;strong>development losses are geographically concentrated&lt;/strong>, not randomly scattered. The labels pinpoint the extremes: &lt;strong>Federal Dist. (VEN)&lt;/strong>, &lt;strong>Carabobo (VEN)&lt;/strong>, and &lt;strong>Aragua (VEN)&lt;/strong> show the deepest red (declines of up to -0.067 points), while &lt;strong>Vichada (COL)&lt;/strong>, &lt;strong>Meta (COL)&lt;/strong>, and &lt;strong>Brokopondo-Sipaliwini (SUR)&lt;/strong> show the brightest green (improvements of up to +0.045). The geographic concentration of gains and losses suggests that spatial proximity plays a role in development trajectories &amp;mdash; a hypothesis that we formalize in the next sections.&lt;/p>
&lt;h2 id="7-spatial-weights">7. Spatial weights&lt;/h2>
&lt;h3 id="71-what-is-a-spatial-weights-matrix">7.1 What is a spatial weights matrix?&lt;/h3>
&lt;p>To test for spatial clustering formally, we first need to define what &amp;ldquo;neighbor&amp;rdquo; means. A &lt;strong>spatial weights matrix&lt;/strong> $W$ is an $n \times n$ matrix where each entry $w_{ij}$ encodes the spatial relationship between regions $i$ and $j$. If two regions are neighbors, $w_{ij} &amp;gt; 0$; if not, $w_{ij} = 0$.&lt;/p>
&lt;p>The most common approach for polygon data is &lt;strong>contiguity-based weights&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Queen contiguity:&lt;/strong> Two regions are neighbors if they share any boundary point (even a single corner). Named after the queen in chess, which can move in any direction.&lt;/li>
&lt;li>&lt;strong>Rook contiguity:&lt;/strong> Two regions are neighbors only if they share an edge (not just a corner). More restrictive than Queen.&lt;/li>
&lt;/ul>
&lt;p>We use Queen contiguity because it captures the broadest definition of adjacency, which is appropriate for irregular administrative boundaries.&lt;/p>
&lt;h3 id="72-building-queen-contiguity-weights">7.2 Building Queen contiguity weights&lt;/h3>
&lt;p>PySAL&amp;rsquo;s &lt;a href="https://pysal.org/libpysal/generated/libpysal.weights.contiguity.Queen.html" target="_blank" rel="noopener">&lt;code>Queen.from_dataframe()&lt;/code>&lt;/a> builds the weights matrix directly from a GeoDataFrame. After construction, we &lt;strong>row-standardize&lt;/strong> the matrix so that each region&amp;rsquo;s neighbor weights sum to 1. This makes the spatial lag (the weighted average of neighbors' values) directly interpretable as the mean neighbor value.&lt;/p>
&lt;pre>&lt;code class="language-python">from libpysal.weights import Queen
W = Queen.from_dataframe(gdf)
W.transform = &amp;quot;r&amp;quot; # Row-standardize
print(f&amp;quot;Number of regions: {W.n}&amp;quot;)
print(f&amp;quot;Min neighbors: {W.min_neighbors}&amp;quot;)
print(f&amp;quot;Max neighbors: {W.max_neighbors}&amp;quot;)
print(f&amp;quot;Mean neighbors: {W.mean_neighbors:.2f}&amp;quot;)
print(f&amp;quot;Islands: {W.islands}&amp;quot;)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Number of regions: 153
Min neighbors: 0
Max neighbors: 11
Mean neighbors: 4.93
Islands: [87, 145]
&lt;/code>&lt;/pre>
&lt;p>The Queen contiguity matrix connects 153 regions with an average of 4.93 neighbors each (minimum 0, maximum 11). Two regions have &lt;strong>no neighbors&lt;/strong> (islands): &lt;strong>San Andres (COL)&lt;/strong> (index 87) and &lt;strong>Nueva Esparta (VEN)&lt;/strong> (index 145) &amp;mdash; both are island territories separated from the mainland by water. PySAL excludes these isolates from spatial autocorrelation calculations, as they have no defined spatial relationship with other regions. Row-standardization ensures that each region&amp;rsquo;s spatial lag is the simple average of its neighbors' values, regardless of how many neighbors it has.&lt;/p>
&lt;h3 id="73-visualizing-the-connectivity-structure">7.3 Visualizing the connectivity structure&lt;/h3>
&lt;p>The &lt;a href="https://splot.readthedocs.io/en/latest/generated/splot.libpysal.plot_spatial_weights.html" target="_blank" rel="noopener">&lt;code>plot_spatial_weights()&lt;/code>&lt;/a> function from splot overlays the weights network on the map, drawing lines between each region&amp;rsquo;s centroid and its neighbors' centroids.&lt;/p>
&lt;pre>&lt;code class="language-python">fig, ax = plt.subplots(figsize=(10, 10))
gdf.plot(ax=ax, facecolor=&amp;quot;none&amp;quot;, edgecolor=GRID_LINE, linewidth=0.5)
plot_spatial_weights(W, gdf, ax=ax)
ax.set_title(&amp;quot;Queen contiguity weights&amp;quot;, fontsize=14, pad=10)
ax.set_axis_off()
plt.savefig(&amp;quot;esda2_spatial_weights.png&amp;quot;, dpi=300, bbox_inches=&amp;quot;tight&amp;quot;)
plt.show()
&lt;/code>&lt;/pre>
&lt;p>&lt;img src="esda2_spatial_weights.png" alt="Map of South America with Queen contiguity network overlaid, showing lines connecting neighboring region centroids.">&lt;/p>
&lt;p>The network visualization shows the connectivity structure underlying all spatial statistics in this tutorial. Denser networks appear in areas with many small regions (e.g., southern Brazil, northern Argentina), while sparser connections appear in areas with large administrative units (e.g., the Amazon basin). The two island territories (San Andres and Nueva Esparta) appear as isolated dots with no connecting lines. This network is the foundation for computing spatial lags &amp;mdash; the weighted average of neighbors' values &amp;mdash; which is the building block of Moran&amp;rsquo;s I.&lt;/p>
&lt;h2 id="8-global-spatial-autocorrelation">8. Global spatial autocorrelation&lt;/h2>
&lt;h3 id="81-morans-i-concept-and-intuition">8.1 Moran&amp;rsquo;s I: concept and intuition&lt;/h3>
&lt;p>&lt;strong>Moran&amp;rsquo;s I&lt;/strong> is the most widely used measure of global spatial autocorrelation. It answers a simple question: &lt;strong>do similar values tend to cluster together more than expected by chance?&lt;/strong> Think of it like temperature on a weather map &amp;mdash; if it is hot in one city, nearby cities are likely hot too. Moran&amp;rsquo;s I measures how strongly this &amp;ldquo;neighbor similarity&amp;rdquo; holds for development levels across South American regions.&lt;/p>
&lt;p>The statistic is defined as:&lt;/p>
&lt;p>$$I = \frac{n}{\sum_{i} \sum_{j} w_{ij}} \cdot \frac{\sum_{i} \sum_{j} w_{ij} (x_i - \bar{x})(x_j - \bar{x})}{\sum_{i} (x_i - \bar{x})^2}$$&lt;/p>
&lt;p>where $n$ is the number of regions, $w_{ij}$ are the spatial weights, $x_i$ is the value at region $i$, and $\bar{x}$ is the overall mean. In plain language: Moran&amp;rsquo;s I compares the product of deviations from the mean for each pair of neighbors. If high-value regions tend to be next to high-value regions (and low next to low), these products are positive, and $I$ is positive.&lt;/p>
&lt;ul>
&lt;li>$I \approx +1$: strong positive spatial autocorrelation (clustering of similar values)&lt;/li>
&lt;li>$I \approx 0$: no spatial pattern (random arrangement)&lt;/li>
&lt;li>$I \approx -1$: strong negative spatial autocorrelation (checkerboard pattern)&lt;/li>
&lt;/ul>
&lt;p>The expected value under spatial randomness is $E(I) = -1/(n-1)$, which approaches zero for large $n$.&lt;/p>
&lt;h3 id="82-morans-i-for-hdi-2013-and-2019">8.2 Moran&amp;rsquo;s I for HDI (2013 and 2019)&lt;/h3>
&lt;p>We compute Moran&amp;rsquo;s I with 999 random permutations to generate a reference distribution and assess statistical significance. A &lt;strong>permutation test&lt;/strong> works by randomly shuffling all the SHDI values across the map 999 times &amp;mdash; like dealing cards to random seats. If the real Moran&amp;rsquo;s I is more extreme than almost all the shuffled values, we can be confident the spatial pattern is real, not coincidence.&lt;/p>
&lt;pre>&lt;code class="language-python">from esda.moran import Moran
moran_2013 = Moran(gdf[&amp;quot;shdi2013&amp;quot;], W, permutations=999)
moran_2019 = Moran(gdf[&amp;quot;shdi2019&amp;quot;], W, permutations=999)
print(f&amp;quot;SHDI 2013: I = {moran_2013.I:.4f}, p-value = {moran_2013.p_sim:.4f}, &amp;quot;
f&amp;quot;z-score = {moran_2013.z_sim:.4f}&amp;quot;)
print(f&amp;quot;SHDI 2019: I = {moran_2019.I:.4f}, p-value = {moran_2019.p_sim:.4f}, &amp;quot;
f&amp;quot;z-score = {moran_2019.z_sim:.4f}&amp;quot;)
print(f&amp;quot;Expected I (random): {moran_2013.EI:.4f}&amp;quot;)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">SHDI 2013: I = 0.5680, p-value = 0.0010, z-score = 10.7661
SHDI 2019: I = 0.6320, p-value = 0.0010, z-score = 11.9890
Expected I (random): -0.0066
&lt;/code>&lt;/pre>
&lt;p>Moran&amp;rsquo;s I for SHDI is &lt;strong>strongly positive and highly significant&lt;/strong> in both years. In 2013, $I = 0.5680$ (p = 0.001, z = 10.77), and in 2019, $I = 0.6320$ (p = 0.001, z = 11.99). Both values are far above the expected value under spatial randomness ($E(I) = -0.0066$), confirming that regions with similar development levels are spatially clustered. Notably, &lt;strong>spatial autocorrelation strengthened&lt;/strong> from 2013 to 2019 ($I$ increased from 0.568 to 0.632), suggesting that development clusters became more pronounced over the period &amp;mdash; the spatial divide deepened.&lt;/p>
&lt;h3 id="83-moran-scatter-plot">8.3 Moran scatter plot&lt;/h3>
&lt;p>The &lt;strong>Moran scatter plot&lt;/strong> visualizes the spatial relationship by plotting each region&amp;rsquo;s standardized value ($z_i$) against the spatial lag of its neighbors ($Wz_i$). The slope of the regression line through the scatter equals Moran&amp;rsquo;s I. The four quadrants identify the type of spatial association for each region:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>HH (top-right):&lt;/strong> High values surrounded by high neighbors&lt;/li>
&lt;li>&lt;strong>LL (bottom-left):&lt;/strong> Low values surrounded by low neighbors&lt;/li>
&lt;li>&lt;strong>LH (top-left):&lt;/strong> Low values surrounded by high neighbors (spatial outlier)&lt;/li>
&lt;li>&lt;strong>HL (bottom-right):&lt;/strong> High values surrounded by low neighbors (spatial outlier)&lt;/li>
&lt;/ul>
&lt;pre>&lt;code class="language-python">from scipy import stats as scipy_stats
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
for ax, moran_obj, year in [
(axes[0], moran_2013, &amp;quot;2013&amp;quot;),
(axes[1], moran_2019, &amp;quot;2019&amp;quot;),
]:
# Standardize values and compute spatial lag
y = gdf[f&amp;quot;shdi{year}&amp;quot;].values
z = (y - y.mean()) / y.std()
wz = lag_spatial(W, z)
ax.scatter(z, wz, color=STEEL_BLUE, s=35, alpha=0.7,
edgecolors=GRID_LINE, linewidths=0.3, zorder=3)
# Regression line (slope = Moran's I)
slope, intercept, _, _, _ = scipy_stats.linregress(z, wz)
x_range = np.array([z.min(), z.max()])
ax.plot(x_range, intercept + slope * x_range, color=WARM_ORANGE,
linewidth=1.5, zorder=2)
# Quadrant dividers at origin
ax.axhline(0, color=LIGHT_TEXT, linewidth=0.8, alpha=0.5, zorder=1)
ax.axvline(0, color=LIGHT_TEXT, linewidth=0.8, alpha=0.5, zorder=1)
# Quadrant labels
xlim, ylim = ax.get_xlim(), ax.get_ylim()
pad_x = (xlim[1] - xlim[0]) * 0.05
pad_y = (ylim[1] - ylim[0]) * 0.05
ax.text(xlim[1] - pad_x, ylim[1] - pad_y, &amp;quot;HH&amp;quot;, fontsize=13,
ha=&amp;quot;right&amp;quot;, va=&amp;quot;top&amp;quot;, color=LIGHT_TEXT, alpha=0.5)
ax.text(xlim[0] + pad_x, ylim[1] - pad_y, &amp;quot;LH&amp;quot;, fontsize=13,
ha=&amp;quot;left&amp;quot;, va=&amp;quot;top&amp;quot;, color=LIGHT_TEXT, alpha=0.5)
ax.text(xlim[0] + pad_x, ylim[0] + pad_y, &amp;quot;LL&amp;quot;, fontsize=13,
ha=&amp;quot;left&amp;quot;, va=&amp;quot;bottom&amp;quot;, color=LIGHT_TEXT, alpha=0.5)
ax.text(xlim[1] - pad_x, ylim[0] + pad_y, &amp;quot;HL&amp;quot;, fontsize=13,
ha=&amp;quot;right&amp;quot;, va=&amp;quot;bottom&amp;quot;, color=LIGHT_TEXT, alpha=0.5)
ax.set_xlabel(f&amp;quot;SHDI {year} (standardized)&amp;quot;)
ax.set_ylabel(f&amp;quot;Spatial lag of SHDI {year}&amp;quot;)
ax.set_title(f&amp;quot;({'a' if year == '2013' else 'b'}) Moran scatter plot &amp;quot;
f&amp;quot;— {year} (I = {moran_obj.I:.4f})&amp;quot;)
plt.tight_layout()
plt.savefig(&amp;quot;esda2_moran_global.png&amp;quot;, dpi=300, bbox_inches=&amp;quot;tight&amp;quot;)
plt.show()
&lt;/code>&lt;/pre>
&lt;p>&lt;img src="esda2_moran_global.png" alt="Two-panel Moran scatter plot for SHDI in 2013 and 2019.">&lt;/p>
&lt;p>Both Moran scatter plots show a clear positive slope, with the majority of regions falling in the &lt;strong>HH and LL quadrants&lt;/strong> (positive spatial autocorrelation). The steeper slope in the 2019 panel visually confirms the increase in Moran&amp;rsquo;s I from 0.5680 to 0.6320. Regions in the HH quadrant (top-right) represent the Southern Cone prosperity cluster, while regions in the LL quadrant (bottom-left) represent the Amazon/Guyana deprivation cluster. The relatively few points in the LH and HL quadrants are spatial outliers &amp;mdash; regions whose development level diverges sharply from their neighbors.&lt;/p>
&lt;h2 id="9-local-spatial-autocorrelation-lisa">9. Local spatial autocorrelation (LISA)&lt;/h2>
&lt;h3 id="91-from-global-to-local-why-lisa-matters">9.1 From global to local: why LISA matters&lt;/h3>
&lt;p>Global Moran&amp;rsquo;s I gives us &lt;strong>one number&lt;/strong> for the entire map, confirming that spatial clustering exists. But it does not tell us &lt;strong>where&lt;/strong> the clusters are located. &lt;strong>Local Indicators of Spatial Association (LISA)&lt;/strong> decompose the global statistic into a contribution from each individual region (&lt;a href="https://doi.org/10.1111/j.1538-4632.1995.tb00338.x" target="_blank" rel="noopener">Anselin, 1995&lt;/a>).&lt;/p>
&lt;p>The local Moran statistic for region $i$ is:&lt;/p>
&lt;p>$$I_i = z_i \sum_{j} w_{ij} z_j$$&lt;/p>
&lt;p>where $z_i = (x_i - \bar{x}) / s$ is the standardized value at region $i$ and $\sum_{j} w_{ij} z_j$ is its spatial lag (the weighted average of neighbors' standardized values). In plain language: each region&amp;rsquo;s local statistic is the product of its own deviation from the mean and the average deviation of its neighbors. In the code, $x_i$ corresponds to &lt;code>gdf[&amp;quot;shdi2019&amp;quot;]&lt;/code> and $w_{ij}$ to the row-standardized Queen weights &lt;code>W&lt;/code>.&lt;/p>
&lt;p>Each region receives a local Moran&amp;rsquo;s I statistic and is classified into one of four types based on its quadrant in the Moran scatter plot:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>HH (High-High):&lt;/strong> A high-value region surrounded by high-value neighbors &amp;mdash; a &amp;ldquo;hot spot&amp;rdquo; or prosperity cluster&lt;/li>
&lt;li>&lt;strong>LL (Low-Low):&lt;/strong> A low-value region surrounded by low-value neighbors &amp;mdash; a &amp;ldquo;cold spot&amp;rdquo; or deprivation trap&lt;/li>
&lt;li>&lt;strong>HL (High-Low):&lt;/strong> A high-value region surrounded by low-value neighbors &amp;mdash; a positive spatial outlier&lt;/li>
&lt;li>&lt;strong>LH (Low-High):&lt;/strong> A low-value region surrounded by high-value neighbors &amp;mdash; a negative spatial outlier&lt;/li>
&lt;/ul>
&lt;p>Statistical significance is assessed via permutation tests. Only regions with p-values below a chosen threshold (here, $p &amp;lt; 0.10$) are classified as belonging to a cluster.&lt;/p>
&lt;h3 id="92-lisa-for-hdi-2019">9.2 LISA for HDI 2019&lt;/h3>
&lt;p>We compute the local Moran&amp;rsquo;s I for SHDI in 2019 and visualize the results as a Moran scatter plot with significant regions colored by quadrant (left panel) and a cluster map (right panel).&lt;/p>
&lt;pre>&lt;code class="language-python">localMoran_2019 = Moran_Local(gdf[&amp;quot;shdi2019&amp;quot;], W, permutations=999, seed=12345)
wlag_2019 = lag_spatial(W, gdf[&amp;quot;shdi2019&amp;quot;].values)
sig_2019 = localMoran_2019.p_sim &amp;lt; 0.10
q_labels = {1: &amp;quot;HH&amp;quot;, 2: &amp;quot;LH&amp;quot;, 3: &amp;quot;LL&amp;quot;, 4: &amp;quot;HL&amp;quot;}
for q_val, q_name in q_labels.items():
count = ((localMoran_2019.q == q_val) &amp;amp; sig_2019).sum()
print(f&amp;quot; {q_name}: {count}&amp;quot;)
print(f&amp;quot; Not significant: {(~sig_2019).sum()}&amp;quot;)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text"> HH: 30
LH: 1
LL: 37
HL: 5
Not significant: 80
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-python">LISA_COLORS = {1: &amp;quot;#d7191c&amp;quot;, 2: &amp;quot;#89cff0&amp;quot;, 3: &amp;quot;#2c7bb6&amp;quot;, 4: &amp;quot;#fdae61&amp;quot;}
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(14, 6))
# (a) LISA scatter plot with colored quadrants
ax = axes[0]
slope, intercept, _, _, _ = scipy_stats.linregress(gdf[&amp;quot;shdi2019&amp;quot;].values, wlag_2019)
# Non-significant points (grey)
ns_mask = ~sig_2019
ax.scatter(gdf.loc[ns_mask, &amp;quot;shdi2019&amp;quot;], wlag_2019[ns_mask],
color=&amp;quot;#bababa&amp;quot;, s=30, alpha=0.4, edgecolors=GRID_LINE,
linewidths=0.3, label=&amp;quot;ns&amp;quot;, zorder=2)
# Significant points colored by quadrant
for q_val, q_name in q_labels.items():
mask = (localMoran_2019.q == q_val) &amp;amp; sig_2019
if mask.any():
ax.scatter(gdf.loc[mask, &amp;quot;shdi2019&amp;quot;], wlag_2019[mask],
color=LISA_COLORS[q_val], s=40, alpha=0.8,
edgecolors=GRID_LINE, linewidths=0.3,
label=q_name, zorder=3)
# Regression line
x_range = np.array([gdf[&amp;quot;shdi2019&amp;quot;].min(), gdf[&amp;quot;shdi2019&amp;quot;].max()])
ax.plot(x_range, intercept + slope * x_range, color=WARM_ORANGE,
linewidth=1.2, zorder=1)
# Crosshairs at mean
ax.axhline(wlag_2019.mean(), color=GRID_LINE, linewidth=0.8, linestyle=&amp;quot;--&amp;quot;, zorder=0)
ax.axvline(gdf[&amp;quot;shdi2019&amp;quot;].mean(), color=GRID_LINE, linewidth=0.8, linestyle=&amp;quot;--&amp;quot;, zorder=0)
ax.set_xlabel(&amp;quot;SHDI 2019&amp;quot;)
ax.set_ylabel(&amp;quot;Spatial lag of SHDI 2019&amp;quot;)
ax.set_title(f&amp;quot;(a) Moran scatter plot (I = {moran_2019.I:.4f})&amp;quot;)
# (b) LISA cluster map
lisa_cluster(localMoran_2019, gdf, p=0.10,
legend_kwds={&amp;quot;bbox_to_anchor&amp;quot;: (0.02, 0.90)}, ax=axes[1])
axes[1].set_facecolor(DARK_NAVY)
axes[1].set_title(&amp;quot;(b) LISA clusters (p &amp;lt; 0.10)&amp;quot;)
# Label extreme LISA regions on both panels
label_idx = []
hh_mask = (localMoran_2019.q == 1) &amp;amp; sig_2019
if hh_mask.any():
label_idx += gdf.loc[hh_mask, &amp;quot;shdi2019&amp;quot;].nlargest(3).index.tolist()
ll_mask = (localMoran_2019.q == 3) &amp;amp; sig_2019
if ll_mask.any():
label_idx += gdf.loc[ll_mask, &amp;quot;shdi2019&amp;quot;].nsmallest(3).index.tolist()
hl_mask = (localMoran_2019.q == 4) &amp;amp; sig_2019
if hl_mask.any():
label_idx.append(gdf.loc[hl_mask, &amp;quot;shdi2019&amp;quot;].idxmax())
lh_mask = (localMoran_2019.q == 2) &amp;amp; sig_2019
if lh_mask.any():
label_idx.append(gdf.loc[lh_mask, &amp;quot;shdi2019&amp;quot;].idxmin())
# Scatter labels
texts = [axes[0].text(gdf.loc[i, &amp;quot;shdi2019&amp;quot;], wlag_2019[i], gdf.loc[i, &amp;quot;region&amp;quot;],
fontsize=7, color=LIGHT_TEXT) for i in label_idx]
adjust_text(texts, ax=axes[0], arrowprops=dict(arrowstyle=&amp;quot;-&amp;quot;, color=LIGHT_TEXT,
alpha=0.5, lw=0.5))
# Map labels
texts = [axes[1].text(gdf.geometry.iloc[i].centroid.x, gdf.geometry.iloc[i].centroid.y,
gdf.loc[i, &amp;quot;region_country&amp;quot;], fontsize=7, color=WHITE_TEXT, weight=&amp;quot;bold&amp;quot;)
for i in label_idx]
adjust_text(texts, ax=axes[1], arrowprops=dict(arrowstyle=&amp;quot;-|&amp;gt;&amp;quot;, color=LIGHT_TEXT,
alpha=0.9, lw=1.2, mutation_scale=8))
plt.tight_layout()
plt.savefig(&amp;quot;esda2_lisa_2019.png&amp;quot;, dpi=300, bbox_inches=&amp;quot;tight&amp;quot;)
plt.show()
&lt;/code>&lt;/pre>
&lt;p>&lt;img src="esda2_lisa_2019.png" alt="Two-panel LISA analysis for SHDI 2019: Moran scatter plot with labeled extreme regions (left) and LISA cluster map (right).">&lt;/p>
&lt;p>At the 10% significance level, the 2019 LISA analysis identifies &lt;strong>30 HH regions&lt;/strong>, &lt;strong>37 LL regions&lt;/strong>, &lt;strong>5 HL outliers&lt;/strong>, &lt;strong>1 LH outlier&lt;/strong>, and &lt;strong>80 non-significant regions&lt;/strong>. The labels highlight the extremes of each cluster type. The &lt;strong>three highest HH regions&lt;/strong> &amp;mdash; R. Metropolitana (CHL, SHDI = 0.883), C. Buenos Aires (ARG, 0.882), and Antofagasta (CHL, 0.875) &amp;mdash; anchor the Southern Cone prosperity core. The &lt;strong>three lowest LL regions&lt;/strong> &amp;mdash; Potaro-Siparuni (GUY, 0.558), Barima-Waini (GUY, 0.592), and Upper Takutu-Essequibo (GUY, 0.601) &amp;mdash; anchor the deprivation cluster in northern South America. &lt;strong>San Andres (COL)&lt;/strong> (0.789) appears as an HL outlier: a high-development island surrounded by lower-development mainland neighbors. &lt;strong>Potosi (BOL)&lt;/strong> (0.631) is the lone LH outlier: a lagging region surrounded by better-performing neighbors.&lt;/p>
&lt;h3 id="93-lisa-for-hdi-2013">9.3 LISA for HDI 2013&lt;/h3>
&lt;p>Repeating the analysis for 2013 allows us to compare how clusters have evolved over time.&lt;/p>
&lt;pre>&lt;code class="language-python">localMoran_2013 = Moran_Local(gdf[&amp;quot;shdi2013&amp;quot;], W, permutations=999, seed=12345)
wlag_2013 = lag_spatial(W, gdf[&amp;quot;shdi2013&amp;quot;].values)
sig_2013 = localMoran_2013.p_sim &amp;lt; 0.10
for q_val, q_name in q_labels.items():
count = ((localMoran_2013.q == q_val) &amp;amp; sig_2013).sum()
print(f&amp;quot; {q_name}: {count}&amp;quot;)
print(f&amp;quot; Not significant: {(~sig_2013).sum()}&amp;quot;)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text"> HH: 31
LH: 0
LL: 29
HL: 5
Not significant: 88
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-python">fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(14, 6))
# (a) LISA scatter plot with colored quadrants
ax = axes[0]
slope, intercept, _, _, _ = scipy_stats.linregress(gdf[&amp;quot;shdi2013&amp;quot;].values, wlag_2013)
ns_mask = ~sig_2013
ax.scatter(gdf.loc[ns_mask, &amp;quot;shdi2013&amp;quot;], wlag_2013[ns_mask],
color=&amp;quot;#bababa&amp;quot;, s=30, alpha=0.4, edgecolors=GRID_LINE,
linewidths=0.3, label=&amp;quot;ns&amp;quot;, zorder=2)
for q_val, q_name in q_labels.items():
mask = (localMoran_2013.q == q_val) &amp;amp; sig_2013
if mask.any():
ax.scatter(gdf.loc[mask, &amp;quot;shdi2013&amp;quot;], wlag_2013[mask],
color=LISA_COLORS[q_val], s=40, alpha=0.8,
edgecolors=GRID_LINE, linewidths=0.3,
label=q_name, zorder=3)
x_range = np.array([gdf[&amp;quot;shdi2013&amp;quot;].min(), gdf[&amp;quot;shdi2013&amp;quot;].max()])
ax.plot(x_range, intercept + slope * x_range, color=WARM_ORANGE,
linewidth=1.2, zorder=1)
ax.axhline(wlag_2013.mean(), color=GRID_LINE, linewidth=0.8, linestyle=&amp;quot;--&amp;quot;, zorder=0)
ax.axvline(gdf[&amp;quot;shdi2013&amp;quot;].mean(), color=GRID_LINE, linewidth=0.8, linestyle=&amp;quot;--&amp;quot;, zorder=0)
ax.set_xlabel(&amp;quot;SHDI 2013&amp;quot;)
ax.set_ylabel(&amp;quot;Spatial lag of SHDI 2013&amp;quot;)
ax.set_title(f&amp;quot;(a) Moran scatter plot (I = {moran_2013.I:.4f})&amp;quot;)
# (b) LISA cluster map
lisa_cluster(localMoran_2013, gdf, p=0.10,
legend_kwds={&amp;quot;bbox_to_anchor&amp;quot;: (0.02, 0.90)}, ax=axes[1])
axes[1].set_facecolor(DARK_NAVY)
axes[1].set_title(&amp;quot;(b) LISA clusters (p &amp;lt; 0.10)&amp;quot;)
# Label extreme LISA regions (3 HH, 3 LL, 1 HL; no LH in 2013)
label_idx = []
hh_mask = (localMoran_2013.q == 1) &amp;amp; sig_2013
if hh_mask.any():
label_idx += gdf.loc[hh_mask, &amp;quot;shdi2013&amp;quot;].nlargest(3).index.tolist()
ll_mask = (localMoran_2013.q == 3) &amp;amp; sig_2013
if ll_mask.any():
label_idx += gdf.loc[ll_mask, &amp;quot;shdi2013&amp;quot;].nsmallest(3).index.tolist()
hl_mask = (localMoran_2013.q == 4) &amp;amp; sig_2013
if hl_mask.any():
label_idx.append(gdf.loc[hl_mask, &amp;quot;shdi2013&amp;quot;].idxmax())
lh_mask = (localMoran_2013.q == 2) &amp;amp; sig_2013
if lh_mask.any():
label_idx.append(gdf.loc[lh_mask, &amp;quot;shdi2013&amp;quot;].idxmin())
texts = [axes[0].text(gdf.loc[i, &amp;quot;shdi2013&amp;quot;], wlag_2013[i], gdf.loc[i, &amp;quot;region&amp;quot;],
fontsize=7, color=LIGHT_TEXT) for i in label_idx]
adjust_text(texts, ax=axes[0], arrowprops=dict(arrowstyle=&amp;quot;-&amp;quot;, color=LIGHT_TEXT,
alpha=0.5, lw=0.5))
texts = [axes[1].text(gdf.geometry.iloc[i].centroid.x, gdf.geometry.iloc[i].centroid.y,
gdf.loc[i, &amp;quot;region_country&amp;quot;], fontsize=7, color=WHITE_TEXT, weight=&amp;quot;bold&amp;quot;)
for i in label_idx]
adjust_text(texts, ax=axes[1], arrowprops=dict(arrowstyle=&amp;quot;-|&amp;gt;&amp;quot;, color=LIGHT_TEXT,
alpha=0.9, lw=1.2, mutation_scale=8))
plt.tight_layout()
plt.savefig(&amp;quot;esda2_lisa_2013.png&amp;quot;, dpi=300, bbox_inches=&amp;quot;tight&amp;quot;)
plt.show()
&lt;/code>&lt;/pre>
&lt;p>&lt;img src="esda2_lisa_2013.png" alt="Two-panel LISA analysis for SHDI 2013: Moran scatter plot with labeled regions (left) and LISA cluster map (right).">&lt;/p>
&lt;p>The 2013 LISA analysis identifies &lt;strong>31 HH regions&lt;/strong>, &lt;strong>29 LL regions&lt;/strong>, &lt;strong>5 HL outliers&lt;/strong>, &lt;strong>0 LH outliers&lt;/strong>, and &lt;strong>88 non-significant regions&lt;/strong>. The same three HH leaders appear: C. Buenos Aires (ARG, 0.878), R. Metropolitana (CHL, 0.857), and Antofagasta (CHL, 0.852). The same three LL anchors persist: Potaro-Siparuni (GUY, 0.554), Barima-Waini (GUY, 0.577), and Upper Takutu-Essequibo (GUY, 0.585). The HL outlier in 2013 is &lt;strong>Nueva Esparta (VEN)&lt;/strong> (0.797) &amp;mdash; an island state that performed well despite its mainland neighbors. Comparing with 2019, the most striking change is the &lt;strong>expansion of the LL cluster&lt;/strong> from 29 to 37 regions, while the HH cluster remained roughly stable (31 to 30). This asymmetric evolution is consistent with the income decline concentrated in Venezuela, which pulled more regions into the deprivation cluster.&lt;/p>
&lt;h3 id="94-comparing-lisa-clusters-across-time">9.4 Comparing LISA clusters across time&lt;/h3>
&lt;p>A transition table reveals how regions moved between LISA categories from 2013 to 2019.&lt;/p>
&lt;pre>&lt;code class="language-python">sig_2013 = localMoran_2013.p_sim &amp;lt; 0.10
sig_2019 = localMoran_2019.p_sim &amp;lt; 0.10
q_labels = {1: &amp;quot;HH&amp;quot;, 2: &amp;quot;LH&amp;quot;, 3: &amp;quot;LL&amp;quot;, 4: &amp;quot;HL&amp;quot;}
labels_2013 = [&amp;quot;ns&amp;quot; if not sig_2013[i] else q_labels[localMoran_2013.q[i]]
for i in range(len(gdf))]
labels_2019 = [&amp;quot;ns&amp;quot; if not sig_2019[i] else q_labels[localMoran_2019.q[i]]
for i in range(len(gdf))]
transition_df = pd.crosstab(
pd.Series(labels_2013, name=&amp;quot;2013&amp;quot;),
pd.Series(labels_2019, name=&amp;quot;2019&amp;quot;)
)
print(transition_df.to_string())
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">2019 HH HL LH LL ns
2013
HH 27 0 0 0 4
HL 0 2 0 2 1
LL 0 2 0 18 9
ns 3 1 1 17 66
&lt;/code>&lt;/pre>
&lt;p>The transition table reveals strong &lt;strong>cluster persistence&lt;/strong>. Of the 31 regions in the HH cluster in 2013, &lt;strong>27 remained HH&lt;/strong> in 2019 (87% persistence), while only 4 became non-significant. Of the 29 LL regions in 2013, &lt;strong>18 remained LL&lt;/strong> (62% persistence). The most notable transition is from non-significant to LL: &lt;strong>17 regions&lt;/strong> that were not part of any significant cluster in 2013 joined the low-development cluster by 2019. This expansion of the LL cluster, combined with the high persistence of HH, paints a picture of entrenched spatial inequality &amp;mdash; prosperity clusters are stable, and deprivation clusters are growing.&lt;/p>
&lt;h2 id="10-space-time-dynamics">10. Space-time dynamics&lt;/h2>
&lt;h3 id="101-directional-moran-scatter-plot">10.1 Directional Moran scatter plot&lt;/h3>
&lt;p>The LISA transition table tracks changes in statistical significance, but regions can also move &lt;em>within&lt;/em> the Moran scatter plot even without crossing significance thresholds. A &lt;strong>directional Moran scatter plot&lt;/strong> shows the movement vector for each region from its 2013 position to its 2019 position in the (standardized value, spatial lag) space. The arrows reveal the direction and magnitude of change in both a region&amp;rsquo;s own development and its neighbors' development.&lt;/p>
&lt;p>To make the two periods comparable, we standardize both years using the &lt;strong>pooled mean and standard deviation&lt;/strong> (across both periods combined), following the same logic as the &lt;a href="https://carlos-mendez.org/post/python_pca2/">Pooled PCA tutorial&lt;/a>.&lt;/p>
&lt;pre>&lt;code class="language-python">from libpysal.weights import lag_spatial
# Standardize using pooled parameters
mean_all = np.mean(np.concatenate([gdf[&amp;quot;shdi2013&amp;quot;].values, gdf[&amp;quot;shdi2019&amp;quot;].values]))
std_all = np.std(np.concatenate([gdf[&amp;quot;shdi2013&amp;quot;].values, gdf[&amp;quot;shdi2019&amp;quot;].values]))
z_2013 = (gdf[&amp;quot;shdi2013&amp;quot;].values - mean_all) / std_all
z_2019 = (gdf[&amp;quot;shdi2019&amp;quot;].values - mean_all) / std_all
# Spatial lags
wz_2013 = lag_spatial(W, z_2013)
wz_2019 = lag_spatial(W, z_2019)
fig, ax = plt.subplots(figsize=(9, 8))
for i in range(len(gdf)):
ax.annotate(&amp;quot;&amp;quot;, xy=(z_2019[i], wz_2019[i]),
xytext=(z_2013[i], wz_2013[i]),
arrowprops=dict(arrowstyle=&amp;quot;-&amp;gt;&amp;quot;, color=STEEL_BLUE,
alpha=0.5, lw=0.8))
ax.scatter(z_2013, wz_2013, color=WARM_ORANGE, s=20, alpha=0.6,
label=&amp;quot;2013&amp;quot;, zorder=4)
ax.scatter(z_2019, wz_2019, color=TEAL, s=20, alpha=0.6,
label=&amp;quot;2019&amp;quot;, zorder=4)
ax.axhline(0, color=GRID_LINE, linewidth=1)
ax.axvline(0, color=GRID_LINE, linewidth=1)
ax.set_xlabel(&amp;quot;SHDI (standardized)&amp;quot;)
ax.set_ylabel(&amp;quot;Spatial lag of SHDI&amp;quot;)
ax.set_title(&amp;quot;Directional Moran scatter plot: movements from 2013 to 2019&amp;quot;)
ax.legend()
plt.savefig(&amp;quot;esda2_directional_moran.png&amp;quot;, dpi=300, bbox_inches=&amp;quot;tight&amp;quot;)
plt.show()
&lt;/code>&lt;/pre>
&lt;p>&lt;img src="esda2_directional_moran.png" alt="Directional Moran scatter plot showing movement vectors from 2013 to 2019 positions for each region.">&lt;/p>
&lt;pre>&lt;code class="language-python"># Classify quadrant transitions
q_2013 = np.where((z_2013 &amp;gt;= 0) &amp;amp; (wz_2013 &amp;gt;= 0), &amp;quot;HH&amp;quot;,
np.where((z_2013 &amp;lt; 0) &amp;amp; (wz_2013 &amp;gt;= 0), &amp;quot;LH&amp;quot;,
np.where((z_2013 &amp;lt; 0) &amp;amp; (wz_2013 &amp;lt; 0), &amp;quot;LL&amp;quot;, &amp;quot;HL&amp;quot;)))
q_2019 = np.where((z_2019 &amp;gt;= 0) &amp;amp; (wz_2019 &amp;gt;= 0), &amp;quot;HH&amp;quot;,
np.where((z_2019 &amp;lt; 0) &amp;amp; (wz_2019 &amp;gt;= 0), &amp;quot;LH&amp;quot;,
np.where((z_2019 &amp;lt; 0) &amp;amp; (wz_2019 &amp;lt; 0), &amp;quot;LL&amp;quot;, &amp;quot;HL&amp;quot;)))
transition_moran = pd.crosstab(
pd.Series(q_2013, name=&amp;quot;2013&amp;quot;),
pd.Series(q_2019, name=&amp;quot;2019&amp;quot;)
)
print(transition_moran.to_string())
stayed = (q_2013 == q_2019).sum()
moved = (q_2013 != q_2019).sum()
print(f&amp;quot;\nStayed in same quadrant: {stayed} ({stayed/len(gdf)*100:.1f}%)&amp;quot;)
print(f&amp;quot;Moved to different quadrant: {moved} ({moved/len(gdf)*100:.1f}%)&amp;quot;)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">2019 HH HL LH LL
2013
HH 41 1 2 10
HL 9 6 0 5
LH 0 0 2 3
LL 7 10 11 46
Stayed in same quadrant: 95 (62.1%)
Moved to different quadrant: 58 (37.9%)
&lt;/code>&lt;/pre>
&lt;p>The directional Moran scatter plot reveals the space-time dynamics of South American development. &lt;strong>95 regions (62.1%)&lt;/strong> remained in the same Moran scatter plot quadrant between 2013 and 2019, while &lt;strong>58 (37.9%)&lt;/strong> crossed quadrant boundaries. The most stable quadrants are HH (41 of 54 stayed, 76%) and LL (46 of 74 stayed, 62%), confirming that both prosperity and deprivation clusters are persistent. The most common transitions are LL to LH (11 regions) and HL to HH (9 regions), suggesting some upward mobility at the boundary of the prosperity cluster. However, the 10 HH-to-LL transitions highlight that the Venezuelan crisis pulled previously well-performing regions into the low-development quadrant &amp;mdash; a dramatic downward trajectory that affected both the regions themselves and their neighbors.&lt;/p>
&lt;h3 id="102-country-focus-venezuela-vs-bolivia">10.2 Country focus: Venezuela vs Bolivia&lt;/h3>
&lt;p>Venezuela and Bolivia offer a stark contrast in subnational development trajectories. In 2013, Venezuela&amp;rsquo;s regions were spread across the upper half of the Moran scatter plot &amp;mdash; 13 of 24 regions sat in the HH quadrant, reflecting relatively high development levels and high-development neighbors. Bolivia&amp;rsquo;s 9 regions, by contrast, were concentrated in the lower-left corner (8 in LL, 1 in LH). By 2019, these two countries had moved in opposite directions. We isolate them in the directional Moran scatter plot to compare their movement vectors.&lt;/p>
&lt;pre>&lt;code class="language-python"># Filter Venezuela and Bolivia regions
ven_mask = gdf[&amp;quot;country&amp;quot;] == &amp;quot;Venezuela&amp;quot;
bol_mask = gdf[&amp;quot;country&amp;quot;] == &amp;quot;Bolivia&amp;quot;
# Shared axis limits (from the full dataset, for comparability)
all_z = np.concatenate([z_2013, z_2019])
all_wz = np.concatenate([wz_2013, wz_2019])
pad = 0.3
shared_xlim = (all_z.min() - pad, all_z.max() + pad)
shared_ylim = (all_wz.min() - pad, all_wz.max() + pad)
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(16, 7))
for ax, mask, title in [
(axes[0], bol_mask, &amp;quot;(a) Bolivia&amp;quot;),
(axes[1], ven_mask, &amp;quot;(b) Venezuela&amp;quot;),
]:
# Background: all regions (grey, faded)
for i in range(len(gdf)):
ax.annotate(&amp;quot;&amp;quot;, xy=(z_2019[i], wz_2019[i]),
xytext=(z_2013[i], wz_2013[i]),
arrowprops=dict(arrowstyle=&amp;quot;-&amp;gt;&amp;quot;, color=GRID_LINE,
alpha=0.15, lw=0.5))
ax.scatter(z_2013, wz_2013, color=GRID_LINE, s=10, alpha=0.15, zorder=2)
ax.scatter(z_2019, wz_2019, color=GRID_LINE, s=10, alpha=0.15, zorder=2)
# Highlighted country
for i in gdf.index[mask]:
ax.annotate(&amp;quot;&amp;quot;, xy=(z_2019[i], wz_2019[i]),
xytext=(z_2013[i], wz_2013[i]),
arrowprops=dict(arrowstyle=&amp;quot;-&amp;gt;&amp;quot;, color=STEEL_BLUE,
alpha=0.7, lw=1.0))
ax.scatter(z_2013[mask], wz_2013[mask], color=WARM_ORANGE, s=30,
alpha=0.8, edgecolors=GRID_LINE, linewidths=0.3,
label=&amp;quot;2013&amp;quot;, zorder=5)
ax.scatter(z_2019[mask], wz_2019[mask], color=TEAL, s=30,
alpha=0.8, edgecolors=GRID_LINE, linewidths=0.3,
label=&amp;quot;2019&amp;quot;, zorder=5)
# Labels at 2019 positions
texts = []
for i in gdf.index[mask]:
texts.append(ax.text(z_2019[i], wz_2019[i], gdf.loc[i, &amp;quot;region&amp;quot;],
fontsize=7, color=LIGHT_TEXT))
adjust_text(texts, ax=ax, arrowprops=dict(arrowstyle=&amp;quot;-&amp;quot;, color=LIGHT_TEXT,
alpha=0.5, lw=0.5))
# Quadrant lines and labels
ax.axhline(0, color=GRID_LINE, linewidth=1, zorder=1)
ax.axvline(0, color=GRID_LINE, linewidth=1, zorder=1)
ax.set_xlim(shared_xlim)
ax.set_ylim(shared_ylim)
ox = (shared_xlim[1] - shared_xlim[0]) * 0.05
oy = (shared_ylim[1] - shared_ylim[0]) * 0.05
for lbl, ha, va, x, y in [
(&amp;quot;HH&amp;quot;, &amp;quot;right&amp;quot;, &amp;quot;top&amp;quot;, shared_xlim[1] - ox, shared_ylim[1] - oy),
(&amp;quot;LH&amp;quot;, &amp;quot;left&amp;quot;, &amp;quot;top&amp;quot;, shared_xlim[0] + ox, shared_ylim[1] - oy),
(&amp;quot;LL&amp;quot;, &amp;quot;left&amp;quot;, &amp;quot;bottom&amp;quot;, shared_xlim[0] + ox, shared_ylim[0] + oy),
(&amp;quot;HL&amp;quot;, &amp;quot;right&amp;quot;, &amp;quot;bottom&amp;quot;, shared_xlim[1] - ox, shared_ylim[0] + oy),
]:
ax.text(x, y, lbl, fontsize=14, ha=ha, va=va,
color=LIGHT_TEXT, alpha=0.6)
ax.set_xlabel(&amp;quot;SHDI (standardized)&amp;quot;)
ax.set_ylabel(&amp;quot;Spatial lag of SHDI&amp;quot;)
ax.set_title(title)
ax.legend(fontsize=8)
plt.tight_layout()
plt.savefig(&amp;quot;esda2_directional_ven_bol.png&amp;quot;, dpi=300, bbox_inches=&amp;quot;tight&amp;quot;)
plt.show()
&lt;/code>&lt;/pre>
&lt;p>&lt;img src="esda2_directional_ven_bol.png" alt="Side-by-side directional Moran scatter plots for Bolivia (left) and Venezuela (right), showing movement vectors from 2013 to 2019.">&lt;/p>
&lt;pre>&lt;code class="language-python"># Summary statistics for Venezuela and Bolivia
for country, mask in [(&amp;quot;Venezuela&amp;quot;, ven_mask), (&amp;quot;Bolivia&amp;quot;, bol_mask)]:
n = mask.sum()
mean_change = gdf.loc[mask, &amp;quot;shdi_change&amp;quot;].mean()
min_change = gdf.loc[mask, &amp;quot;shdi_change&amp;quot;].min()
max_change = gdf.loc[mask, &amp;quot;shdi_change&amp;quot;].max()
# Quadrant transitions
q13 = q_2013[mask]
q19 = q_2019[mask]
stayed = (q13 == q19).sum()
moved = (q13 != q19).sum()
print(f&amp;quot;\n{country} ({n} regions):&amp;quot;)
print(f&amp;quot; Mean SHDI change: {mean_change:+.4f}&amp;quot;)
print(f&amp;quot; Range: [{min_change:+.4f}, {max_change:+.4f}]&amp;quot;)
print(f&amp;quot; Quadrant stability: {stayed} stayed, {moved} moved&amp;quot;)
print(f&amp;quot; 2013 quadrants: {', '.join(f'{q}={c}' for q, c in zip(*np.unique(q13, return_counts=True)))}&amp;quot;)
print(f&amp;quot; 2019 quadrants: {', '.join(f'{q}={c}' for q, c in zip(*np.unique(q19, return_counts=True)))}&amp;quot;)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Venezuela (24 regions):
Mean SHDI change: -0.0653
Range: [-0.0670, -0.0640]
Quadrant stability: 3 stayed, 21 moved
2013 quadrants: HH=13, HL=5, LH=3, LL=3
2019 quadrants: HL=1, LH=2, LL=21
Bolivia (9 regions):
Mean SHDI change: +0.0333
Range: [+0.0300, +0.0350]
Quadrant stability: 7 stayed, 2 moved
2013 quadrants: LH=1, LL=8
2019 quadrants: HL=1, LH=2, LL=6
&lt;/code>&lt;/pre>
&lt;p>Panel (a) shows Bolivia&amp;rsquo;s modest but consistent rightward movement. All 9 regions started in the lower-left portion of the plot (8 in LL, 1 in LH) and shifted rightward by 2019, reflecting genuine improvement in own-region development. The mean SHDI change was &lt;strong>+0.033&lt;/strong>, with a remarkably tight range ([+0.030, +0.035]) indicating that the gains were broad-based across all Bolivian regions. &lt;strong>Seven of 9 regions (78%) remained in the same quadrant&lt;/strong>, with 2 moving out of LL &amp;mdash; one to LH and one to HL. The arrows are short and point consistently to the right, meaning Bolivia improved its own development levels without substantially changing the spatial lag (its neighbors' conditions remained similar). This pattern suggests steady, internally driven progress that has not yet been large enough to escape the low-development spatial cluster.&lt;/p>
&lt;p>Panel (b) tells the opposite story. &lt;strong>Venezuela&amp;rsquo;s 24 regions experienced the most dramatic downward shift&lt;/strong> in the entire dataset, with a mean SHDI change of &lt;strong>-0.065&lt;/strong>. In 2013, Venezuelan regions were spread across the upper portion of the plot &amp;mdash; 13 in HH, 5 in HL, 3 in LH, and only 3 in LL. By 2019, the picture had completely inverted: &lt;strong>21 of 24 regions (88%) crossed quadrant boundaries&lt;/strong>, with 21 ending in the LL quadrant. The arrows sweep uniformly downward and to the left, reflecting both the collapse of each region&amp;rsquo;s own development level and the negative spillover onto its neighbors' spatial lags. The narrow range of change ([-0.067, -0.064]) reveals that the crisis was not localized to a few regions &amp;mdash; it was a near-uniform national collapse that dragged every Venezuelan region, regardless of its 2013 starting point, into the low-development quadrant.&lt;/p>
&lt;p>The juxtaposition is instructive. Bolivia&amp;rsquo;s arrows are short, rightward, and clustered &amp;mdash; a country making incremental gains within a stable spatial structure. Venezuela&amp;rsquo;s arrows are long, southwest-pointing, and tightly bundled &amp;mdash; a country experiencing systemic collapse that erased decades of development advantage in just six years. The contrast highlights how economic crises can propagate spatially: Venezuela&amp;rsquo;s decline did not just reduce its own regions' development, it also pulled down the spatial lags of neighboring Colombian and Brazilian border regions, contributing to the expansion of the LL cluster documented in Section 9.&lt;/p>
&lt;h2 id="11-discussion">11. Discussion&lt;/h2>
&lt;p>&lt;strong>Spatial autocorrelation in South American human development is strong and persistent.&lt;/strong> Global Moran&amp;rsquo;s I increased from 0.568 in 2013 to 0.632 in 2019 (both p = 0.001), indicating that the spatial clustering of development levels strengthened over the period. This means the development gap between prosperous and lagging regions is not only large but spatially structured &amp;mdash; high-development regions form a contiguous band across the Southern Cone, while low-development regions form an equally contiguous band across the Amazon basin and northern South America.&lt;/p>
&lt;p>The LISA analysis pinpoints these clusters with precision. In 2019, 30 regions form a significant HH cluster (high development surrounded by high-development neighbors) and 37 regions form a significant LL cluster (low development surrounded by low-development neighbors). The LL cluster expanded from 29 to 37 regions between 2013 and 2019, driven primarily by Venezuela&amp;rsquo;s economic crisis and its spillover effects on neighboring regions. The HH cluster remained stable (31 to 30), with 87% persistence &amp;mdash; a sign that prosperity corridors in the Southern Cone are structurally entrenched.&lt;/p>
&lt;p>The space-time analysis reveals that 62% of regions stayed in the same Moran scatter plot quadrant, but the 38% that moved tell an important story. The most concerning transitions are the 10 regions that moved from HH to LL and the 17 previously non-significant regions that joined the LL LISA cluster. These movements are concentrated in Venezuela and its neighbors, illustrating how economic shocks can propagate spatially.&lt;/p>
&lt;p>The &lt;strong>Venezuela&amp;ndash;Bolivia comparison&lt;/strong> crystallizes the two forces shaping South America&amp;rsquo;s spatial development landscape. Venezuela&amp;rsquo;s 24 regions collapsed nearly uniformly (mean SHDI change of -0.065, with 88% crossing quadrant boundaries), transforming a country that was largely in the HH quadrant in 2013 into one almost entirely in the LL quadrant by 2019. Bolivia&amp;rsquo;s 9 regions, starting from a much lower base, improved steadily (+0.033) with 78% quadrant stability. These divergent trajectories illustrate that spatial clusters are not static: they can expand rapidly through crisis-driven contagion (Venezuela pulling its neighbors downward) or contract slowly through sustained internal improvement (Bolivia gradually lifting its regions rightward in the Moran scatter plot). The fact that Venezuela&amp;rsquo;s decline was spatially contagious &amp;mdash; dragging down the spatial lags of neighboring Colombian and Brazilian border regions &amp;mdash; while Bolivia&amp;rsquo;s improvement remained spatially contained underscores an asymmetry: negative shocks propagate faster and farther across borders than positive ones.&lt;/p>
&lt;p>For policy, these findings suggest that &lt;strong>spatially targeted interventions&lt;/strong> may be more effective than uniform national programs. The persistent LL clusters represent development traps where a region&amp;rsquo;s own conditions are reinforced by the equally poor conditions of its neighbors. Breaking these traps may require coordinated cross-regional or cross-border programs that address the spatial dimension of underdevelopment. Bolivia&amp;rsquo;s experience suggests that broad-based national improvement can lift all regions, but escaping the low-development spatial cluster may require the additional step of improving neighbors' conditions simultaneously &amp;mdash; a challenge that calls for cross-border cooperation.&lt;/p>
&lt;h2 id="12-summary-and-next-steps">12. Summary and next steps&lt;/h2>
&lt;p>&lt;strong>Key takeaways:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Method insight:&lt;/strong> ESDA reveals spatial patterns invisible in aspatial analysis. The same dataset that shows a modest aggregate improvement (+0.005 SHDI) conceals a deepening spatial divide &amp;mdash; Moran&amp;rsquo;s I increased from 0.568 to 0.632, meaning spatial clustering strengthened between 2013 and 2019.&lt;/li>
&lt;li>&lt;strong>Data insight:&lt;/strong> 30 HH and 37 LL regions form statistically significant clusters at the 10% level. The LL cluster expanded by 8 regions (from 29 to 37), while the HH cluster remained stable. Cluster persistence is high: 87% for HH and 62% for LL, indicating entrenched spatial inequality.&lt;/li>
&lt;li>&lt;strong>Country insight:&lt;/strong> Venezuela and Bolivia illustrate contrasting development dynamics. Venezuela&amp;rsquo;s 24 regions collapsed nearly uniformly (mean -0.065), with 88% crossing quadrant boundaries from the upper to the lower portion of the Moran scatter plot. Bolivia&amp;rsquo;s 9 regions improved steadily (+0.033) with 78% quadrant stability, showing broad-based gains that have not yet been large enough to escape the LL spatial cluster.&lt;/li>
&lt;li>&lt;strong>Limitation:&lt;/strong> Queen contiguity assumes shared borders, which excludes island territories (San Andres, Nueva Esparta) and may not capture cross-water economic linkages. With only two time periods (2013 and 2019), we cannot distinguish permanent structural clusters from temporary effects of the Venezuelan crisis. The p = 0.10 significance threshold is relatively permissive.&lt;/li>
&lt;li>&lt;strong>Next step:&lt;/strong> Extend the analysis with spatial regression models (spatial lag and spatial error models) to test whether a region&amp;rsquo;s development is directly influenced by its neighbors' development, or whether the clustering is driven by shared underlying factors. Bivariate LISA could reveal whether income clusters coincide with education clusters. Adding more time periods (2000&amp;ndash;2019) from the full Global Data Lab series would enable Spatial Markov chain analysis of cluster transition probabilities.&lt;/li>
&lt;/ul>
&lt;h2 id="13-exercises">13. Exercises&lt;/h2>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Income clusters.&lt;/strong> Repeat the LISA analysis for the income index (&lt;code>incindex2019&lt;/code>) instead of SHDI. Are income clusters in the same locations as HDI clusters? How many regions belong to both an income LL and an HDI LL cluster?&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Alternative weights.&lt;/strong> Build k-nearest neighbors weights (&lt;code>KNN&lt;/code> from &lt;code>libpysal.weights&lt;/code>) with $k = 5$ and Rook contiguity (&lt;code>Rook&lt;/code> from &lt;code>libpysal.weights&lt;/code>) instead of Queen contiguity. How does Moran&amp;rsquo;s I change under each specification? Does the KNN approach resolve the island problem?&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Bivariate Moran.&lt;/strong> Use &lt;a href="https://pysal.org/esda/generated/esda.Moran_BV.html" target="_blank" rel="noopener">&lt;code>Moran_BV&lt;/code>&lt;/a> from esda to compute the bivariate Moran&amp;rsquo;s I between education and income indices. Are regions with high education surrounded by regions with high income, or are the two dimensions spatially independent?&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Spatial autocorrelation of change.&lt;/strong> Compute Moran&amp;rsquo;s I for &lt;code>shdi_change&lt;/code> instead of the level variables. Is the &lt;em>change&lt;/em> in SHDI between 2013 and 2019 itself spatially clustered? Compare the result with the change choropleth from Section 6.2. Hint: &lt;code>Moran(gdf[&amp;quot;shdi_change&amp;quot;], W, permutations=999)&lt;/code>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Component-level Moran&amp;rsquo;s I.&lt;/strong> Compute Moran&amp;rsquo;s I for the health, education, and income indices separately in both 2013 and 2019. Which component shows the strongest spatial autocorrelation? Does the income index &amp;mdash; which declined in 46% of regions &amp;mdash; show a different spatial pattern than health or education?&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Multiple testing sensitivity.&lt;/strong> Re-run the 2019 LISA analysis at $p &amp;lt; 0.05$ instead of $p &amp;lt; 0.10$. How many HH and LL regions survive the stricter threshold? Research the Bonferroni correction ($0.05 / 153 \approx 0.0003$) and the False Discovery Rate (FDR) procedure &amp;mdash; how would these affect the cluster counts?&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Neighbor count distribution.&lt;/strong> Plot a histogram of the number of neighbors per region from the Queen weights matrix (use &lt;code>W.cardinalities&lt;/code>). What is the shape of the distribution? Which regions have the most and fewest neighbors, and why?&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Is the Moran&amp;rsquo;s I increase significant?&lt;/strong> Moran&amp;rsquo;s I rose from 0.568 to 0.632 between 2013 and 2019. But does this difference pass a significance test? Try a bootstrap approach: pool the 2013 and 2019 SHDI values, randomly assign them to the two periods 999 times, and compute the difference in Moran&amp;rsquo;s I each time. Where does the observed difference (0.064) fall in the bootstrap distribution?&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Moran&amp;rsquo;s I excluding Venezuela.&lt;/strong> Recompute Moran&amp;rsquo;s I for 2013 and 2019 after dropping Venezuela&amp;rsquo;s 24 regions (rebuild the Queen weights on the subset GeoDataFrame). Does the increase in spatial autocorrelation survive? If not, the &amp;ldquo;deepening spatial divide&amp;rdquo; may be driven by a single country&amp;rsquo;s crisis rather than a continent-wide trend.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>LISA significance map.&lt;/strong> Create a choropleth map coloring each region by its LISA p-value (&lt;code>localMoran_2019.p_sim&lt;/code>) using a sequential colormap. How many regions have $p &amp;lt; 0.01$ vs $p &amp;lt; 0.05$ vs $p &amp;lt; 0.10$? Are the deeply significant regions ($p &amp;lt; 0.01$) concentrated in the same locations as the cluster map from Section 9.2?&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="14-references">14. References&lt;/h2>
&lt;ol>
&lt;li>&lt;a href="https://doi.org/10.1111/j.1538-4632.1995.tb00338.x" target="_blank" rel="noopener">Anselin, L. (1995). Local Indicators of Spatial Association &amp;mdash; LISA. &lt;em>Geographical Analysis&lt;/em>, 27(2), 93&amp;ndash;115.&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://doi.org/10.1038/sdata.2019.38" target="_blank" rel="noopener">Smits, J. and Permanyer, I. (2019). The Subnational Human Development Database. &lt;em>Scientific Data&lt;/em>, 6, 190038.&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://rrs.scholasticahq.com/article/8285" target="_blank" rel="noopener">Rey, S. J. and Anselin, L. (2007). PySAL: A Python Library of Spatial Analytical Methods. &lt;em>Review of Regional Studies&lt;/em>, 37(1), 5&amp;ndash;27.&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://globaldatalab.org/shdi/" target="_blank" rel="noopener">Global Data Lab &amp;mdash; Subnational Human Development Index&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://pysal.org/esda/" target="_blank" rel="noopener">PySAL ESDA documentation&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://splot.readthedocs.io/" target="_blank" rel="noopener">splot documentation&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://carlos-mendez.org/post/python_pca2/">Mendez, C. (2026). Pooled PCA for Building Development Indicators Across Time.&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://carlos-mendez.org/publication/20210318-economia/" target="_blank" rel="noopener">Mendez, C. and Gonzales, E. (2021). Human Capital Constraints, Spatial Dependence, and Regionalization in Bolivia. &lt;em>Economia&lt;/em>, 44(87).&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://carlos-mendez.org/post/python_monitor_regional_development/">Mendez, C. (2026). Monitoring Regional Development with Python.&lt;/a>&lt;/li>
&lt;/ol></description></item><item><title>Multiscale Geographically Weighted Regression: Spatially Varying Economic Convergence in Indonesia</title><link>https://carlos-mendez.org/post/python_mgwr/</link><pubDate>Sun, 22 Mar 2026 00:00:00 +0000</pubDate><guid>https://carlos-mendez.org/post/python_mgwr/</guid><description>&lt;h2 id="1-overview">1. Overview&lt;/h2>
&lt;p>When we ask &amp;ldquo;do poorer regions catch up to richer ones?&amp;rdquo;, the standard approach is to run a single regression across all regions and report one coefficient. But what if the answer depends on &lt;em>where&lt;/em> you look? A negative coefficient in Sumatra does not mean the same process is at work in Papua. A global regression forces every district onto the same line &amp;mdash; and in doing so, it may hide the most interesting part of the story.&lt;/p>
&lt;p>&lt;strong>Multiscale Geographically Weighted Regression (MGWR)&lt;/strong> addresses this by estimating a separate set of coefficients at every location, weighted by proximity. Its key innovation over standard GWR is that each variable is allowed to operate at its own spatial scale. The intercept (representing baseline growth conditions) might vary smoothly across large regions, while the convergence coefficient might shift sharply between neighboring districts. MGWR discovers these scales from the data rather than imposing a single bandwidth on all variables.&lt;/p>
&lt;p>This tutorial applies MGWR to &lt;strong>514 Indonesian districts&lt;/strong> to answer: &lt;strong>does economic catching-up happen at the same pace everywhere in Indonesia, or does geography shape how fast poorer districts close the gap?&lt;/strong> We progress from a global regression baseline through MGWR estimation and coefficient mapping, revealing that the global R² of 0.214 jumps to 0.762 once we allow the relationship to vary across space.&lt;/p>
&lt;p>&lt;strong>Learning objectives:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Understand why a single regression coefficient may hide important spatial variation&lt;/li>
&lt;li>Estimate location-specific relationships with spatially varying coefficients&lt;/li>
&lt;li>Apply MGWR to allow each variable to operate at its own spatial scale&lt;/li>
&lt;li>Map and interpret spatially varying coefficients across Indonesia&lt;/li>
&lt;li>Compare global OLS vs MGWR model fit and diagnostics&lt;/li>
&lt;/ul>
&lt;h2 id="2-the-modeling-pipeline">2. The modeling pipeline&lt;/h2>
&lt;p>The analysis follows a natural progression: start with a simple global model, visualize the spatial patterns it cannot capture, then let MGWR reveal the local structure.&lt;/p>
&lt;pre>&lt;code class="language-mermaid">graph LR
A[&amp;quot;&amp;lt;b&amp;gt;Step 1&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;Load &amp;amp;&amp;lt;br/&amp;gt;Explore&amp;quot;] --&amp;gt; B[&amp;quot;&amp;lt;b&amp;gt;Step 2&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;Map&amp;lt;br/&amp;gt;Variables&amp;quot;]
B --&amp;gt; C[&amp;quot;&amp;lt;b&amp;gt;Step 3&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;Global&amp;lt;br/&amp;gt;OLS&amp;quot;]
C --&amp;gt; D[&amp;quot;&amp;lt;b&amp;gt;Step 4&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;MGWR&amp;lt;br/&amp;gt;Estimation&amp;quot;]
D --&amp;gt; E[&amp;quot;&amp;lt;b&amp;gt;Step 5&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;Map&amp;lt;br/&amp;gt;Coefficients&amp;quot;]
E --&amp;gt; F[&amp;quot;&amp;lt;b&amp;gt;Step 6&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;Significance&amp;lt;br/&amp;gt;&amp;amp; Compare&amp;quot;]
style A fill:#141413,stroke:#6a9bcc,color:#fff
style B fill:#d97757,stroke:#141413,color:#fff
style C fill:#6a9bcc,stroke:#141413,color:#fff
style D fill:#00d4c8,stroke:#141413,color:#fff
style E fill:#00d4c8,stroke:#141413,color:#fff
style F fill:#1a3a8a,stroke:#141413,color:#fff
&lt;/code>&lt;/pre>
&lt;h2 id="3-setup-and-imports">3. Setup and imports&lt;/h2>
&lt;p>The analysis uses &lt;a href="https://mgwr.readthedocs.io/" target="_blank" rel="noopener">mgwr&lt;/a> for multiscale regression, &lt;a href="https://geopandas.org/" target="_blank" rel="noopener">GeoPandas&lt;/a> for spatial data, and &lt;a href="https://pysal.org/mapclassify/" target="_blank" rel="noopener">mapclassify&lt;/a> for choropleth classification.&lt;/p>
&lt;pre>&lt;code class="language-python">import numpy as np
import pandas as pd
import geopandas as gpd
import matplotlib.pyplot as plt
from matplotlib.patches import Patch
import mapclassify
from scipy import stats
from mgwr.gwr import MGWR
from mgwr.sel_bw import Sel_BW
import warnings
warnings.filterwarnings(&amp;quot;ignore&amp;quot;)
# Site color palette
STEEL_BLUE = &amp;quot;#6a9bcc&amp;quot;
WARM_ORANGE = &amp;quot;#d97757&amp;quot;
NEAR_BLACK = &amp;quot;#141413&amp;quot;
TEAL = &amp;quot;#00d4c8&amp;quot;
&lt;/code>&lt;/pre>
&lt;details>
&lt;summary>Dark theme figure styling (click to expand)&lt;/summary>
&lt;pre>&lt;code class="language-python">DARK_NAVY = &amp;quot;#0f1729&amp;quot;
GRID_LINE = &amp;quot;#1f2b5e&amp;quot;
LIGHT_TEXT = &amp;quot;#c8d0e0&amp;quot;
WHITE_TEXT = &amp;quot;#e8ecf2&amp;quot;
plt.rcParams.update({
&amp;quot;figure.facecolor&amp;quot;: DARK_NAVY,
&amp;quot;axes.facecolor&amp;quot;: DARK_NAVY,
&amp;quot;axes.edgecolor&amp;quot;: DARK_NAVY,
&amp;quot;axes.linewidth&amp;quot;: 0,
&amp;quot;axes.labelcolor&amp;quot;: LIGHT_TEXT,
&amp;quot;axes.titlecolor&amp;quot;: WHITE_TEXT,
&amp;quot;axes.spines.top&amp;quot;: False,
&amp;quot;axes.spines.right&amp;quot;: False,
&amp;quot;axes.spines.left&amp;quot;: False,
&amp;quot;axes.spines.bottom&amp;quot;: False,
&amp;quot;axes.grid&amp;quot;: True,
&amp;quot;grid.color&amp;quot;: GRID_LINE,
&amp;quot;grid.linewidth&amp;quot;: 0.6,
&amp;quot;grid.alpha&amp;quot;: 0.8,
&amp;quot;xtick.color&amp;quot;: LIGHT_TEXT,
&amp;quot;ytick.color&amp;quot;: LIGHT_TEXT,
&amp;quot;xtick.major.size&amp;quot;: 0,
&amp;quot;ytick.major.size&amp;quot;: 0,
&amp;quot;text.color&amp;quot;: WHITE_TEXT,
&amp;quot;font.size&amp;quot;: 12,
&amp;quot;legend.frameon&amp;quot;: False,
&amp;quot;legend.fontsize&amp;quot;: 11,
&amp;quot;legend.labelcolor&amp;quot;: LIGHT_TEXT,
&amp;quot;figure.edgecolor&amp;quot;: DARK_NAVY,
&amp;quot;savefig.facecolor&amp;quot;: DARK_NAVY,
&amp;quot;savefig.edgecolor&amp;quot;: DARK_NAVY,
})
&lt;/code>&lt;/pre>
&lt;/details>
&lt;h2 id="4-data-loading-and-exploration">4. Data loading and exploration&lt;/h2>
&lt;p>The dataset covers &lt;strong>514 Indonesian districts&lt;/strong> with GDP per capita in 2010 and the subsequent growth rate through 2018. Indonesia is an ideal setting for studying spatial heterogeneity: it spans over 17,000 islands across 5,000 km of ocean, with enormous variation in economic structure, geography, and institutional capacity.&lt;/p>
&lt;p>The core idea behind convergence is straightforward: if poorer districts tend to grow faster than richer ones, the income gap narrows over time. In a regression framework, this means we expect a &lt;strong>negative relationship&lt;/strong> between initial income (log GDP per capita in 2010) and subsequent growth. The question is whether that negative relationship holds uniformly across the archipelago &amp;mdash; or whether it is stronger in some places and weaker (or even reversed) in others.&lt;/p>
&lt;pre>&lt;code class="language-python">CSV_URL = (&amp;quot;https://github.com/quarcs-lab/data-quarcs/raw/refs/heads/&amp;quot;
&amp;quot;master/indonesia514/dataBeta.csv&amp;quot;)
GEO_URL = (&amp;quot;https://github.com/quarcs-lab/data-quarcs/raw/refs/heads/&amp;quot;
&amp;quot;master/indonesia514/mapIdonesia514-opt.geojson&amp;quot;)
df = pd.read_csv(CSV_URL)
geo = gpd.read_file(GEO_URL)
gdf = geo.merge(df, on=&amp;quot;districtID&amp;quot;, how=&amp;quot;left&amp;quot;)
print(f&amp;quot;Loaded: {gdf.shape[0]} districts, {gdf.shape[1]} columns&amp;quot;)
print(gdf[[&amp;quot;ln_gdppc2010&amp;quot;, &amp;quot;g&amp;quot;]].describe().round(4).to_string())
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Loaded: 514 districts, 16 columns
ln_gdppc2010 g
count 514.0000 514.0000
mean 9.8371 0.3860
std 0.7603 0.3205
min 7.1657 -2.0452
25% 9.3983 0.2583
50% 9.7626 0.3453
75% 10.1739 0.4158
max 13.4438 2.0563
&lt;/code>&lt;/pre>
&lt;p>The 514 districts span a wide range of initial income: log GDP per capita ranges from 7.17 (the poorest district, roughly \$1,300 per capita) to 13.44 (the richest, roughly \$690,000 &amp;mdash; likely a resource-extraction enclave). Growth rates also vary enormously, from -2.05 (severe contraction) to +2.06 (rapid expansion), with a mean of 0.39. This high variance in both variables suggests that a single regression line will struggle to capture the full picture.&lt;/p>
&lt;h2 id="5-exploratory-maps">5. Exploratory maps&lt;/h2>
&lt;p>Before fitting any model, we map the two key variables to see whether spatial patterns are visible to the naked eye. If initial income and growth are geographically clustered, that is already a hint that spatial models will outperform global ones.&lt;/p>
&lt;pre>&lt;code class="language-python">fig, axes = plt.subplots(2, 1, figsize=(14, 14))
for ax, col, title in [
(axes[0], &amp;quot;ln_gdppc2010&amp;quot;, &amp;quot;(a) Log GDP per capita, 2010&amp;quot;),
(axes[1], &amp;quot;g&amp;quot;, &amp;quot;(b) GDP growth rate, 2010–2018&amp;quot;),
]:
fj = mapclassify.FisherJenks(gdf[col].dropna().values, k=5)
classified = mapclassify.UserDefined(gdf[col].values, bins=fj.bins.tolist())
cmap = plt.cm.coolwarm
norm = plt.Normalize(vmin=0, vmax=4)
colors = [cmap(norm(c)) for c in classified.yb]
gdf.plot(ax=ax, color=colors, edgecolor=GRID_LINE, linewidth=0.2)
ax.set_title(title, fontsize=14, pad=10)
ax.set_axis_off()
plt.tight_layout()
plt.savefig(&amp;quot;mgwr_map_xy.png&amp;quot;, dpi=300, bbox_inches=&amp;quot;tight&amp;quot;)
plt.show()
&lt;/code>&lt;/pre>
&lt;p>&lt;img src="mgwr_map_xy.png" alt="Two-panel choropleth map of Indonesia showing log GDP per capita in 2010 and GDP growth rate 2010-2018.">&lt;/p>
&lt;p>The maps reveal clear spatial structure. Initial income (panel a) is highest in Jakarta and resource-rich districts in Kalimantan and Papua (warm red), while the lowest-income districts cluster in eastern Nusa Tenggara and parts of Maluku (cool blue). Growth rates (panel b) show a different pattern: some of the poorest districts in Papua and Sulawesi experienced rapid growth (suggesting catching-up), while several high-income resource districts saw contraction. The fact that these patterns are geographically organized &amp;mdash; not randomly scattered &amp;mdash; motivates the use of spatially varying models.&lt;/p>
&lt;h2 id="6-global-regression-baseline">6. Global regression baseline&lt;/h2>
&lt;p>The simplest test for economic convergence fits a single regression line through all 514 districts. If the slope is negative, poorer districts (low initial income) tend to grow faster than richer ones.&lt;/p>
&lt;p>$$g_i = \alpha + \beta \cdot \ln(y_{i,2010}) + \varepsilon_i$$&lt;/p>
&lt;p>where $g_i$ is the growth rate, $\ln(y_{i,2010})$ is log initial income, and $\beta &amp;lt; 0$ indicates convergence. In the code, $g_i$ corresponds to the column &lt;code>g&lt;/code> and $\ln(y_{i,2010})$ to &lt;code>ln_gdppc2010&lt;/code>.&lt;/p>
&lt;pre>&lt;code class="language-python">slope, intercept, r_value, p_value, std_err = stats.linregress(
gdf[&amp;quot;ln_gdppc2010&amp;quot;], gdf[&amp;quot;g&amp;quot;]
)
print(f&amp;quot;Slope (convergence coefficient): {slope:.4f}&amp;quot;)
print(f&amp;quot;R-squared: {r_value**2:.4f}&amp;quot;)
print(f&amp;quot;p-value: {p_value:.6f}&amp;quot;)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Slope (convergence coefficient): -0.1948
R-squared: 0.2135
p-value: 0.000000
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-python">fig, ax = plt.subplots(figsize=(10, 7))
ax.scatter(gdf[&amp;quot;ln_gdppc2010&amp;quot;], gdf[&amp;quot;g&amp;quot;],
color=STEEL_BLUE, edgecolors=GRID_LINE, s=35, alpha=0.6, zorder=3)
x_range = np.linspace(gdf[&amp;quot;ln_gdppc2010&amp;quot;].min(), gdf[&amp;quot;ln_gdppc2010&amp;quot;].max(), 100)
ax.plot(x_range, intercept + slope * x_range, color=WARM_ORANGE,
linewidth=2, zorder=2)
ax.set_xlabel(&amp;quot;Log GDP per capita (2010)&amp;quot;)
ax.set_ylabel(&amp;quot;GDP growth rate (2010–2018)&amp;quot;)
ax.set_title(&amp;quot;Global convergence regression&amp;quot;)
plt.savefig(&amp;quot;mgwr_scatter_global.png&amp;quot;, dpi=300, bbox_inches=&amp;quot;tight&amp;quot;)
plt.show()
&lt;/code>&lt;/pre>
&lt;p>&lt;img src="mgwr_scatter_global.png" alt="Scatter plot of log GDP per capita 2010 vs growth rate with OLS regression line.">&lt;/p>
&lt;p>The global regression confirms that convergence exists &lt;strong>on average&lt;/strong>: the slope is $-0.195$ (p &amp;lt; 0.001), meaning a 1-unit increase in log initial income is associated with a 0.195 percentage-point lower growth rate. However, the R² of only 0.214 means this single line explains just 21% of the variation in growth rates. The scatter plot shows enormous dispersion around the regression line &amp;mdash; many districts with similar initial income experienced vastly different growth trajectories. This low explanatory power is the motivation for MGWR: perhaps the relationship is not weak everywhere, but rather strong in some regions and absent in others, and a single coefficient is simply averaging over this heterogeneity.&lt;/p>
&lt;h2 id="7-from-global-to-local-why-mgwr">7. From global to local: why MGWR?&lt;/h2>
&lt;h3 id="71-the-limitation-of-a-single-coefficient">7.1 The limitation of a single coefficient&lt;/h3>
&lt;p>The global regression tells us that $\beta = -0.195$ on average across Indonesia. But consider two districts with the same initial income &amp;mdash; one in Java, where infrastructure and market access are strong, and one in Papua, where remoteness and institutional challenges dominate. There is no reason to expect the same convergence dynamic in both places. A single coefficient forces them onto the same line.&lt;/p>
&lt;p>&lt;strong>Geographically Weighted Regression (GWR)&lt;/strong> addresses this by estimating a separate regression at each location, using a kernel function &amp;mdash; a distance-decay weighting scheme (typically Gaussian or bisquare) that gives more weight to nearby observations and less to distant ones. The result is a set of &lt;strong>location-specific coefficients&lt;/strong> &amp;mdash; each district gets its own slope and intercept:&lt;/p>
&lt;p>$$g_i = \alpha(u_i, v_i) + \beta(u_i, v_i) \cdot \ln(y_{i,2010}) + \varepsilon_i$$&lt;/p>
&lt;p>where $(u_i, v_i)$ are the geographic coordinates of district $i$, and both $\alpha$ and $\beta$ are now functions of location rather than fixed constants. In the code, $(u_i, v_i)$ correspond to &lt;code>COORD_X&lt;/code> and &lt;code>COORD_Y&lt;/code>. The &lt;strong>bandwidth&lt;/strong> parameter $h$ controls how many neighbors contribute to each local regression &amp;mdash; a small bandwidth means only very close districts matter (highly local), while a large bandwidth approaches the global model.&lt;/p>
&lt;p>However, standard GWR uses a single bandwidth for all variables, which means the intercept and the convergence coefficient are forced to vary at the same spatial scale.&lt;/p>
&lt;p>&lt;strong>MGWR&lt;/strong> removes this constraint. It allows each variable to find its own optimal bandwidth through an iterative back-fitting procedure &amp;mdash; a process that cycles through each variable, optimizing its bandwidth while holding the others fixed, until all bandwidths converge. If baseline growth conditions vary smoothly across large regions (large bandwidth), while the convergence speed varies sharply between neighboring districts (small bandwidth), MGWR will discover this from the data. This makes MGWR a more flexible and realistic model for processes that operate at multiple spatial scales. The key assumption is that spatial relationships are &lt;strong>locally stationary&lt;/strong> within each kernel window &amp;mdash; the relationship between income and growth is approximately constant among the nearest $h$ districts, even if it differs across the full map.&lt;/p>
&lt;h3 id="72-mgwr-estimation">7.2 MGWR estimation&lt;/h3>
&lt;p>The &lt;code>mgwr&lt;/code> package requires variables to be &lt;strong>standardized&lt;/strong> (zero mean, unit variance) before multiscale bandwidth selection. This ensures that the bandwidths are comparable across variables measured in different units. The &lt;code>spherical=True&lt;/code> flag tells the algorithm to compute great-circle distances rather than Euclidean distances, which is essential when working with geographic coordinates spanning a large area like Indonesia.&lt;/p>
&lt;pre>&lt;code class="language-python"># Prepare variables
y = gdf[&amp;quot;g&amp;quot;].values.reshape((-1, 1))
X = gdf[[&amp;quot;ln_gdppc2010&amp;quot;]].values
coords = list(zip(gdf[&amp;quot;COORD_X&amp;quot;], gdf[&amp;quot;COORD_Y&amp;quot;]))
# Standardize (required for MGWR)
Zy = (y - y.mean(axis=0)) / y.std(axis=0)
ZX = (X - X.mean(axis=0)) / X.std(axis=0)
# Bandwidth selection and model fitting
mgwr_selector = Sel_BW(coords, Zy, ZX, multi=True, spherical=True)
mgwr_bw = mgwr_selector.search()
mgwr_results = MGWR(coords, Zy, ZX, mgwr_selector, spherical=True).fit()
mgwr_results.summary()
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">===========================================================================
Model type Gaussian
Number of observations: 514
Number of covariates: 2
Global Regression Results
---------------------------------------------------------------------------
R2: 0.214
Adj. R2: 0.212
Multi-Scale Geographically Weighted Regression (MGWR) Results
---------------------------------------------------------------------------
Spatial kernel: Adaptive bisquare
MGWR bandwidths
---------------------------------------------------------------------------
Variable Bandwidth ENP_j Adj t-val(95%) Adj alpha(95%)
X0 44.000 26.805 3.127 0.002
X1 44.000 25.271 3.109 0.002
Diagnostic information
---------------------------------------------------------------------------
Residual sum of squares: 122.081
Effective number of parameters (trace(S)): 52.076
Sigma estimate: 0.514
R2 0.762
Adjusted R2 0.736
AICc: 838.405
===========================================================================
&lt;/code>&lt;/pre>
&lt;p>The MGWR results are striking. &lt;strong>R² jumps from 0.214 (global) to 0.762 (MGWR)&lt;/strong> &amp;mdash; the spatially varying model explains more than three times as much variation as the global regression. Both the intercept and the convergence coefficient receive a bandwidth of 44, meaning each local regression draws on the 44 nearest districts. This is a relatively local scale (44 out of 514 districts, or about 8.6% of the sample), confirming that the convergence relationship varies substantially across the archipelago. The effective number of parameters is 52.1, reflecting the cost of estimating location-specific coefficients instead of two global ones.&lt;/p>
&lt;h3 id="73-mapping-mgwr-coefficients">7.3 Mapping MGWR coefficients&lt;/h3>
&lt;p>The power of MGWR lies in the coefficient maps. Instead of a single number for the whole country, we can now visualize how the convergence relationship changes from district to district. Because MGWR is estimated on standardized variables, the mapped coefficients are in &lt;strong>standard-deviation units&lt;/strong>: a coefficient of $-1.0$ means that a one-standard-deviation increase in log initial income is associated with a one-standard-deviation decrease in growth at that location.&lt;/p>
&lt;pre>&lt;code class="language-python">gdf[&amp;quot;mgwr_intercept&amp;quot;] = mgwr_results.params[:, 0]
gdf[&amp;quot;mgwr_slope&amp;quot;] = mgwr_results.params[:, 1]
&lt;/code>&lt;/pre>
&lt;p>&lt;strong>Intercept map&lt;/strong> &amp;mdash; the intercept captures baseline growth conditions after accounting for initial income. Positive values indicate districts that grew faster than expected given their income level; negative values indicate underperformance.&lt;/p>
&lt;pre>&lt;code class="language-python">fig, ax = plt.subplots(figsize=(14, 8))
# Fisher-Jenks classification with Patch legend (see script.py for details)
gdf.plot(ax=ax, column=&amp;quot;mgwr_intercept&amp;quot;, scheme=&amp;quot;FisherJenks&amp;quot;, k=5,
cmap=&amp;quot;coolwarm&amp;quot;, edgecolor=GRID_LINE, linewidth=0.2, legend=True)
ax.set_title(f&amp;quot;MGWR intercept (bandwidth = {int(mgwr_bw[0])})&amp;quot;)
ax.set_axis_off()
plt.savefig(&amp;quot;mgwr_mgwr_intercept.png&amp;quot;, dpi=300, bbox_inches=&amp;quot;tight&amp;quot;)
plt.show()
&lt;/code>&lt;/pre>
&lt;p>&lt;img src="mgwr_mgwr_intercept.png" alt="MGWR intercept map across Indonesia&amp;rsquo;s 514 districts.">&lt;/p>
&lt;p>The intercept map reveals a clear east&amp;ndash;west gradient. Districts in &lt;strong>western Indonesia&lt;/strong> (Sumatra and Java) tend to have negative intercepts &amp;mdash; they grew &lt;strong>less&lt;/strong> than the convergence model would predict based on their initial income alone. Districts in &lt;strong>eastern Indonesia&lt;/strong> (Papua, Maluku, Nusa Tenggara) show positive intercepts, indicating growth that &lt;strong>exceeded&lt;/strong> what initial income would predict. This pattern may reflect the role of resource extraction, infrastructure investment, and fiscal transfers that disproportionately boosted growth in less-developed eastern regions during the 2010&amp;ndash;2018 period.&lt;/p>
&lt;p>&lt;strong>Convergence coefficient map&lt;/strong> &amp;mdash; the slope captures how strongly initial income predicts subsequent growth at each location. Large negative values indicate rapid catching-up; values near zero or positive indicate no convergence or divergence.&lt;/p>
&lt;pre>&lt;code class="language-python">fig, ax = plt.subplots(figsize=(14, 8))
gdf.plot(ax=ax, column=&amp;quot;mgwr_slope&amp;quot;, scheme=&amp;quot;FisherJenks&amp;quot;, k=5,
cmap=&amp;quot;coolwarm&amp;quot;, edgecolor=GRID_LINE, linewidth=0.2, legend=True)
ax.set_title(f&amp;quot;MGWR convergence coefficient (bandwidth = {int(mgwr_bw[1])})&amp;quot;)
ax.set_axis_off()
plt.savefig(&amp;quot;mgwr_mgwr_slope.png&amp;quot;, dpi=300, bbox_inches=&amp;quot;tight&amp;quot;)
plt.show()
&lt;/code>&lt;/pre>
&lt;p>&lt;img src="mgwr_mgwr_slope.png" alt="MGWR convergence coefficient map across Indonesia.">&lt;/p>
&lt;p>The convergence coefficient map is the central finding of this analysis. The global regression reported a single $\beta = -0.195$, but MGWR reveals that this average hides enormous spatial variation. The &lt;strong>strongest catching-up&lt;/strong> (deepest blue, coefficients as negative as $-1.74$) concentrates in &lt;strong>western Sumatra and parts of Kalimantan&lt;/strong> &amp;mdash; districts where poorer areas grew much faster than richer neighbors. In contrast, most of &lt;strong>Java, eastern Indonesia, and the Maluku islands&lt;/strong> show coefficients near zero (light pink), indicating that the convergence relationship is essentially absent in these areas. A handful of districts show weakly positive coefficients (up to 0.42), suggesting localized divergence where richer districts pulled further ahead. The coefficient ranges from $-1.74$ to $+0.42$, with a median of $-0.085$ and a standard deviation of 0.553 &amp;mdash; far from the single value of $-0.195$ reported by the global model.&lt;/p>
&lt;h3 id="74-statistical-significance">7.4 Statistical significance&lt;/h3>
&lt;p>Not all local coefficients are statistically distinguishable from zero. MGWR provides t-values corrected for multiple testing, which we use to classify each district&amp;rsquo;s convergence coefficient as significantly negative (catching-up), not significant, or significantly positive (diverging).&lt;/p>
&lt;pre>&lt;code class="language-python">mgwr_filtered_t = mgwr_results.filter_tvals()
t_sig = mgwr_filtered_t[:, 1] # Slope t-values
sig_cats = np.where(t_sig &amp;lt; 0, &amp;quot;Negative (catching-up)&amp;quot;,
np.where(t_sig &amp;gt; 0, &amp;quot;Positive (diverging)&amp;quot;, &amp;quot;Not significant&amp;quot;))
print(f&amp;quot;Negative (catching-up): {(sig_cats == 'Negative (catching-up)').sum()}&amp;quot;)
print(f&amp;quot;Not significant: {(sig_cats == 'Not significant').sum()}&amp;quot;)
print(f&amp;quot;Positive (diverging): {(sig_cats == 'Positive (diverging)').sum()}&amp;quot;)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Negative (catching-up): 149
Not significant: 365
Positive (diverging): 0
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-python">fig, ax = plt.subplots(figsize=(14, 8))
cat_colors = {
&amp;quot;Negative (catching-up)&amp;quot;: &amp;quot;#2c7bb6&amp;quot;,
&amp;quot;Not significant&amp;quot;: GRID_LINE,
&amp;quot;Positive (diverging)&amp;quot;: &amp;quot;#d7191c&amp;quot;,
}
colors_sig = [cat_colors[c] for c in sig_cats]
gdf.plot(ax=ax, color=colors_sig, edgecolor=GRID_LINE, linewidth=0.2)
ax.set_title(&amp;quot;MGWR convergence coefficient: statistical significance&amp;quot;)
ax.set_axis_off()
plt.savefig(&amp;quot;mgwr_mgwr_significance.png&amp;quot;, dpi=300, bbox_inches=&amp;quot;tight&amp;quot;)
plt.show()
&lt;/code>&lt;/pre>
&lt;p>&lt;img src="mgwr_mgwr_significance.png" alt="Significance map showing districts with statistically significant catching-up.">&lt;/p>
&lt;p>Of 514 districts, &lt;strong>149 (29%)&lt;/strong> show statistically significant convergence at the corrected 5% level &amp;mdash; concentrated in &lt;strong>Sumatra, western Kalimantan, and Sulawesi&lt;/strong>. The remaining &lt;strong>365 districts (71%)&lt;/strong> have convergence coefficients that are not distinguishable from zero after correcting for multiple comparisons. &lt;strong>No district&lt;/strong> shows significant divergence. This means that while the global regression detects convergence on average, it is actually driven by a minority of districts &amp;mdash; primarily in western Indonesia &amp;mdash; while the majority of the archipelago shows no significant relationship between initial income and growth.&lt;/p>
&lt;h2 id="8-model-comparison">8. Model comparison&lt;/h2>
&lt;p>The table below summarizes how much explanatory power the spatially varying model adds over the global baseline.&lt;/p>
&lt;pre>&lt;code class="language-python">print(f&amp;quot;{'Metric':&amp;lt;25} {'Global OLS':&amp;gt;12} {'MGWR':&amp;gt;12}&amp;quot;)
print(f&amp;quot;{'R²':&amp;lt;25} {0.2135:&amp;gt;12.4f} {0.7625:&amp;gt;12.4f}&amp;quot;)
print(f&amp;quot;{'Adj. R²':&amp;lt;25} {0.2120:&amp;gt;12.4f} {0.7357:&amp;gt;12.4f}&amp;quot;)
print(f&amp;quot;{'AICc':&amp;lt;25} {1341.25:&amp;gt;12.2f} {838.41:&amp;gt;12.2f}&amp;quot;)
print(f&amp;quot;{'Bandwidth (intercept)':&amp;lt;25} {'all (514)':&amp;gt;12} {'44':&amp;gt;12}&amp;quot;)
print(f&amp;quot;{'Bandwidth (slope)':&amp;lt;25} {'all (514)':&amp;gt;12} {'44':&amp;gt;12}&amp;quot;)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Metric Global OLS MGWR
R² 0.2135 0.7625
Adj. R² 0.2120 0.7357
AICc 1341.25 838.41
Bandwidth (intercept) all (514) 44
Bandwidth (slope) all (514) 44
&lt;/code>&lt;/pre>
&lt;p>MGWR more than triples the explained variance ($R^2$: 0.214 to 0.762) and dramatically reduces the AICc from 1341 to 838, confirming that the improvement in fit is not merely due to additional flexibility. The bandwidth of 44 for both variables means each local regression uses the nearest 44 districts (about 8.6% of the sample), confirming that the convergence process is highly localized. The adjusted $R^2$ of 0.736 accounts for the additional complexity (52 effective parameters vs 2 in OLS) and still shows a massive improvement, indicating that the spatial variation in coefficients is genuine and not overfitting.&lt;/p>
&lt;h2 id="9-discussion">9. Discussion&lt;/h2>
&lt;p>&lt;strong>Economic catching-up in Indonesia is not uniform &amp;mdash; it is concentrated in western Sumatra and parts of Kalimantan, while most of the archipelago shows no significant convergence.&lt;/strong> The global regression&amp;rsquo;s $\beta = -0.195$ suggests a moderate convergence tendency, but MGWR reveals that this average is driven by a subset of 149 districts (29%) with strong catching-up dynamics. The remaining 365 districts have convergence coefficients indistinguishable from zero.&lt;/p>
&lt;p>The intercept map adds another dimension: eastern Indonesian districts tend to have positive intercepts (above-expected growth), while western districts have negative intercepts (below-expected growth). This east&amp;ndash;west gradient likely reflects the impact of fiscal transfers, resource booms, and infrastructure programs that targeted less-developed regions during the 2010&amp;ndash;2018 period. Combined with the convergence coefficient map, the picture is nuanced: eastern Indonesia grew faster than expected (high intercept), but not because of convergence dynamics (near-zero slope) &amp;mdash; rather, because of other factors captured by the intercept.&lt;/p>
&lt;p>For policy, these findings challenge the assumption that national-level convergence statistics reflect what is happening locally. A policymaker looking at $\beta = -0.195$ might conclude that Indonesia&amp;rsquo;s development strategy is successfully closing regional gaps. MGWR reveals that catching-up is geographically selective, and the majority of districts are not on a convergence path at all. Spatially targeted interventions &amp;mdash; rather than uniform national programs &amp;mdash; may be needed to address this uneven landscape.&lt;/p>
&lt;h2 id="10-summary-and-next-steps">10. Summary and next steps&lt;/h2>
&lt;p>&lt;strong>Key takeaways:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Method insight:&lt;/strong> MGWR reveals spatial heterogeneity invisible to global regression. R² improves from 0.214 to 0.762 by allowing location-specific coefficients. Both variables operate at a bandwidth of 44 districts (~8.6% of the sample), indicating highly localized economic dynamics. Variable standardization is essential before MGWR estimation.&lt;/li>
&lt;li>&lt;strong>Data insight:&lt;/strong> Only 149 of 514 Indonesian districts (29%) show statistically significant convergence, concentrated in Sumatra and Kalimantan. The convergence coefficient ranges from $-1.74$ to $+0.42$, far from the global average of $-0.195$. Eastern Indonesia grows faster than expected (positive intercepts) but not through convergence &amp;mdash; the catching-up mechanism is absent there.&lt;/li>
&lt;li>&lt;strong>Limitation:&lt;/strong> The bivariate model (one independent variable) is intentionally simple for pedagogical purposes. Real convergence analysis would include controls for human capital, infrastructure, institutional quality, and sectoral composition. The bandwidth of 44 applies to both variables in this case, but with additional covariates, MGWR&amp;rsquo;s ability to assign different bandwidths per variable would be more visible.&lt;/li>
&lt;li>&lt;strong>Next step:&lt;/strong> Extend the model with additional covariates (education, investment, fiscal transfers) to disentangle the sources of spatial heterogeneity. Apply MGWR to panel data with multiple time periods. Compare MGWR results with the spatial clusters identified in the &lt;a href="https://carlos-mendez.org/post/python_esda2/">ESDA tutorial&lt;/a> to see whether convergence hotspots align with LISA clusters.&lt;/li>
&lt;/ul>
&lt;h2 id="11-exercises">11. Exercises&lt;/h2>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Add a second variable.&lt;/strong> Include an education indicator (e.g., years of schooling) as a second independent variable and re-run MGWR. Do the two covariates receive different bandwidths? What does that tell you about the spatial scale at which education affects growth?&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Map the t-values.&lt;/strong> Instead of mapping the raw coefficients, map the local t-statistics from &lt;code>mgwr_results.tvalues[:, 1]&lt;/code>. How does this map compare to the significance map based on corrected t-values?&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Compare with ESDA.&lt;/strong> Run a Moran&amp;rsquo;s I test on the MGWR residuals. Is there remaining spatial autocorrelation? If not, MGWR has successfully captured the spatial structure. If yes, what might be missing?&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="12-references">12. References&lt;/h2>
&lt;ol>
&lt;li>&lt;a href="https://doi.org/10.1080/24694452.2017.1352480" target="_blank" rel="noopener">Fotheringham, A. S., Yang, W., and Kang, W. (2017). Multiscale Geographically Weighted Regression (MGWR). &lt;em>Annals of the American Association of Geographers&lt;/em>, 107(6), 1247&amp;ndash;1265.&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://doi.org/10.21105/joss.01750" target="_blank" rel="noopener">Oshan, T. M., Li, Z., Kang, W., Wolf, L. J., and Fotheringham, A. S. (2019). mgwr: A Python Implementation of Multiscale Geographically Weighted Regression. &lt;em>JOSS&lt;/em>, 4(42), 1750.&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://doi.org/10.1111/j.1538-4632.1996.tb00936.x" target="_blank" rel="noopener">Brunsdon, C., Fotheringham, A. S., and Charlton, M. E. (1996). Geographically Weighted Regression: A Method for Exploring Spatial Nonstationarity. &lt;em>Geographical Analysis&lt;/em>, 28(4), 281&amp;ndash;298.&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.wiley.com/en-us/Geographically&amp;#43;Weighted&amp;#43;Regression-p-9780471496168" target="_blank" rel="noopener">Fotheringham, A. S., Brunsdon, C., and Charlton, M. (2002). &lt;em>Geographically Weighted Regression: The Analysis of Spatially Varying Relationships&lt;/em>. Wiley.&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://carlos-mendez.org/publication/20241219-ae/" target="_blank" rel="noopener">Mendez, C. and Jiang, Q. (2024). Spatial Heterogeneity Modeling for Regional Economic Analysis: A Computational Approach Using Python and Cloud Computing. Working Paper, Nagoya University.&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://mgwr.readthedocs.io/" target="_blank" rel="noopener">mgwr documentation&lt;/a>&lt;/li>
&lt;/ol></description></item><item><title>Exploratory Spatial Data Analysis (ESDA)</title><link>https://carlos-mendez.org/post/python_esda/</link><pubDate>Fri, 01 Mar 2024 00:00:00 +0000</pubDate><guid>https://carlos-mendez.org/post/python_esda/</guid><description>&lt;h1 id="exploratory-spatial-data-analysis-esda-of-regional-development">Exploratory Spatial Data Analysis (ESDA) of Regional Development&lt;/h1>
&lt;p>This &lt;a href="https://esda101-bolivia339.streamlit.app/" target="_blank" rel="noopener">interactive application&lt;/a> enables users to explore municipal development indicators across Bolivia. In particular, it offers:&lt;/p>
&lt;ul>
&lt;li>🗺️ Geographical data visualizations&lt;/li>
&lt;li>📈 Distribution and comparative analysis tools&lt;/li>
&lt;li>💾 Downloadable datasets&lt;/li>
&lt;li>🧮 Access to a cloud-based computational notebook on &lt;a href="https://colab.research.google.com/drive/1JHf8wPxSxBdKKhXaKQZUzhEpVznKGiep?usp=sharing" target="_blank" rel="noopener">Google Colab&lt;/a>&lt;/li>
&lt;/ul>
&lt;iframe
src="https://cmg777.github.io/open-results/files/mapBolivia339imds.html"
width="100%"
height="576"
frameborder="0"
loading="lazy"
style="border:none;">
&lt;/iframe>
&lt;blockquote>
&lt;p>⚠️ This application is open source and still work in progress. Source code is available at: &lt;a href="https://github.com/cmg777/streamlit_esda101" target="_blank" rel="noopener">github.com/cmg777/streamlit_esda101&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;h2 id="-data-sources-and-credits">📚 Data Sources and Credits&lt;/h2>
&lt;ul>
&lt;li>Primary data source: &lt;a href="https://sdsnbolivia.org/Atlas/" target="_blank" rel="noopener">Municipal Atlas of the SDGs in Bolivia 2020.&lt;/a>&lt;/li>
&lt;li>Additional indicators for multiple years were sourced from the &lt;a href="https://www.aiddata.org/geoquery" target="_blank" rel="noopener">GeoQuery project.&lt;/a>&lt;/li>
&lt;li>Administrative boundaries from the &lt;a href="https://www.geoboundaries.org/" target="_blank" rel="noopener">GeoBoundaries database&lt;/a>&lt;/li>
&lt;li>Streamlit web app and computational notebook by &lt;a href="https://carlos-mendez.org" target="_blank" rel="noopener">Carlos Mendez.&lt;/a>&lt;/li>
&lt;li>Erick Gonzales and Pedro Leoni also colaborated in the organization of the data and the creation of the initial geospatial database&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Citation&lt;/strong>:&lt;br>
Mendez, C. (2025, March 24). &lt;em>Regional Development Indicators of Bolivia: A Dashboard for Exploratory Analysis&lt;/em> (Version 0.0.2) [Computer software]. Zenodo. &lt;a href="https://doi.org/10.5281/zenodo.15074864" target="_blank" rel="noopener">https://doi.org/10.5281/zenodo.15074864&lt;/a>&lt;/p>
&lt;hr>
&lt;h2 id="-context-and-motivation">🌐 Context and Motivation&lt;/h2>
&lt;p>Adopted in 2015, the &lt;strong>2030 Agenda for Sustainable Development&lt;/strong> established 17 Sustainable Development Goals. While global metrics offer useful benchmarks, they often overlook subnational disparities—particularly in heterogeneous countries such as Bolivia.&lt;/p>
&lt;ul>
&lt;li>🇧🇴 Bolivia ranks &lt;strong>79/166&lt;/strong> on the 2020 SDG Index (score: 69.3)&lt;/li>
&lt;li>🏘️ The &lt;em>&lt;a href="http://atlas.sdsnbolivia.org" target="_blank" rel="noopener">Municipal Atlas of the SDGs in Bolivia 2020&lt;/a>&lt;/em> reveals &lt;strong>intra-national disparities&lt;/strong> comparable to &lt;strong>global inter-country variation&lt;/strong>&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="-development-index-índice-municipal-de-desarrollo-sostenible-imds">📊 Development Index: Índice Municipal de Desarrollo Sostenible (IMDS)&lt;/h2>
&lt;p>The &lt;strong>Municipal Sustainable Development Index (IMDS)&lt;/strong> summarizes municipal performance using 62 indicators across 15 Sustainable Development Goals. However, systematic and reliable information on goals 12 and 14 were not available at the municipal level.&lt;/p>
&lt;h3 id="-methodological-criteria">🎯 Methodological Criteria&lt;/h3>
&lt;ul>
&lt;li>✅ Relevance to local Sustainable Development Goal targets&lt;/li>
&lt;li>📥 Data availability from official or trusted sources&lt;/li>
&lt;li>🌐 Full municipal coverage (339 municipalities)&lt;/li>
&lt;li>🕒 Data mostly from 2012–2019&lt;/li>
&lt;li>🧮 Low redundancy between indicators&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="-indicators-by-sustainable-development-goal">🗃️ Indicators by Sustainable Development Goal&lt;/h2>
&lt;h3 id="-goal-1-no-poverty">🧱 Goal 1: No Poverty&lt;/h3>
&lt;ul>
&lt;li>Energy poverty rate (2012, INE)&lt;/li>
&lt;li>Multidimensional Poverty Index (2013, UDAPE)&lt;/li>
&lt;li>Unmet Basic Needs (2012, INE)&lt;/li>
&lt;li>Access to basic services: water, sanitation, electricity (2012, INE)&lt;/li>
&lt;/ul>
&lt;h3 id="-goal-2-zero-hunger">🌾 Goal 2: Zero Hunger&lt;/h3>
&lt;ul>
&lt;li>Chronic malnutrition in children under five (2016, Ministry of Health)&lt;/li>
&lt;li>Obesity prevalence in women (2016, Ministry of Health)&lt;/li>
&lt;li>Average agricultural unit size (2013, Agricultural Census)&lt;/li>
&lt;li>Tractor density per 1,000 farms (2013, Agricultural Census)&lt;/li>
&lt;/ul>
&lt;h3 id="-goal-3-good-health-and-well-being">🏥 Goal 3: Good Health and Well-being&lt;/h3>
&lt;ul>
&lt;li>Infant and under-five mortality rates (2016, Ministry of Health)&lt;/li>
&lt;li>Institutional birth coverage (2016, Ministry of Health)&lt;/li>
&lt;li>Incidence of Chagas, HIV, malaria, tuberculosis, dengue (2016, Ministry of Health)&lt;/li>
&lt;li>Adolescent fertility rate (2016, Ministry of Health)&lt;/li>
&lt;/ul>
&lt;h3 id="-goal-4-quality-education">📚 Goal 4: Quality Education&lt;/h3>
&lt;ul>
&lt;li>Secondary school dropout rates, by gender (2016, Ministry of Education)&lt;/li>
&lt;li>Adult literacy rate (2012, INE)&lt;/li>
&lt;li>Share of population with higher education (2012, INE)&lt;/li>
&lt;li>Share of qualified teachers, initial and secondary levels (2016, Ministry of Education)&lt;/li>
&lt;/ul>
&lt;h3 id="-goal-5-gender-equality">⚖️ Goal 5: Gender Equality&lt;/h3>
&lt;ul>
&lt;li>Gender parity in education, labor participation, and poverty (2012–2016, INE and UDAPE)&lt;/li>
&lt;li>&lt;em>Note: Data on gender-based violence not available at municipal level&lt;/em>&lt;/li>
&lt;/ul>
&lt;h3 id="-goal-6-clean-water-and-sanitation">💧 Goal 6: Clean Water and Sanitation&lt;/h3>
&lt;ul>
&lt;li>Access to potable water (2012, INE)&lt;/li>
&lt;li>Access to sanitation services (2012, INE)&lt;/li>
&lt;li>Proportion of treated wastewater (2015, Ministry of Environment)&lt;/li>
&lt;/ul>
&lt;h3 id="-goal-7-affordable-and-clean-energy">⚡ Goal 7: Affordable and Clean Energy&lt;/h3>
&lt;ul>
&lt;li>Electricity coverage (2012, INE)&lt;/li>
&lt;li>Per capita electricity consumption (2015, Ministry of Energy)&lt;/li>
&lt;li>Use of clean cooking energy (2015, Ministry of Hydrocarbons)&lt;/li>
&lt;li>CO₂ emissions per capita, energy-related (2015, international satellite data)&lt;/li>
&lt;/ul>
&lt;h3 id="-goal-8-decent-work-and-economic-growth">💼 Goal 8: Decent Work and Economic Growth&lt;/h3>
&lt;ul>
&lt;li>Share of non-functioning electricity meters (proxy for informality/unemployment) (2015, Ministry of Energy)&lt;/li>
&lt;li>Labor force participation rate (2012, INE)&lt;/li>
&lt;li>Youth not in education, employment, or training (NEET rate) (2015, Ministry of Labor)&lt;/li>
&lt;/ul>
&lt;h3 id="-goal-9-industry-innovation-and-infrastructure">🏗️ Goal 9: Industry, Innovation, and Infrastructure&lt;/h3>
&lt;ul>
&lt;li>Internet access in households (2012, INE)&lt;/li>
&lt;li>Mobile signal coverage (2015, telecommunications data)&lt;/li>
&lt;li>Availability of urban infrastructure (2015, Ministry of Public Works)&lt;/li>
&lt;/ul>
&lt;h3 id="-goal-10-reduced-inequality">⚖️ Goal 10: Reduced Inequality&lt;/h3>
&lt;ul>
&lt;li>Proxy measures: municipal differences in poverty and participation rates (2012–2016, INE and UDAPE)&lt;/li>
&lt;/ul>
&lt;h3 id="-goal-11-sustainable-cities-and-communities">🏘️ Goal 11: Sustainable Cities and Communities&lt;/h3>
&lt;ul>
&lt;li>Urban housing adequacy (2012, INE)&lt;/li>
&lt;li>Access to collective transportation (2015, Ministry of Transport)&lt;/li>
&lt;/ul>
&lt;h3 id="-goal-13-climate-action">🌍 Goal 13: Climate Action&lt;/h3>
&lt;ul>
&lt;li>Natural disaster resilience index (2015, Ministry of Environment)&lt;/li>
&lt;li>CO₂ emissions and forest degradation (2015, satellite data)&lt;/li>
&lt;/ul>
&lt;h3 id="-goal-15-life-on-land">🌳 Goal 15: Life on Land&lt;/h3>
&lt;ul>
&lt;li>Deforestation rates (2015, satellite data)&lt;/li>
&lt;li>Biodiversity loss indicators (2015, Ministry of Environment)&lt;/li>
&lt;/ul>
&lt;h3 id="-goal-16-peace-justice-and-strong-institutions">🕊️ Goal 16: Peace, Justice, and Strong Institutions&lt;/h3>
&lt;ul>
&lt;li>Birth registration coverage (2012, INE)&lt;/li>
&lt;li>Crime and homicide rates (2015, Ministry of Government)&lt;/li>
&lt;li>Corruption perceptions (2015, civil society organizations)&lt;/li>
&lt;/ul>
&lt;h3 id="-goal-17-partnerships-for-the-goals">🤝 Goal 17: Partnerships for the Goals&lt;/h3>
&lt;ul>
&lt;li>Municipal fiscal capacity (2015, Ministry of Economy)&lt;/li>
&lt;li>Public investment per capita (2015, Ministry of Economy)&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="-limitations-and-future-work">⚠️ Limitations and Future Work&lt;/h2>
&lt;ul>
&lt;li>No disaggregated data for Indigenous Territories (TIOC)&lt;/li>
&lt;li>Many indicators based on 2012 Census; updates pending&lt;/li>
&lt;li>Limited information for Goals 12 and 14 at municipal level&lt;/li>
&lt;li>No indicators for educational quality (due to lack of standardized testing)&lt;/li>
&lt;li>Gender violence data unavailable at municipal scale&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="-access">🔗 Access&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>Original website&lt;/strong>: &lt;a href="http://atlas.sdsnbolivia.org" target="_blank" rel="noopener">atlas.sdsnbolivia.org&lt;/a>&lt;/li>
&lt;li>&lt;strong>Original Publication&lt;/strong>: &lt;a href="http://www.sdsnbolivia.org/Atlas" target="_blank" rel="noopener">sdsnbolivia.org/Atlas&lt;/a>&lt;/li>
&lt;li>&lt;strong>Source Code of the Web App&lt;/strong>: &lt;a href="https://github.com/cmg777/streamlit_esda101" target="_blank" rel="noopener">github.com/cmg777/streamlit_esda101&lt;/a>&lt;/li>
&lt;li>&lt;strong>Computational Notebook&lt;/strong>: &lt;a href="https://colab.research.google.com/drive/1JHf8wPxSxBdKKhXaKQZUzhEpVznKGiep?usp=sharing" target="_blank" rel="noopener">Google Colab&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Studying spatial heterogeneity</title><link>https://carlos-mendez.org/post/python_gwr_mgwr/</link><pubDate>Sat, 23 Dec 2023 00:00:00 +0000</pubDate><guid>https://carlos-mendez.org/post/python_gwr_mgwr/</guid><description>&lt;h1 id="a-geocomputational-notebook-to-compute-gwr-and-mgwr">&lt;strong>A geocomputational notebook to compute GWR and MGWR&lt;/strong>&lt;/h1>
&lt;p>.&lt;/p></description></item><item><title>Construct and export spatial connectivity structures (W)</title><link>https://carlos-mendez.org/post/python_how_to_build_w/</link><pubDate>Sat, 02 Dec 2023 00:00:00 +0000</pubDate><guid>https://carlos-mendez.org/post/python_how_to_build_w/</guid><description>&lt;p>.&lt;/p></description></item><item><title>Cross-Sectional Spatial Regression in Stata: Crime in Columbus Neighborhoods</title><link>https://carlos-mendez.org/post/stata_sp_regression_cross_section/</link><pubDate>Fri, 01 Dec 2023 00:00:00 +0000</pubDate><guid>https://carlos-mendez.org/post/stata_sp_regression_cross_section/</guid><description>&lt;h2 id="1-overview">1. Overview&lt;/h2>
&lt;p>Crime does not stop at neighborhood boundaries. A neighborhood&amp;rsquo;s crime rate may depend not only on its own socioeconomic conditions but also on conditions in adjacent areas &amp;mdash; through spatial displacement (criminals move to easier targets nearby), diffusion (criminal networks operate across borders), and shared exposure to common risk factors. Standard regression models that treat each neighborhood as an independent observation miss these &lt;strong>spatial spillovers&lt;/strong>, potentially producing biased estimates of how income and housing values affect crime.&lt;/p>
&lt;p>This tutorial introduces the &lt;strong>complete taxonomy of cross-sectional spatial regression models&lt;/strong> &amp;mdash; from a simple OLS baseline through the most general GNS (General Nesting Spatial) specification. Using the classic Columbus crime dataset, we progressively estimate eight models: OLS, SAR, SEM, SLX, SDM, SDEM, SAC, and GNS. Each model captures spatial dependence through a different combination of three channels: the spatial lag of the dependent variable ($\rho Wy$), the spatial lag of the explanatory variables ($WX\theta$), and the spatial lag of the error term ($\lambda Wu$). We use &lt;strong>specification tests&lt;/strong> from the SDM to determine which simpler model the data supports, and compare all models using log-likelihoods and direct/indirect effect decompositions, following Elhorst (2014, Chapter 2).&lt;/p>
&lt;p>The Columbus crime dataset contains 49 neighborhoods in Columbus, Ohio, with data on residential burglaries and vehicle thefts per 1,000 households (CRIME), household income in \$1,000 (INC), and housing value in \$1,000 (HOVAL). The spatial weight matrix is a Queen contiguity matrix &amp;mdash; two neighborhoods are neighbors if they share a common border or vertex &amp;mdash; row-standardized so that the spatial lag of a variable equals the weighted average among a neighborhood&amp;rsquo;s neighbors. All estimation uses Stata&amp;rsquo;s official &lt;code>spregress&lt;/code> command (available since Stata 15), which implements maximum likelihood estimation for the full family of cross-sectional spatial models.&lt;/p>
&lt;blockquote>
&lt;p>Mendez, C. (2021). &lt;em>Spatial econometrics for cross-sectional data in Stata.&lt;/em> DOI: &lt;a href="https://doi.org/10.5281/zenodo.5151076" target="_blank" rel="noopener">10.5281/zenodo.5151076&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;h3 id="learning-objectives">Learning objectives&lt;/h3>
&lt;ul>
&lt;li>Construct and load a Queen contiguity spatial weight matrix in Stata using &lt;code>spmatrix fromdata&lt;/code>&lt;/li>
&lt;li>Compute spatial lags of explanatory variables ($WX$) manually using Mata&lt;/li>
&lt;li>Test for spatial autocorrelation using Moran&amp;rsquo;s I and LM tests&lt;/li>
&lt;li>Estimate the full taxonomy of spatial models (SAR, SEM, SLX, SDM, SDEM, SAC, GNS) using &lt;code>spregress&lt;/code>&lt;/li>
&lt;li>Decompose coefficient estimates into direct, indirect (spillover), and total effects using &lt;code>estat impact&lt;/code>&lt;/li>
&lt;li>Use specification tests to determine whether the SDM simplifies to SAR, SLX, or SEM&lt;/li>
&lt;li>Compare models and identify the SDM and SDEM as preferred specifications following Elhorst (2014)&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="2-the-spatial-model-taxonomy">2. The spatial model taxonomy&lt;/h2>
&lt;p>The eight models in this tutorial form a nested hierarchy. At the top sits the &lt;strong>GNS&lt;/strong> (General Nesting Spatial) model, which includes all three spatial channels simultaneously. Each intermediate model imposes one or more restrictions, and OLS sits at the bottom with no spatial terms at all. Understanding this nesting structure is essential for model selection &amp;mdash; we estimate from the general to the specific, using statistical tests to determine whether restrictions are warranted.&lt;/p>
&lt;pre>&lt;code class="language-mermaid">graph TD
GNS[&amp;quot;&amp;lt;b&amp;gt;GNS&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;y = ρWy + Xβ + WXθ + u&amp;lt;br/&amp;gt;u = λWu + ε&amp;lt;br/&amp;gt;&amp;lt;i&amp;gt;Most general&amp;lt;/i&amp;gt;&amp;quot;]
SDM[&amp;quot;&amp;lt;b&amp;gt;SDM&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;y = ρWy + Xβ + WXθ + ε&amp;lt;br/&amp;gt;&amp;lt;i&amp;gt;λ = 0&amp;lt;/i&amp;gt;&amp;quot;]
SDEM[&amp;quot;&amp;lt;b&amp;gt;SDEM&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;y = Xβ + WXθ + u&amp;lt;br/&amp;gt;u = λWu + ε&amp;lt;br/&amp;gt;&amp;lt;i&amp;gt;ρ = 0&amp;lt;/i&amp;gt;&amp;quot;]
SAC[&amp;quot;&amp;lt;b&amp;gt;SAC&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;y = ρWy + Xβ + u&amp;lt;br/&amp;gt;u = λWu + ε&amp;lt;br/&amp;gt;&amp;lt;i&amp;gt;θ = 0&amp;lt;/i&amp;gt;&amp;quot;]
SAR[&amp;quot;&amp;lt;b&amp;gt;SAR&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;y = ρWy + Xβ + ε&amp;lt;br/&amp;gt;&amp;lt;i&amp;gt;λ = 0, θ = 0&amp;lt;/i&amp;gt;&amp;quot;]
SEM[&amp;quot;&amp;lt;b&amp;gt;SEM&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;y = Xβ + u&amp;lt;br/&amp;gt;u = λWu + ε&amp;lt;br/&amp;gt;&amp;lt;i&amp;gt;ρ = 0, θ = 0&amp;lt;/i&amp;gt;&amp;quot;]
SLX[&amp;quot;&amp;lt;b&amp;gt;SLX&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;y = Xβ + WXθ + ε&amp;lt;br/&amp;gt;&amp;lt;i&amp;gt;ρ = 0, λ = 0&amp;lt;/i&amp;gt;&amp;quot;]
OLS[&amp;quot;&amp;lt;b&amp;gt;OLS&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;y = Xβ + ε&amp;lt;br/&amp;gt;&amp;lt;i&amp;gt;ρ = 0, θ = 0, λ = 0&amp;lt;/i&amp;gt;&amp;quot;]
GNS --&amp;gt; SDM
GNS --&amp;gt; SDEM
GNS --&amp;gt; SAC
SDM --&amp;gt; SAR
SDM --&amp;gt; SLX
SDEM --&amp;gt; SLX
SDEM --&amp;gt; SEM
SAC --&amp;gt; SAR
SAC --&amp;gt; SEM
SAR --&amp;gt; OLS
SEM --&amp;gt; OLS
SLX --&amp;gt; OLS
style GNS fill:#141413,stroke:#d97757,color:#fff
style SDM fill:#00d4c8,stroke:#141413,color:#141413
style SDEM fill:#6a9bcc,stroke:#141413,color:#fff
style SAC fill:#6a9bcc,stroke:#141413,color:#fff
style SAR fill:#d97757,stroke:#141413,color:#fff
style SEM fill:#d97757,stroke:#141413,color:#fff
style SLX fill:#d97757,stroke:#141413,color:#fff
style OLS fill:#141413,stroke:#6a9bcc,color:#fff
&lt;/code>&lt;/pre>
&lt;p>The diagram shows three spatial channels and their corresponding parameters: $\rho$ (spatial lag of $y$), $\theta$ (spatial lag of $X$), and $\lambda$ (spatial lag of the error). Setting any of these to zero yields a nested model. The SDM is often the starting point for model selection because it nests the three most common models &amp;mdash; SAR, SLX, and SEM &amp;mdash; and the restrictions can be tested with standard Wald tests.&lt;/p>
&lt;hr>
&lt;h2 id="3-setup-and-data-loading">3. Setup and data loading&lt;/h2>
&lt;p>Before running any spatial models, we need the &lt;code>estout&lt;/code> package for table output and the &lt;code>spatwmat&lt;/code>/&lt;code>spatdiag&lt;/code> packages for LM diagnostic tests. If you have not installed them, uncomment the &lt;code>ssc install&lt;/code> and &lt;code>net install&lt;/code> lines below.&lt;/p>
&lt;pre>&lt;code class="language-stata">clear all
macro drop _all
set more off
* Install packages (uncomment if needed)
*ssc install estout, replace
*net install st0085_2, from(http://www.stata-journal.com/software/sj14-2)
&lt;/code>&lt;/pre>
&lt;h3 id="31-spatial-weight-matrix">3.1 Spatial weight matrix&lt;/h3>
&lt;p>The spatial weight matrix &lt;strong>W&lt;/strong> defines the neighborhood structure among the 49 Columbus neighborhoods. We use a Queen contiguity matrix where two neighborhoods are neighbors if they share a common border or vertex. The matrix is stored in a &lt;code>.dta&lt;/code> file and converted to an &lt;code>spmatrix&lt;/code> object with row-standardization &amp;mdash; meaning that each row sums to one, so the spatial lag of a variable equals the &lt;strong>weighted average&lt;/strong> among a neighborhood&amp;rsquo;s neighbors.&lt;/p>
&lt;pre>&lt;code class="language-stata">* Load Queen contiguity W matrix
use &amp;quot;https://github.com/quarcs-lab/data-open/raw/master/Columbus/columbus/Wqueen_fromStata_spmat.dta&amp;quot;, clear
gen id = _n
order id, first
spset id
spmatrix fromdata W = v*, normalize(row) replace
spmatrix summarize W
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Spatial-weighting matrix W
Dimensions: 49 x 49
Stored type: dense
Normalization: row
Summary statistics
-------------------------------------------
Min Mean Max N
-------------------------------------------
Nonzero .0625 .2049 .5000 236
All .0000 .0042 .5000 2401
-------------------------------------------
&lt;/code>&lt;/pre>
&lt;p>The &lt;code>spmatrix fromdata&lt;/code> command reads the columns of the loaded dataset and stores them as a spatial weight matrix object named &lt;code>W&lt;/code>. The &lt;code>normalize(row)&lt;/code> option applies row-standardization, and &lt;code>replace&lt;/code> overwrites any existing matrix with the same name. The matrix has 236 nonzero entries out of 2,401 total cells, meaning the average neighborhood has approximately $236 / 49 \approx 4.8$ neighbors.&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>Note:&lt;/strong> The companion &lt;code>analysis.do&lt;/code> file uses the longer name &lt;code>WqueenS_fromStata15&lt;/code> for the spatial weight matrix to match the original Colab notebook. In this tutorial, we use the shorter name &lt;code>W&lt;/code> for readability. Both names are interchangeable &amp;mdash; only the name passed to &lt;code>spmatrix fromdata&lt;/code> matters.&lt;/p>
&lt;/blockquote>
&lt;h3 id="32-generating-spatial-lags-of-x">3.2 Generating spatial lags of X&lt;/h3>
&lt;p>Before loading the crime data, we pre-compute the spatial lags of the explanatory variables ($W \cdot INC$ and $W \cdot HOVAL$) using Mata. These spatial lags represent each neighborhood&amp;rsquo;s &lt;strong>neighbors' average&lt;/strong> income and housing value, and will be used as explicit regressors in the SLX, SDM, SDEM, and GNS models.&lt;/p>
&lt;pre>&lt;code class="language-stata">* Load data and generate spatial lags of X manually
use &amp;quot;https://github.com/quarcs-lab/data-open/raw/master/Columbus/columbus/columbusDbase.dta&amp;quot;, clear
spset id
label var CRIME &amp;quot;Crime&amp;quot;
label var INC &amp;quot;Income&amp;quot;
label var HOVAL &amp;quot;House value&amp;quot;
* Compute W*X using Mata (bypasses spregress ivarlag)
mata: spmatrix_matafromsp(W_mata, id_vec, &amp;quot;W&amp;quot;)
mata: st_view(inc=., ., &amp;quot;INC&amp;quot;)
mata: st_view(hoval=., ., &amp;quot;HOVAL&amp;quot;)
gen double W_INC = .
gen double W_HOVAL = .
mata: st_store(., &amp;quot;W_INC&amp;quot;, W_mata * inc)
mata: st_store(., &amp;quot;W_HOVAL&amp;quot;, W_mata * hoval)
label var W_INC &amp;quot;W * Income&amp;quot;
label var W_HOVAL &amp;quot;W * House value&amp;quot;
&lt;/code>&lt;/pre>
&lt;blockquote>
&lt;p>&lt;strong>Why compute W*X manually?&lt;/strong> Stata&amp;rsquo;s &lt;code>spregress&lt;/code> command provides the &lt;code>ivarlag()&lt;/code> option to include spatial lags of explanatory variables. However, this option may produce incorrect coefficient signs in some Stata versions. Computing $WX$ explicitly using Mata and including the result as a regular regressor is more transparent and produces results consistent with Elhorst (2014) and PySAL&amp;rsquo;s &lt;code>spreg&lt;/code> package.&lt;/p>
&lt;/blockquote>
&lt;h3 id="33-summary-statistics">3.3 Summary statistics&lt;/h3>
&lt;pre>&lt;code class="language-stata">summarize CRIME INC HOVAL
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text"> Variable | Obs Mean Std. dev. Min Max
-------------+---------------------------------------------------------
CRIME | 49 35.1288 16.5647 .1783 68.8920
INC | 49 14.3765 5.7575 3.7240 27.8966
HOVAL | 49 38.4362 18.4661 5.0000 96.4000
&lt;/code>&lt;/pre>
&lt;h3 id="34-variables">3.4 Variables&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Variable&lt;/th>
&lt;th>Description&lt;/th>
&lt;th>Mean&lt;/th>
&lt;th>Std. Dev.&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>CRIME&lt;/code>&lt;/td>
&lt;td>Residential burglaries and vehicle thefts per 1,000 households&lt;/td>
&lt;td>35.13&lt;/td>
&lt;td>16.56&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>INC&lt;/code>&lt;/td>
&lt;td>Household income (\$1,000)&lt;/td>
&lt;td>14.38&lt;/td>
&lt;td>5.76&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>HOVAL&lt;/code>&lt;/td>
&lt;td>Housing value (\$1,000)&lt;/td>
&lt;td>38.44&lt;/td>
&lt;td>18.47&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Mean crime is 35.13 incidents per 1,000 households, with substantial variation across neighborhoods (standard deviation of 16.56, ranging from near zero to 68.89). Mean household income is \$14,380 and mean housing value is \$38,440. The wide range of both income (\$3,724 to \$27,897) and housing value (\$5,000 to \$96,400) reflects the considerable socioeconomic heterogeneity across Columbus neighborhoods, providing sufficient variation to estimate the effects of these variables on crime.&lt;/p>
&lt;hr>
&lt;h2 id="4-ols-baseline-and-spatial-diagnostics">4. OLS baseline and spatial diagnostics&lt;/h2>
&lt;h3 id="41-ols-regression">4.1 OLS regression&lt;/h3>
&lt;p>Before introducing any spatial structure, we estimate a standard OLS regression of crime on income and housing value. This provides a non-spatial benchmark against which all subsequent models will be compared.&lt;/p>
&lt;pre>&lt;code class="language-stata">regress CRIME INC HOVAL
eststo OLS
estat ic
mat s = r(S)
quietly estadd scalar AIC = s[1,5]
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text"> Source | SS df MS Number of obs = 49
-------------+---------------------------------- F(2, 46) = 28.39
Model | 5765.1588 2 2882.5794 Prob &amp;gt; F = 0.0000
Residual | 4670.9753 46 101.5429 R-squared = 0.5524
-------------+---------------------------------- Adj R-squared = 0.5330
Total | 10436.1341 48 217.4194 Root MSE = 10.0769
------------------------------------------------------------------------------
CRIME | Coefficient Std. err. t P&amp;gt;|t| [95% conf. interval]
-------------+----------------------------------------------------------------
INC | -1.5973 .3341 -4.78 0.000 -2.2699 -.9247
HOVAL | -0.2739 .1032 -2.65 0.011 -0.4817 -.0661
_cons | 68.6190 4.7355 14.49 0.000 59.0876 78.1504
------------------------------------------------------------------------------
&lt;/code>&lt;/pre>
&lt;p>OLS estimates that each additional \$1,000 in household income is associated with a reduction of &lt;strong>1.60 crimes&lt;/strong> per 1,000 households, and each additional \$1,000 in housing value is associated with a reduction of &lt;strong>0.27 crimes&lt;/strong>. Both coefficients are statistically significant, and the model explains about &lt;strong>55%&lt;/strong> of the variation in crime rates across neighborhoods (R-squared = 0.552). The intercept of 68.62 represents the predicted crime rate for a hypothetical neighborhood with zero income and zero housing value. However, OLS assumes that crime in one neighborhood is independent of conditions in adjacent neighborhoods &amp;mdash; an assumption we now test directly.&lt;/p>
&lt;h3 id="42-morans-i-test">4.2 Moran&amp;rsquo;s I test&lt;/h3>
&lt;p>Moran&amp;rsquo;s I is the most widely used test for spatial autocorrelation. Applied to OLS residuals, it tests whether the residuals in nearby neighborhoods are more similar (positive spatial autocorrelation) or more dissimilar (negative spatial autocorrelation) than expected under spatial independence. The test statistic is:&lt;/p>
&lt;p>$$I = \frac{N}{S_0} \cdot \frac{e' W e}{e' e}$$&lt;/p>
&lt;p>where $e$ is the vector of OLS residuals, $W$ is the row-standardized spatial weight matrix, $N$ is the number of observations, and $S_0$ is the sum of all elements of $W$. Under the null hypothesis of no spatial autocorrelation, $I$ follows an approximately standard normal distribution after standardization.&lt;/p>
&lt;pre>&lt;code class="language-stata">regress CRIME INC HOVAL
estat moran, errorlag(W)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Moran test for spatial autocorrelation in the error
H0: Error is i.i.d.
I = 0.2222
E(I) = -0.0208
Mean = -0.0208
Sd(I) = 0.0856
z = 2.8391
p-value = 0.0045
&lt;/code>&lt;/pre>
&lt;p>Moran&amp;rsquo;s I is &lt;strong>0.222&lt;/strong> with a z-statistic of &lt;strong>2.84&lt;/strong> (p = 0.005), providing strong evidence of &lt;strong>positive spatial autocorrelation&lt;/strong> in the OLS residuals. Neighborhoods with high unexplained crime tend to cluster near other neighborhoods with high unexplained crime, and vice versa. This violates the OLS assumption of independent errors and motivates the use of spatial regression models. The positive sign of Moran&amp;rsquo;s I is consistent with crime diffusion &amp;mdash; criminal activity in one neighborhood spills over into adjacent areas.&lt;/p>
&lt;h3 id="43-lm-tests-for-spatial-specification">4.3 LM tests for spatial specification&lt;/h3>
&lt;p>While Moran&amp;rsquo;s I confirms the presence of spatial autocorrelation, it does not indicate the &lt;strong>form&lt;/strong> of the spatial dependence. The Lagrange Multiplier (LM) tests proposed by Anselin (1988) test separately for the spatial lag ($\rho Wy$) and spatial error ($\lambda Wu$) specifications. The robust versions of these tests remain valid even when the alternative specification is also present.&lt;/p>
&lt;pre>&lt;code class="language-stata">* Create compatible W matrix for spatdiag
spatwmat using &amp;quot;https://github.com/quarcs-lab/data-open/raw/master/Columbus/columbus/Wqueen_fromStata_spmat.dta&amp;quot;, ///
name(Wcompat) eigenval(eWcompat) standardize
quietly regress CRIME INC HOVAL
spatdiag, weights(Wcompat)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Spatial error:
Moran's I = 0.2055 Prob = 0.0068
Lagrange multiplier = 5.3282 Prob = 0.0210
Robust LM = 2.1901 Prob = 0.1389
Spatial lag:
Lagrange multiplier = 3.3954 Prob = 0.0654
Robust LM = 0.2572 Prob = 0.6121
&lt;/code>&lt;/pre>
&lt;p>The standard LM test for the spatial error ($\lambda$) is significant at the 5% level (LM = &lt;strong>5.33&lt;/strong>, p = 0.021), while the standard LM test for the spatial lag ($\rho$) is marginally significant at the 10% level (LM = &lt;strong>3.40&lt;/strong>, p = 0.065). The robust tests provide further guidance: the robust LM-error is &lt;strong>2.19&lt;/strong> (p = 0.139) and the robust LM-lag is only &lt;strong>0.26&lt;/strong> (p = 0.612).&lt;/p>
&lt;p>Following the Anselin (2005) decision rule &amp;mdash; compare the standard LM tests first, then use the robust tests to break ties &amp;mdash; the evidence favors the &lt;strong>SEM&lt;/strong> specification. The standard LM-error is larger and more significant than the standard LM-lag, and the robust LM-error remains larger than the robust LM-lag. The decision tree below summarizes this logic. However, as we will see, the full model taxonomy reveals a more nuanced picture.&lt;/p>
&lt;pre>&lt;code class="language-mermaid">graph TD
MI[&amp;quot;&amp;lt;b&amp;gt;Moran's I&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;I = 0.222, p = 0.005&amp;lt;br/&amp;gt;Significant&amp;quot;]
LM[&amp;quot;&amp;lt;b&amp;gt;Standard LM Tests&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;LM-error = 5.33 (p = 0.021)&amp;lt;br/&amp;gt;LM-lag = 3.40 (p = 0.065)&amp;quot;]
RLM[&amp;quot;&amp;lt;b&amp;gt;Robust LM Tests&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;Robust LM-error = 2.19&amp;lt;br/&amp;gt;Robust LM-lag = 0.26&amp;quot;]
SEM_d[&amp;quot;&amp;lt;b&amp;gt;SEM Preferred&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;Error specification&amp;lt;br/&amp;gt;dominates&amp;quot;]
MI --&amp;gt;|&amp;quot;Spatial dependence?&amp;quot;| LM
LM --&amp;gt;|&amp;quot;Both significant?&amp;quot;| RLM
RLM --&amp;gt;|&amp;quot;Error &amp;gt; Lag&amp;quot;| SEM_d
style MI fill:#6a9bcc,stroke:#141413,color:#fff
style LM fill:#d97757,stroke:#141413,color:#fff
style RLM fill:#00d4c8,stroke:#141413,color:#141413
style SEM_d fill:#141413,stroke:#d97757,color:#fff
&lt;/code>&lt;/pre>
&lt;hr>
&lt;h2 id="5-first-generation-spatial-models">5. First-generation spatial models&lt;/h2>
&lt;h3 id="51-sar-spatial-autoregressive--spatial-lag">5.1 SAR (Spatial Autoregressive / Spatial Lag)&lt;/h3>
&lt;p>The SAR model adds a spatial lag of the dependent variable to the OLS specification. It assumes that crime in a neighborhood depends directly on the crime rate in adjacent neighborhoods &amp;mdash; a &amp;ldquo;contagion&amp;rdquo; or &amp;ldquo;diffusion&amp;rdquo; channel where high crime in one area breeds crime in neighboring areas.&lt;/p>
&lt;p>$$y = \rho W y + X \beta + \varepsilon$$&lt;/p>
&lt;p>The parameter $\rho$ measures the strength of this spatial feedback. Because $Wy$ is endogenous (it depends on $y$, which depends on $\varepsilon$), OLS estimation would be inconsistent. We use maximum likelihood estimation via &lt;code>spregress&lt;/code>.&lt;/p>
&lt;pre>&lt;code class="language-stata">spregress CRIME INC HOVAL, ml dvarlag(W)
eststo SAR
estat ic
mat s = r(S)
quietly estadd scalar AIC = s[1,5]
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Spatial autoregressive model Number of obs = 49
Maximum likelihood estimates Wald chi2(2) = 54.83
Prob &amp;gt; chi2 = 0.0000
Log-likelihood = -184.926 Pseudo R2 = 0.5830
------------------------------------------------------------------------------
CRIME | Coefficient Std. err. z P&amp;gt;|z| [95% conf. interval]
-------------+----------------------------------------------------------------
CRIME |
INC | -1.0312 .3359 -3.07 0.002 -1.6897 -.3728
HOVAL | -0.2654 .0922 -2.88 0.004 -0.4461 -.0847
_cons | 45.0719 7.8406 5.75 0.000 29.7046 60.4392
-------------+----------------------------------------------------------------
W |
CRIME | 0.4283 .1228 3.49 0.000 0.1875 0.6690
------------------------------------------------------------------------------
&lt;/code>&lt;/pre>
&lt;p>The spatial autoregressive parameter $\rho$ is &lt;strong>0.428&lt;/strong> (z = 3.49, p &amp;lt; 0.001), indicating substantial positive spatial dependence. After accounting for the spatial lag, the own income coefficient drops to &lt;strong>-1.03&lt;/strong> (from -1.60 in OLS), while the housing value coefficient remains similar at &lt;strong>-0.27&lt;/strong>. The reduction in the income coefficient suggests that part of what OLS attributed to income was actually capturing spatial spillover effects that are now absorbed by $\rho$.&lt;/p>
&lt;p>However, the raw coefficients in the SAR model do not have the same interpretation as OLS coefficients because the spatial lag creates a &lt;strong>feedback loop&lt;/strong>: a change in income in one neighborhood affects its crime, which affects its neighbors' crime, which feeds back to the original neighborhood. The proper interpretation requires decomposing effects into direct, indirect, and total components.&lt;/p>
&lt;pre>&lt;code class="language-stata">estat impact
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text"> Coefficient Std. err. z P&amp;gt;|z|
-------------------------------------------------------------------
INC
Direct | -1.1024 .3486 -3.16 0.002
Indirect | -0.7594 .3712 -2.05 0.041
Total | -1.8618 .5803 -3.21 0.001
-------------------------------------------------------------------
HOVAL
Direct | -0.2838 .0983 -2.89 0.004
Indirect | -0.1954 .1123 -1.74 0.082
Total | -0.4792 .1722 -2.78 0.005
-------------------------------------------------------------------
&lt;/code>&lt;/pre>
&lt;p>The &lt;strong>direct effect&lt;/strong> of income is -1.10, meaning that a \$1,000 increase in a neighborhood&amp;rsquo;s own income reduces its crime by 1.10 incidents per 1,000 households. The &lt;strong>indirect (spillover) effect&lt;/strong> is -0.76 and statistically significant (p = 0.041), meaning that when all neighboring neighborhoods experience a \$1,000 income increase, the focal neighborhood&amp;rsquo;s crime drops by an additional 0.76 incidents through the spatial feedback channel. The &lt;strong>total effect&lt;/strong> of income is -1.86, larger than the OLS estimate of -1.60, revealing that OLS understates the total impact of income on crime. However, a key limitation of the SAR is that the ratio between the indirect and direct effect is the same for every variable ($\delta / (1 - \delta) \approx 0.75$), which may be overly restrictive.&lt;/p>
&lt;h3 id="52-sem-spatial-error-model">5.2 SEM (Spatial Error Model)&lt;/h3>
&lt;p>The SEM assumes that spatial dependence operates through the error term rather than through a direct contagion channel. Spatially correlated unobservable factors &amp;mdash; such as local policing strategies, community organizations, or land use patterns &amp;mdash; generate correlated residuals across adjacent neighborhoods.&lt;/p>
&lt;p>$$y = X \beta + u, \quad u = \lambda W u + \varepsilon$$&lt;/p>
&lt;p>The parameter $\lambda$ measures the degree of spatial autocorrelation in the error term. Unlike the SAR, the SEM does not produce indirect (spillover) effects &amp;mdash; the spatial dependence is treated as a nuisance rather than a substantive economic channel.&lt;/p>
&lt;pre>&lt;code class="language-stata">spregress CRIME INC HOVAL, ml errorlag(W)
eststo SEM
estat ic
mat s = r(S)
quietly estadd scalar AIC = s[1,5]
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Spatial error model Number of obs = 49
Maximum likelihood estimates Wald chi2(2) = 50.51
Prob &amp;gt; chi2 = 0.0000
Log-likelihood = -184.379 Pseudo R2 = 0.5877
------------------------------------------------------------------------------
CRIME | Coefficient Std. err. z P&amp;gt;|z| [95% conf. interval]
-------------+----------------------------------------------------------------
CRIME |
INC | -0.9376 .3393 -2.76 0.006 -1.6027 -.2726
HOVAL | -0.3023 .0909 -3.32 0.001 -0.4805 -.1241
_cons | 59.6228 5.4722 10.90 0.000 48.8975 70.3481
-------------+----------------------------------------------------------------
W |
lambda | 0.5623 .1330 4.23 0.000 0.3017 0.8230
------------------------------------------------------------------------------
&lt;/code>&lt;/pre>
&lt;p>The spatial error parameter $\lambda$ is &lt;strong>0.562&lt;/strong> (z = 4.23, p &amp;lt; 0.001), confirming substantial spatial autocorrelation in the unobservables. The income coefficient is &lt;strong>-0.94&lt;/strong>, further attenuated from the OLS estimate, and the housing value coefficient is &lt;strong>-0.30&lt;/strong>, slightly larger in magnitude than OLS. The log-likelihood of -184.38 is higher than OLS (-187.38), confirming the spatial error structure improves fit.&lt;/p>
&lt;pre>&lt;code class="language-stata">estat impact
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text"> Coefficient Std. err. z P&amp;gt;|z|
-------------------------------------------------------------------
INC
Direct | -0.9376 .3393 -2.76 0.006
Indirect | 0.0000 . . .
Total | -0.9376 .3393 -2.76 0.006
-------------------------------------------------------------------
HOVAL
Direct | -0.3023 .0909 -3.32 0.001
Indirect | 0.0000 . . .
Total | -0.3023 .0909 -3.32 0.001
-------------------------------------------------------------------
&lt;/code>&lt;/pre>
&lt;p>As expected, the SEM produces &lt;strong>zero indirect effects&lt;/strong> by construction. In the SEM, spatial dependence is a nuisance in the error term, not a substantive spillover channel. The direct and total effects are identical. If one believes that crime spillovers are substantively important &amp;mdash; for example, through displacement or diffusion &amp;mdash; the SEM&amp;rsquo;s assumption that all spatial dependence is in the errors is overly restrictive. As we will see in Sections 6 and 8, models that include $WX\theta$ terms reveal a significant negative spillover of neighbors' income on crime, which the SEM cannot detect.&lt;/p>
&lt;hr>
&lt;h2 id="6-models-with-spatial-lags-of-x">6. Models with spatial lags of X&lt;/h2>
&lt;h3 id="61-slx-spatial-lag-of-x">6.1 SLX (Spatial Lag of X)&lt;/h3>
&lt;p>The SLX model includes spatial lags of the explanatory variables but no spatial lag of $y$ and no spatial error. It captures &lt;strong>local spillovers&lt;/strong> &amp;mdash; the idea that a neighborhood&amp;rsquo;s crime depends on its neighbors' income and housing values &amp;mdash; without the global feedback mechanism of the SAR.&lt;/p>
&lt;p>$$y = X \beta + W X \theta + \varepsilon$$&lt;/p>
&lt;p>The $\theta$ coefficients measure the direct impact of neighbors' characteristics on the focal neighborhood&amp;rsquo;s crime. Unlike the SAR, the SLX does not generate a spatial multiplier &amp;mdash; the spillover effects are localized to immediate neighbors. Since the SLX has no spatial autoregressive or error component, it can be estimated by OLS with the pre-computed $W \cdot INC$ and $W \cdot HOVAL$ variables as additional regressors.&lt;/p>
&lt;pre>&lt;code class="language-stata">regress CRIME INC HOVAL W_INC W_HOVAL
eststo SLX
estat ic
mat s = r(S)
quietly estadd scalar AIC = s[1,5]
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text"> Source | SS df MS Number of obs = 49
-------------+---------------------------------- F(4, 44) = 17.24
Model | 6373.4060 4 1593.35150 Prob &amp;gt; F = 0.0000
Residual | 4062.7281 44 92.33473 R-squared = 0.6105
-------------+---------------------------------- Adj R-squared = 0.5751
Total | 10436.1341 48 217.4194 Root MSE = 9.6090
------------------------------------------------------------------------------
CRIME | Coefficient Std. err. t P&amp;gt;|t| [95% conf. interval]
-------------+----------------------------------------------------------------
INC | -1.0974 .3738 -2.94 0.005 -1.8509 -.3438
HOVAL | -0.2944 .1017 -2.90 0.006 -0.4993 -.0895
W_INC | -1.3987 .5601 -2.50 0.016 -2.5275 -.2700
W_HOVAL | 0.2148 .2079 1.03 0.307 -0.2045 0.6342
_cons | 74.5534 6.7156 11.10 0.000 61.0167 88.0901
------------------------------------------------------------------------------
&lt;/code>&lt;/pre>
&lt;p>The spatial lag of income ($W \cdot INC$) is &lt;strong>-1.40&lt;/strong> and statistically significant (t = -2.50, p = 0.016), meaning that higher average income among a neighborhood&amp;rsquo;s neighbors is associated with &lt;strong>lower&lt;/strong> crime in the focal neighborhood. This is economically intuitive: neighborhoods surrounded by wealthier areas benefit from reduced crime, possibly through better public services, lower criminal opportunity, or social spillovers. The spatial lag of housing value ($W \cdot HOVAL$) is &lt;strong>+0.21&lt;/strong> but statistically insignificant (p = 0.307). The own-variable coefficients are INC at &lt;strong>-1.10&lt;/strong> and HOVAL at &lt;strong>-0.29&lt;/strong>, both highly significant. The log-likelihood of -184.0 is higher than OLS (-187.4), and the LR-test of the SLX versus OLS is 6.8 with 2 df (critical value 5.99), meaning the OLS model needs to be rejected in favor of the SLX.&lt;/p>
&lt;p>The direct and indirect effects in the SLX correspond directly to $\beta$ and $\theta$ because there is no spatial multiplier:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;/th>
&lt;th>Direct&lt;/th>
&lt;th>Indirect&lt;/th>
&lt;th>Total&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>INC&lt;/strong>&lt;/td>
&lt;td>-1.10***&lt;/td>
&lt;td>-1.40**&lt;/td>
&lt;td>-2.50***&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>HOVAL&lt;/strong>&lt;/td>
&lt;td>-0.29***&lt;/td>
&lt;td>+0.21&lt;/td>
&lt;td>-0.08&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The total effect of income is &lt;strong>-2.50&lt;/strong>, much larger than the OLS estimate of -1.60, revealing that a substantial portion of the income effect operates through the neighbors' income channel. For housing value, the positive but insignificant indirect effect partially offsets the negative direct effect, suggesting that the crime-reducing effect of housing value is primarily a within-neighborhood phenomenon.&lt;/p>
&lt;h3 id="62-sdm-spatial-durbin-model">6.2 SDM (Spatial Durbin Model)&lt;/h3>
&lt;p>The SDM combines the spatial lag of $y$ from the SAR with the spatial lags of $X$ from the SLX. It is the most popular &amp;ldquo;general purpose&amp;rdquo; spatial model because it nests SAR, SLX, and SEM as special cases, enabling formal specification testing.&lt;/p>
&lt;p>$$y = \rho W y + X \beta + W X \theta + \varepsilon$$&lt;/p>
&lt;p>The SDM captures spillovers through two channels: a &lt;strong>global feedback&lt;/strong> channel ($\rho Wy$, where shocks propagate through the entire network) and a &lt;strong>local&lt;/strong> channel ($WX\theta$, where neighbors' characteristics directly affect local outcomes). We include $W \cdot INC$ and $W \cdot HOVAL$ as regular regressors alongside the spatial lag of crime.&lt;/p>
&lt;pre>&lt;code class="language-stata">spregress CRIME INC HOVAL W_INC W_HOVAL, ml dvarlag(W)
eststo SDM
estat ic
mat s = r(S)
quietly estadd scalar AIC = s[1,5]
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Spatial Durbin model Number of obs = 49
Maximum likelihood estimates Wald chi2(4) = 56.79
Prob &amp;gt; chi2 = 0.0000
Log-likelihood = -181.639 Pseudo R2 = 0.6037
------------------------------------------------------------------------------
CRIME | Coefficient Std. err. z P&amp;gt;|z| [95% conf. interval]
-------------+----------------------------------------------------------------
CRIME |
INC | -0.9199 .3347 -2.75 0.006 -1.5758 -.2639
HOVAL | -0.2971 .0904 -3.29 0.001 -0.4742 -.1200
W_INC | -0.5839 .5742 -1.02 0.309 -1.7094 0.5415
W_HOVAL | 0.2577 .1872 1.38 0.169 -0.1092 0.6247
-------------+----------------------------------------------------------------
W |
CRIME | 0.4035 .1613 2.50 0.012 0.0873 0.7197
_cons | 44.3200 13.0455 3.40 0.001 18.7512 69.8888
------------------------------------------------------------------------------
&lt;/code>&lt;/pre>
&lt;p>The spatial autoregressive parameter $\rho$ is &lt;strong>0.404&lt;/strong> (z = 2.50, p = 0.012), close to the SAR estimate. The own income coefficient is &lt;strong>-0.92&lt;/strong> and housing value is &lt;strong>-0.30&lt;/strong>. The spatial lag of income ($W \cdot INC = -0.58$) is negative but individually insignificant (p = 0.309), while the spatial lag of housing value ($W \cdot HOVAL = +0.26$) is positive and also insignificant (p = 0.169). Although the $\theta$ terms are individually insignificant, their joint significance is tested formally via the specification tests in Section 7.&lt;/p>
&lt;pre>&lt;code class="language-stata">estat impact
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text"> Coefficient Std. err. z P&amp;gt;|z|
-------------------------------------------------------------------
INC
Direct | -1.0250 .3350 -3.06 0.002
Indirect | -1.4959 .8060 -1.86 0.064
Total | -2.5209 .8820 -2.86 0.004
-------------------------------------------------------------------
HOVAL
Direct | -0.2820 .0900 -3.13 0.002
Indirect | 0.2158 .2990 0.72 0.470
Total | -0.0661 .3050 -0.22 0.828
-------------------------------------------------------------------
&lt;/code>&lt;/pre>
&lt;p>The direct effect of income is &lt;strong>-1.03&lt;/strong>, similar to the SAR. The indirect (spillover) effect of income is &lt;strong>-1.50&lt;/strong> and marginally significant (p = 0.064), much larger than in the SAR (-0.76), because the SDM accounts for both the spatial feedback channel ($\rho$) and the direct effect of neighbors' income ($\theta_{INC}$). The total effect of income is &lt;strong>-2.52&lt;/strong>, substantially larger than the SAR&amp;rsquo;s -1.86. For housing value, the indirect effect is &lt;strong>+0.22&lt;/strong> (insignificant), suggesting that neighbors' housing values do not generate meaningful crime spillovers once the global feedback is accounted for.&lt;/p>
&lt;hr>
&lt;h2 id="7-specification-tests-from-sdm">7. Specification tests from SDM&lt;/h2>
&lt;p>The SDM nests SAR, SLX, and SEM as special cases. Before accepting the full SDM, we test whether the data supports simplifying to one of these more parsimonious specifications. We re-estimate the SDM and apply three tests. We use both &lt;strong>Wald tests&lt;/strong> (from the Stata estimation) and &lt;strong>LR tests&lt;/strong> (comparing log-likelihoods across models), following Elhorst (2014, Section 2.9).&lt;/p>
&lt;pre>&lt;code class="language-stata">quietly spregress CRIME INC HOVAL W_INC W_HOVAL, ml dvarlag(W)
&lt;/code>&lt;/pre>
&lt;h3 id="71-reduce-to-slx-test-rho--0">7.1 Reduce to SLX? (test $\rho = 0$)&lt;/h3>
&lt;p>The SLX model restricts $\rho = 0$ &amp;mdash; there is no spatial autoregressive feedback. Under SLX, neighbors' characteristics affect local crime directly, but there is no contagion through the spatial lag of crime itself.&lt;/p>
&lt;pre>&lt;code class="language-stata">* Wald test: Reduce to SLX? (NO if p &amp;lt; 0.05)
test ([W]CRIME = 0)
&lt;/code>&lt;/pre>
&lt;p>The test &lt;strong>rejects&lt;/strong> the SLX restriction at the 1% level. The spatial autoregressive parameter $\rho$ is significantly different from zero, meaning that the global feedback channel is an important feature of the data. The LR test confirms this: $-2(\text{LogL}_{SLX} - \text{LogL}_{SDM}) \approx 7.4$ with 1 df (critical value 3.84). Dropping $\rho$ would misspecify the model.&lt;/p>
&lt;h3 id="72-reduce-to-sar-test-theta--0">7.2 Reduce to SAR? (test $\theta = 0$)&lt;/h3>
&lt;p>The SAR model restricts $\theta = 0$ &amp;mdash; the spatial lags of the explanatory variables are zero. Under SAR, only neighbors' crime levels matter, not their incomes or housing values directly.&lt;/p>
&lt;pre>&lt;code class="language-stata">* Wald test: Reduce to SAR? (NO if p &amp;lt; 0.05)
test ([CRIME]W_INC = 0) ([CRIME]W_HOVAL = 0)
&lt;/code>&lt;/pre>
&lt;p>The test &lt;strong>fails to reject&lt;/strong> the SAR restriction. The spatial lags of income and housing value are jointly insignificant, suggesting that the SAR specification may be adequate. The LR test also fails to reject: $-2(\text{LogL}_{SAR} - \text{LogL}_{SDM}) \approx 2.0$ with 2 df (critical value 5.99). However, this does not mean the $\theta$ terms are unimportant &amp;mdash; it may simply reflect insufficient power with only 49 observations.&lt;/p>
&lt;h3 id="73-reduce-to-sem-common-factor-restriction">7.3 Reduce to SEM? (common factor restriction)&lt;/h3>
&lt;p>The SEM imposes the common factor restriction $\theta + \rho \beta = 0$. Under this restriction, the apparent spatial lag effects are entirely attributable to spatially correlated errors rather than substantive spillovers.&lt;/p>
&lt;pre>&lt;code class="language-stata">* Wald test: Reduce to SEM? (NO if p &amp;lt; 0.05)
testnl ([CRIME]W_INC = -[W]CRIME * [CRIME]INC) ([CRIME]W_HOVAL = -[W]CRIME * [CRIME]HOVAL)
&lt;/code>&lt;/pre>
&lt;p>The test &lt;strong>fails to reject&lt;/strong> the SEM common factor restriction. The LR test yields $-2(\text{LogL}_{SEM} - \text{LogL}_{SDM}) \approx 4.0$ with 2 df (critical value 5.99), confirming the SEM is not rejected. This means that the spatial dependence in the Columbus data could be interpreted as arising from spatially correlated unobservables rather than substantive crime spillovers.&lt;/p>
&lt;h3 id="74-sdm-vs-slx-the-key-comparison">7.4 SDM vs. SLX: the key comparison&lt;/h3>
&lt;p>The SDM clearly outperforms the SLX. The SLX is estimated by OLS (no spatial lag of $y$), while the SDM adds $\rho Wy$ which is highly significant ($\rho = 0.40$, z = 2.50). This spatial feedback term substantially improves the fit. The SLX alone, despite its significant $W \cdot INC$ coefficient, fails to capture the global spatial feedback that the $\rho$ parameter provides.&lt;/p>
&lt;h3 id="75-summary-of-specification-tests">7.5 Summary of specification tests&lt;/h3>
&lt;pre>&lt;code class="language-mermaid">graph TD
SDM[&amp;quot;&amp;lt;b&amp;gt;Spatial Durbin Model (SDM)&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;Starting point&amp;quot;]
SLX[&amp;quot;&amp;lt;b&amp;gt;SLX&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;ρ = 0&amp;lt;br/&amp;gt;Rejected&amp;quot;]
SAR[&amp;quot;&amp;lt;b&amp;gt;SAR&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;θ = 0&amp;lt;br/&amp;gt;Not rejected&amp;quot;]
SEM[&amp;quot;&amp;lt;b&amp;gt;SEM&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;θ + ρβ = 0&amp;lt;br/&amp;gt;Not rejected&amp;quot;]
SDM --&amp;gt;|&amp;quot;LR ≈ 7.4, 1 df&amp;quot;| SLX
SDM --&amp;gt;|&amp;quot;LR ≈ 2.0, 2 df&amp;quot;| SAR
SDM --&amp;gt;|&amp;quot;LR ≈ 4.0, 2 df&amp;quot;| SEM
style SDM fill:#00d4c8,stroke:#141413,color:#141413
style SLX fill:#d97757,stroke:#141413,color:#fff
style SAR fill:#6a9bcc,stroke:#141413,color:#fff
style SEM fill:#6a9bcc,stroke:#141413,color:#fff
&lt;/code>&lt;/pre>
&lt;p>The specification tests tell a nuanced story. Both the SAR restriction ($\theta = 0$) and the SEM common factor restriction ($\theta + \rho\beta = 0$) cannot be rejected at the 5% level. Only the SLX restriction ($\rho = 0$) is rejected, confirming that the spatial autoregressive parameter $\rho$ is essential. This leaves both SAR and SEM as statistically adequate simplifications. However, as Elhorst (2014) points out, the SAR&amp;rsquo;s constraint that the ratio between the indirect and direct effect is the same for every variable is economically restrictive. An alternative path is to consider the &lt;strong>SDEM&lt;/strong>, which also nests SLX and SEM (see Section 8.1).&lt;/p>
&lt;hr>
&lt;h2 id="8-extended-spatial-models">8. Extended spatial models&lt;/h2>
&lt;h3 id="81-sdem-spatial-durbin-error-model">8.1 SDEM (Spatial Durbin Error Model)&lt;/h3>
&lt;p>The SDEM combines the spatial lags of X from the SLX with the spatial error structure of the SEM. It captures &lt;strong>local spillovers&lt;/strong> through $WX\theta$ and &lt;strong>spatially correlated unobservables&lt;/strong> through $\lambda Wu$, but does not include the global feedback mechanism of $\rho Wy$.&lt;/p>
&lt;p>$$y = X \beta + W X \theta + u, \quad u = \lambda W u + \varepsilon$$&lt;/p>
&lt;p>The SDEM is sometimes preferred over the SDM when one believes that spillovers are local (limited to immediate neighbors) rather than global (propagating through the entire network). Like the SDM, the SDEM nests both the SLX ($\lambda = 0$) and the SEM ($\theta = 0$).&lt;/p>
&lt;pre>&lt;code class="language-stata">spregress CRIME INC HOVAL W_INC W_HOVAL, ml errorlag(W)
eststo SDEM
estat ic
mat s = r(S)
quietly estadd scalar AIC = s[1,5]
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Spatial Durbin error model Number of obs = 49
Maximum likelihood estimates Wald chi2(4) = 66.92
Prob &amp;gt; chi2 = 0.0000
Log-likelihood = -181.779 Pseudo R2 = 0.5988
------------------------------------------------------------------------------
CRIME | Coefficient Std. err. z P&amp;gt;|z| [95% conf. interval]
-------------+----------------------------------------------------------------
CRIME |
INC | -1.0523 .3213 -3.28 0.001 -1.6821 -.4225
HOVAL | -0.2782 .0911 -3.05 0.002 -0.4568 -.0996
W_INC | -1.2049 .5736 -2.10 0.036 -2.3292 -.0806
W_HOVAL | 0.1312 .2072 0.63 0.527 -0.2749 0.5374
-------------+----------------------------------------------------------------
W |
lambda | 0.4036 .1635 2.47 0.014 0.0832 0.7241
_cons | 73.6451 8.7239 8.44 0.000 56.5465 90.7437
------------------------------------------------------------------------------
&lt;/code>&lt;/pre>
&lt;p>The spatial error parameter $\lambda$ is &lt;strong>0.404&lt;/strong> (z = 2.47, p = 0.014), confirming that spatially correlated unobservables are important. Crucially, the spatial lag of income $W \cdot INC$ is &lt;strong>-1.20&lt;/strong> and statistically significant (z = -2.10, p = 0.036). This is a key result: even after controlling for spatially correlated errors, neighbors' average income significantly reduces a neighborhood&amp;rsquo;s crime rate. The spatial lag of housing value ($W \cdot HOVAL = +0.13$) remains insignificant (p = 0.527).&lt;/p>
&lt;p>In the SDEM, the indirect effects correspond directly to the $\theta$ coefficients because there is no spatial multiplier (no $\rho Wy$ term):&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;/th>
&lt;th>Direct&lt;/th>
&lt;th>Indirect&lt;/th>
&lt;th>Total&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>INC&lt;/strong>&lt;/td>
&lt;td>-1.05***&lt;/td>
&lt;td>-1.20**&lt;/td>
&lt;td>-2.26***&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>HOVAL&lt;/strong>&lt;/td>
&lt;td>-0.28***&lt;/td>
&lt;td>+0.13&lt;/td>
&lt;td>-0.15&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The indirect effect of income is &lt;strong>-1.20&lt;/strong> (significant at 5%), indicating that a \$1,000 increase in neighbors' average income reduces crime in the focal neighborhood by 1.20 incidents per 1,000 households. This is a substantively important local spillover: neighborhoods benefit from having wealthier neighbors through reduced crime. The total effect of income is &lt;strong>-2.26&lt;/strong>, even larger than the OLS estimate of -1.60, because OLS ignores the neighbors' income channel entirely.&lt;/p>
&lt;h3 id="82-sac--sarar">8.2 SAC / SARAR&lt;/h3>
&lt;p>The SAC (also called SARAR) model includes both a spatial lag of the dependent variable and a spatial error term, but no spatial lags of $X$. It separates two forms of spatial dependence: substantive spillovers through $\rho Wy$ and nuisance dependence through $\lambda Wu$.&lt;/p>
&lt;p>$$y = \rho W y + X \beta + u, \quad u = \lambda W u + \varepsilon$$&lt;/p>
&lt;pre>&lt;code class="language-stata">spregress CRIME INC HOVAL, ml dvarlag(W) errorlag(W)
eststo SAC
estat ic
mat s = r(S)
quietly estadd scalar AIC = s[1,5]
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">SAC model Number of obs = 49
Wald chi2(2) = 54.77
Log-likelihood = -182.581 Prob &amp;gt; chi2 = 0.0000
------------------------------------------------------------------------------
CRIME | Coefficient Std. err. z P&amp;gt;|z| [95% conf. interval]
-------------+----------------------------------------------------------------
CRIME |
INC | -1.0260 .3268 -3.14 0.002 -1.6666 -.3854
HOVAL | -0.2820 .0900 -3.13 0.002 -0.4584 -.1056
_cons | 47.8000 9.8900 4.83 0.000 28.4159 67.1841
-------------+----------------------------------------------------------------
W |
CRIME | 0.4780 .1622 2.95 0.003 0.1601 0.7959
lambda | 0.1660 .2969 0.56 0.576 -0.4158 0.7478
------------------------------------------------------------------------------
&lt;/code>&lt;/pre>
&lt;p>In the SAC model, $\rho$ is &lt;strong>0.478&lt;/strong> (z = 2.95, p = 0.003) and $\lambda$ is &lt;strong>0.166&lt;/strong> (z = 0.56, p = 0.576). When both are included, $\rho$ remains significant but $\lambda$ becomes insignificant, suggesting that the spatial lag model (SAR) dominates the spatial error structure. The coefficient of $\rho$ in the SAC (0.478) is close to the SAR value (0.428), and $\lambda$ in the SAC (0.166) is much smaller than in the SEM (0.562). The LR test of SAC versus SAR is approximately 0.3 with 1 df, and SAC versus SEM is approximately 2.3 with 1 df &amp;mdash; neither reaches the 5% critical value of 3.84, making it difficult to choose among these three models. However, since $\rho$ is significant while $\lambda$ is not, the SAR is the more parsimonious choice.&lt;/p>
&lt;pre>&lt;code class="language-stata">estat impact
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text"> Coefficient Std. err. z P&amp;gt;|z|
-------------------------------------------------------------------
INC
Direct | -1.0630 .3250 -3.27 0.001
Indirect | -0.5600 .3390 -1.65 0.099
Total | -1.6230 .5500 -2.95 0.003
-------------------------------------------------------------------
HOVAL
Direct | -0.2920 .0910 -3.21 0.001
Indirect | -0.1540 .0980 -1.57 0.116
Total | -0.4460 .1580 -2.82 0.005
-------------------------------------------------------------------
&lt;/code>&lt;/pre>
&lt;p>The SAC&amp;rsquo;s effect decomposition falls between the SAR and SEM. The direct effect of income (-1.06) is similar to the SAR (-1.10), and the indirect effects are somewhat attenuated because the spatial error term absorbs a portion of the spatial dependence. One key limitation of the SAC (shared with the SAR) is that the ratio between the indirect and direct effect is the same for every explanatory variable, because spillovers operate only through the spatial multiplier $(I - \rho W)^{-1}$. This constraint is economically restrictive &amp;mdash; there is no reason to expect that income and housing value should have proportionally equal spillover intensities.&lt;/p>
&lt;h3 id="83-gns-general-nesting-spatial">8.3 GNS (General Nesting Spatial)&lt;/h3>
&lt;p>The GNS model includes all three spatial channels simultaneously: the spatial lag of $y$, the spatial lags of $X$, and the spatial error. It is the most general specification in the taxonomy.&lt;/p>
&lt;p>$$y = \rho W y + X \beta + W X \theta + u, \quad u = \lambda W u + \varepsilon$$&lt;/p>
&lt;pre>&lt;code class="language-stata">spregress CRIME INC HOVAL W_INC W_HOVAL, ml dvarlag(W) errorlag(W)
eststo GNS
estat ic
mat s = r(S)
quietly estadd scalar AIC = s[1,5]
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">General nesting spatial model Number of obs = 49
Wald chi2(4) = 55.64
Log-likelihood = -179.689 Prob &amp;gt; chi2 = 0.0000
------------------------------------------------------------------------------
CRIME | Coefficient Std. err. z P&amp;gt;|z| [95% conf. interval]
-------------+----------------------------------------------------------------
CRIME |
INC | -0.9510 .4397 -2.16 0.031 -1.8129 -.0891
HOVAL | -0.2860 .0997 -2.87 0.004 -0.4813 -.0907
W_INC | -0.6930 1.6896 -0.41 0.682 -4.0046 2.6186
W_HOVAL | 0.2080 .2849 0.73 0.465 -0.3504 0.7664
-------------+----------------------------------------------------------------
W |
CRIME | 0.3150 .9553 0.33 0.742 -1.5574 2.1874
lambda | 0.1540 1.0267 0.15 0.881 -1.8583 2.1663
_cons | 50.9000 14.2800 3.56 0.000 22.9115 78.8885
------------------------------------------------------------------------------
&lt;/code>&lt;/pre>
&lt;p>In the GNS model, $\rho$ is &lt;strong>0.315&lt;/strong> (p = 0.742), $\lambda$ is &lt;strong>0.154&lt;/strong> (p = 0.881), and the spatial lags of income and housing value are both insignificant. With seven spatial parameters competing to explain the same 49 observations, the model is &lt;strong>overparameterized&lt;/strong>. As Gibbons and Overman (2012) explain, interaction effects among the dependent variable and interaction effects among the error terms are only &lt;strong>weakly identified&lt;/strong> separately. Combining both (as in the GNS) compounds this problem &amp;mdash; significance levels of all variables tend to collapse. The log-likelihood barely improves over the SDM or SDEM, and the AIC is higher, confirming that the additional complexity does not improve fit.&lt;/p>
&lt;p>The GNS&amp;rsquo;s effect decomposition is correspondingly imprecise:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;/th>
&lt;th>Direct&lt;/th>
&lt;th>Indirect&lt;/th>
&lt;th>Total&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>INC&lt;/strong>&lt;/td>
&lt;td>-1.03***&lt;/td>
&lt;td>-1.37&lt;/td>
&lt;td>-2.40&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>HOVAL&lt;/strong>&lt;/td>
&lt;td>-0.28***&lt;/td>
&lt;td>+0.16&lt;/td>
&lt;td>-0.11&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The direct effects remain significant and stable (consistent with all other models), but the indirect effects have very large standard errors. The GNS confirms what the specification tests already suggested &amp;mdash; the data does not support the most general specification, and a more parsimonious model is needed.&lt;/p>
&lt;hr>
&lt;h2 id="9-model-comparison">9. Model comparison&lt;/h2>
&lt;h3 id="91-coefficient-comparison">9.1 Coefficient comparison&lt;/h3>
&lt;p>We compare all eight models side by side, focusing on the key coefficients and model fit. Values are based on ML estimation; t-values in parentheses.&lt;/p>
&lt;pre>&lt;code class="language-stata">esttab OLS SAR SEM SLX SDM SDEM SAC GNS, ///
label stats(AIC) mtitle(&amp;quot;OLS&amp;quot; &amp;quot;SAR&amp;quot; &amp;quot;SEM&amp;quot; &amp;quot;SLX&amp;quot; &amp;quot;SDM&amp;quot; &amp;quot;SDEM&amp;quot; &amp;quot;SAC&amp;quot; &amp;quot;GNS&amp;quot;)
&lt;/code>&lt;/pre>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;/th>
&lt;th>OLS&lt;/th>
&lt;th>SAR&lt;/th>
&lt;th>SEM&lt;/th>
&lt;th>SLX&lt;/th>
&lt;th>SDM&lt;/th>
&lt;th>SDEM&lt;/th>
&lt;th>SAC&lt;/th>
&lt;th>GNS&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>INC&lt;/td>
&lt;td>-1.60***&lt;/td>
&lt;td>-1.03***&lt;/td>
&lt;td>-0.94***&lt;/td>
&lt;td>-1.10***&lt;/td>
&lt;td>-0.92***&lt;/td>
&lt;td>-1.05***&lt;/td>
&lt;td>-1.03***&lt;/td>
&lt;td>-0.95**&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>HOVAL&lt;/td>
&lt;td>-0.27***&lt;/td>
&lt;td>-0.27***&lt;/td>
&lt;td>-0.30***&lt;/td>
&lt;td>-0.29***&lt;/td>
&lt;td>-0.30***&lt;/td>
&lt;td>-0.28***&lt;/td>
&lt;td>-0.28***&lt;/td>
&lt;td>-0.29***&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$\rho$ (W*y)&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>0.43***&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>0.40**&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>0.48***&lt;/td>
&lt;td>0.32&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$\lambda$ (W*e)&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>0.56***&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>0.40**&lt;/td>
&lt;td>0.17&lt;/td>
&lt;td>0.15&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>W*INC&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>-1.40**&lt;/td>
&lt;td>-0.58&lt;/td>
&lt;td>-1.20**&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>-0.69&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>W*HOVAL&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>+0.21&lt;/td>
&lt;td>+0.26&lt;/td>
&lt;td>+0.13&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>+0.21&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Several patterns emerge. First, the income coefficient is &lt;strong>consistently negative&lt;/strong> across all models, ranging from -0.92 (SDM) to -1.60 (OLS). The spatial models generally produce smaller income coefficients than OLS, suggesting that part of the OLS income effect was capturing omitted spatial structure. Second, the housing value coefficient is &lt;strong>remarkably stable&lt;/strong> across all models, ranging from -0.27 to -0.30 &amp;mdash; this variable is insensitive to the spatial specification choice. Third, and crucially, the spatial lag of income ($W \cdot INC$) is &lt;strong>negative and significant&lt;/strong> in the SLX (-1.40, t = -2.50) and the SDEM (-1.20, z = -2.10), meaning that neighbors' income is a substantive predictor of crime. The SLX, SDM, SDEM, and GNS models all agree that $W \cdot INC$ is negative and $W \cdot HOVAL$ is positive, producing a consistent pattern of spatial spillover estimates regardless of which other spatial channels are included.&lt;/p>
&lt;h3 id="92-direct-and-indirect-effects-comparison">9.2 Direct and indirect effects comparison&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;/th>
&lt;th>OLS&lt;/th>
&lt;th>SAR&lt;/th>
&lt;th>SEM&lt;/th>
&lt;th>SLX&lt;/th>
&lt;th>SDM&lt;/th>
&lt;th>SDEM&lt;/th>
&lt;th>SAC&lt;/th>
&lt;th>GNS&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>INC&lt;/strong>&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Direct&lt;/td>
&lt;td>-1.60***&lt;/td>
&lt;td>-1.10***&lt;/td>
&lt;td>-0.94***&lt;/td>
&lt;td>-1.10***&lt;/td>
&lt;td>-1.03***&lt;/td>
&lt;td>-1.05***&lt;/td>
&lt;td>-1.06***&lt;/td>
&lt;td>-1.03***&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Indirect&lt;/td>
&lt;td>0&lt;/td>
&lt;td>-0.76**&lt;/td>
&lt;td>0&lt;/td>
&lt;td>-1.40**&lt;/td>
&lt;td>-1.50*&lt;/td>
&lt;td>-1.20**&lt;/td>
&lt;td>-0.56&lt;/td>
&lt;td>-1.37&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Total&lt;/td>
&lt;td>-1.60***&lt;/td>
&lt;td>-1.86***&lt;/td>
&lt;td>-0.94***&lt;/td>
&lt;td>-2.50***&lt;/td>
&lt;td>-2.52***&lt;/td>
&lt;td>-2.26***&lt;/td>
&lt;td>-1.62***&lt;/td>
&lt;td>-2.40&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>HOVAL&lt;/strong>&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Direct&lt;/td>
&lt;td>-0.27***&lt;/td>
&lt;td>-0.28***&lt;/td>
&lt;td>-0.30***&lt;/td>
&lt;td>-0.29***&lt;/td>
&lt;td>-0.28***&lt;/td>
&lt;td>-0.28***&lt;/td>
&lt;td>-0.29***&lt;/td>
&lt;td>-0.28***&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Indirect&lt;/td>
&lt;td>0&lt;/td>
&lt;td>-0.20*&lt;/td>
&lt;td>0&lt;/td>
&lt;td>+0.21&lt;/td>
&lt;td>+0.22&lt;/td>
&lt;td>+0.13&lt;/td>
&lt;td>-0.15&lt;/td>
&lt;td>+0.16&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Total&lt;/td>
&lt;td>-0.27***&lt;/td>
&lt;td>-0.48***&lt;/td>
&lt;td>-0.30***&lt;/td>
&lt;td>-0.08&lt;/td>
&lt;td>-0.07&lt;/td>
&lt;td>-0.15&lt;/td>
&lt;td>-0.45***&lt;/td>
&lt;td>-0.11&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The direct effects of income and housing value are broadly consistent across models: approximately -0.94 to -1.60 for income and -0.27 to -0.30 for housing value. The &lt;strong>indirect effects&lt;/strong> reveal the most important differences:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>The OLS, SEM, and SAR models produce no or wrong spillover effects.&lt;/strong> OLS has zero spillovers by construction. The SEM&amp;rsquo;s spillovers are zero by construction. The SAR constrains the ratio between indirect and direct effects to be equal for every variable, which forces the housing value spillover to be negative (-0.20) even though the SLX, SDM, SDEM, and GNS all suggest it is positive.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>The SLX, SDM, SDEM, and GNS models agree on the pattern&lt;/strong>: income spillovers are large and negative (-1.20 to -1.50), while housing value spillovers are small and positive (+0.13 to +0.22) and insignificant. This consistency across different model specifications strengthens the case that the income spillover is a robust finding.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>The total effect of income is substantially larger in models with $\theta$ terms&lt;/strong> (-2.26 to -2.52) than in models without them (-0.94 to -1.86). This reveals that the standard SAR/SEM models substantially underestimate the full impact of income on crime by ignoring the local spillover channel.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="10-discussion">10. Discussion&lt;/h2>
&lt;p>The Columbus crime dataset illustrates a recurring challenge in spatial econometrics: choosing among models that capture spatial dependence through different channels. Following Elhorst (2014, Section 2.9), the evidence points toward the &lt;strong>SDM&lt;/strong> and &lt;strong>SDEM&lt;/strong> as the preferred specifications, though neither the SAR nor SEM can be formally rejected.&lt;/p>
&lt;p>&lt;strong>Why not SAR, SEM, or SAC?&lt;/strong> The specification tests fail to reject both the SAR restriction ($\theta = 0$) and the SEM common factor restriction ($\theta + \rho\beta = 0$), which might suggest these simpler models are adequate. However, as Elhorst (2014) emphasizes, these models have structural limitations. The SAR and SAC constrain the ratio between the indirect and direct effect to be &lt;strong>the same for every explanatory variable&lt;/strong> &amp;mdash; a consequence of spillovers operating solely through the spatial multiplier $(I - \rho W)^{-1}\beta_k$. In the Columbus data, this forces the housing value spillover to be negative (proportional to the direct effect), even though the SLX, SDM, SDEM, and GNS models all estimate it as positive. The SEM, on the other hand, produces &lt;strong>zero spillover effects&lt;/strong> by construction, which may be too restrictive if one believes that crime is genuinely affected by conditions in neighboring areas.&lt;/p>
&lt;p>&lt;strong>Why SDM and SDEM?&lt;/strong> Both models allow the indirect effect to differ freely across explanatory variables. In both, the spillover effect of income is negative and significant (SDM: -1.50, marginally significant; SDEM: -1.20, significant at 5%), while the spillover effect of housing value is positive but insignificant. This flexibility produces economically sensible results: neighborhoods surrounded by higher-income areas experience less crime (consistent with crime displacement and opportunity theory), but neighbors' housing values have no significant independent effect on crime.&lt;/p>
&lt;p>&lt;strong>The SDM-SDEM dilemma.&lt;/strong> Whether it is the SDM or the SDEM model that better describes the data is difficult to say, since these two models are &lt;strong>non-nested&lt;/strong> (the SDM has $\rho$ but no $\lambda$; the SDEM has $\lambda$ but no $\rho$). The GNS, which nests both, is overparameterized and produces insignificant estimates for all spatial parameters. Both models produce comparable spillover effects in terms of magnitude and significance. As Elhorst (2014) notes, this is worrying because the two models have &lt;strong>different interpretations&lt;/strong>: the SDM implies that crime spillovers propagate globally through the network, while the SDEM implies they are local (limited to immediate neighbors) with the remaining spatial pattern driven by unobserved common factors.&lt;/p>
&lt;p>&lt;strong>Policy implications.&lt;/strong> A \$1,000 increase in household income reduces crime by approximately 1.0 incident per 1,000 households directly and an additional 1.2&amp;ndash;1.5 incidents indirectly through the spatial spillover channel, for a total effect of 2.3&amp;ndash;2.5. This means that policies to increase income in the poorest neighborhoods generate positive externalities for neighboring areas that are even larger than the within-neighborhood effect. The total income effect in the SDM/SDEM (-2.3 to -2.5) is &lt;strong>40&amp;ndash;55% larger&lt;/strong> than the OLS estimate (-1.60), revealing the magnitude of the bias from ignoring spatial spillovers.&lt;/p>
&lt;p>This tutorial complements the companion post on &lt;a href="https://carlos-mendez.org/post/stata_sp_regression_panel/">spatial panel regression&lt;/a>, which demonstrates the same model taxonomy in a panel data setting using cigarette demand across US states. The panel setting offers additional advantages &amp;mdash; fixed effects to control for unobserved heterogeneity and dynamic extensions to separate temporal from spatial dynamics &amp;mdash; but requires repeated observations over time. The cross-sectional framework presented here is appropriate when only a single snapshot of spatial data is available, which is common in urban economics, criminology, and regional science.&lt;/p>
&lt;hr>
&lt;h2 id="11-summary-and-next-steps">11. Summary and next steps&lt;/h2>
&lt;p>This tutorial covered the complete taxonomy of cross-sectional spatial regression models in Stata &amp;mdash; from OLS diagnostics through the most general GNS specification. The key takeaways are:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Spatial autocorrelation is significant.&lt;/strong> Moran&amp;rsquo;s I of 0.222 (p = 0.005) confirms that OLS residuals are positively spatially autocorrelated, and the LM tests favor the spatial error specification.&lt;/li>
&lt;li>&lt;strong>The SDM and SDEM are the preferred models.&lt;/strong> Both models allow the indirect effects to differ across explanatory variables, and both identify a significant negative spillover effect of income. The SAR, SEM, and SLX restrictions from the SDM cannot be formally rejected, but the SAR and SAC impose an economically restrictive constraint (equal spillover-to-direct ratios for all variables), while the SEM produces zero spillovers by construction.&lt;/li>
&lt;li>&lt;strong>Direct effects are robust to spatial specification.&lt;/strong> The direct effect of income ranges from -1.03 to -1.10 across the four models with $\theta$ terms (SLX, SDM, SDEM, GNS), and the direct effect of housing value ranges from -0.28 to -0.29 &amp;mdash; substantially more stable than the indirect effects.&lt;/li>
&lt;li>&lt;strong>Neighbors' income significantly reduces crime.&lt;/strong> The indirect effect of income is -1.20 (SDEM) to -1.50 (SDM), comparable to or larger than the direct effect. The total income effect in the SDM/SDEM (-2.3 to -2.5) is &lt;strong>40&amp;ndash;55% larger&lt;/strong> than the OLS estimate (-1.60), revealing substantial bias from ignoring spatial spillovers.&lt;/li>
&lt;li>&lt;strong>The GNS is overparameterized.&lt;/strong> When all three spatial channels ($\rho$, $\theta$, $\lambda$) are included simultaneously, all become insignificant. The difficulty of separately identifying endogenous interaction effects and error interaction effects is a fundamental limitation of the cross-sectional setting.&lt;/li>
&lt;/ul>
&lt;p>For further study, consider the companion tutorial on &lt;a href="https://carlos-mendez.org/post/stata_sp_regression_panel/">spatial panel regression&lt;/a>, which extends these methods to panel data with fixed effects and dynamic specifications. For Python implementations, the PySAL &lt;code>spreg&lt;/code> package provides analogous spatial regression tools.&lt;/p>
&lt;hr>
&lt;h2 id="12-exercises">12. Exercises&lt;/h2>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Alternative weight matrix.&lt;/strong> Replace the Queen contiguity matrix with a k-nearest neighbors matrix (e.g., $k = 4$ or $k = 6$). Re-estimate the SAR and SEM models and compare the spatial parameter estimates ($\rho$ and $\lambda$). Does the choice of weight matrix change the substantive conclusions about spatial dependence in crime?&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Single explanatory variable.&lt;/strong> Re-estimate all eight models using only INC (dropping HOVAL). How do the spatial parameter estimates and the AIC rankings change? Does the Wald test from the SDM still fail to reject the SAR and SEM restrictions?&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Rook vs. Queen contiguity.&lt;/strong> Construct a Rook contiguity matrix (neighbors share a common edge, not just a vertex) and re-estimate the SDM. Compare the Wald specification test results to those obtained with Queen contiguity. Are the conclusions about which spatial model is appropriate sensitive to the contiguity definition?&lt;/p>
&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="references">References&lt;/h2>
&lt;ol>
&lt;li>&lt;a href="https://doi.org/10.1007/978-94-015-7799-1" target="_blank" rel="noopener">Anselin, L. (1988). &lt;em>Spatial Econometrics: Methods and Models&lt;/em>. Kluwer Academic Publishers.&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://doi.org/10.1201/9781420064254" target="_blank" rel="noopener">LeSage, J. P. &amp;amp; Pace, R. K. (2009). &lt;em>Introduction to Spatial Econometrics&lt;/em>. Chapman &amp;amp; Hall/CRC.&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://link.springer.com/book/10.1007/978-3-642-40340-8" target="_blank" rel="noopener">Elhorst, J. P. (2014). &lt;em>Spatial Econometrics: From Cross-Sectional Data to Spatial Panels&lt;/em>. Springer.&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://geodacenter.github.io/documentation.html" target="_blank" rel="noopener">Anselin, L. (2005). &lt;em>Exploring Spatial Data with GeoDa: A Workbook&lt;/em>. Center for Spatially Integrated Social Science.&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://doi.org/10.5281/zenodo.5151076" target="_blank" rel="noopener">Mendez, C. (2021). &lt;em>Spatial econometrics for cross-sectional data in Stata&lt;/em>. DOI: 10.5281/zenodo.5151076.&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://geodacenter.github.io/data-and-lab/columbus/" target="_blank" rel="noopener">Columbus crime dataset &amp;mdash; GeoDa Center Data and Lab.&lt;/a>&lt;/li>
&lt;/ol></description></item><item><title>Spatial Panel Regression in Stata: Cigarette Demand Across US States</title><link>https://carlos-mendez.org/post/stata_sp_regression_panel/</link><pubDate>Fri, 01 Dec 2023 00:00:00 +0000</pubDate><guid>https://carlos-mendez.org/post/stata_sp_regression_panel/</guid><description>&lt;h2 id="1-overview">1. Overview&lt;/h2>
&lt;p>Cigarette taxation is a state-level policy instrument, but consumption in one state does not exist in isolation. When a state raises its tobacco tax, consumers near state borders may simply drive across to buy cheaper cigarettes in a neighboring state. This &lt;strong>cross-border shopping&lt;/strong> effect means that a state&amp;rsquo;s cigarette consumption depends not only on its own prices and income but also on the prices and income of its neighbors. Standard panel data models &amp;mdash; pooled OLS, fixed effects, and two-way fixed effects &amp;mdash; cannot capture these spatial spillovers because they treat each state as an independent observation.&lt;/p>
&lt;p>This tutorial introduces &lt;strong>spatial panel regression&lt;/strong> as a framework for modeling geographic interdependence in panel data. We use the classic Baltagi cigarette demand dataset, which tracks per-capita cigarette consumption, real prices, and real per-capita income across 46 US states from 1963 to 1992. Starting from non-spatial panel models as a baseline, we progressively build toward the &lt;strong>Spatial Durbin Model (SDM)&lt;/strong> &amp;mdash; a flexible specification that includes both the spatial lag of the dependent variable and spatial lags of the explanatory variables. We then use &lt;strong>Wald tests&lt;/strong> to determine whether simpler spatial models (SAR, SLX, or SEM) are adequate, and finally extend the framework to &lt;strong>dynamic spatial panels&lt;/strong> that account for habit persistence in cigarette consumption.&lt;/p>
&lt;p>All estimation is performed using the &lt;code>xsmle&lt;/code> package in Stata, which implements maximum likelihood estimation for a family of spatial panel models with fixed effects. The spatial weight matrix is a binary contiguity matrix that defines two states as neighbors if they share a common border, row-standardized so that the spatial lag of a variable equals the average value among a state&amp;rsquo;s neighbors.&lt;/p>
&lt;h3 id="learning-objectives">Learning objectives&lt;/h3>
&lt;ul>
&lt;li>Estimate non-spatial panel models (pooled OLS, region FE, time FE, two-way FE) and compare their price and income elasticities&lt;/li>
&lt;li>Construct and load a row-standardized spatial weight matrix for panel data in Stata&lt;/li>
&lt;li>Estimate the Spatial Durbin Model (SDM) with two-way fixed effects using the &lt;code>xsmle&lt;/code> package&lt;/li>
&lt;li>Apply the Lee and Yu bias correction for spatial panels with moderate time dimensions&lt;/li>
&lt;li>Use Wald tests to evaluate whether the SDM simplifies to SAR, SLX, or SEM&lt;/li>
&lt;li>Estimate dynamic spatial panel models with temporal and spatiotemporal lags to capture habit persistence&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="2-the-modeling-pipeline">2. The modeling pipeline&lt;/h2>
&lt;p>The tutorial follows a progressive approach &amp;mdash; each stage builds on the previous one by relaxing assumptions and adding complexity. The diagram below summarizes the path from data preparation through the final dynamic spatial models.&lt;/p>
&lt;pre>&lt;code class="language-mermaid">graph LR
A[&amp;quot;&amp;lt;b&amp;gt;Data &amp;amp; W&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;i&amp;gt;Section 3&amp;lt;/i&amp;gt;&amp;lt;br/&amp;gt;Panel setup&amp;lt;br/&amp;gt;Weight matrix&amp;quot;]
B[&amp;quot;&amp;lt;b&amp;gt;Non-Spatial&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;i&amp;gt;Section 4&amp;lt;/i&amp;gt;&amp;lt;br/&amp;gt;OLS, FE,&amp;lt;br/&amp;gt;Two-way FE&amp;quot;]
C[&amp;quot;&amp;lt;b&amp;gt;SDM&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;i&amp;gt;Section 6&amp;lt;/i&amp;gt;&amp;lt;br/&amp;gt;Spatial Durbin&amp;lt;br/&amp;gt;+ Lee-Yu&amp;quot;]
D[&amp;quot;&amp;lt;b&amp;gt;Wald Tests&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;i&amp;gt;Section 7&amp;lt;/i&amp;gt;&amp;lt;br/&amp;gt;SAR? SLX?&amp;lt;br/&amp;gt;SEM?&amp;quot;]
E[&amp;quot;&amp;lt;b&amp;gt;Dynamic&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;&amp;lt;i&amp;gt;Section 8&amp;lt;/i&amp;gt;&amp;lt;br/&amp;gt;Temporal &amp;amp;&amp;lt;br/&amp;gt;spatial lags&amp;quot;]
A --&amp;gt; B
B --&amp;gt; C
C --&amp;gt; D
D --&amp;gt; E
style A fill:#6a9bcc,stroke:#141413,color:#fff
style B fill:#d97757,stroke:#141413,color:#fff
style C fill:#00d4c8,stroke:#141413,color:#141413
style D fill:#141413,stroke:#d97757,color:#fff
style E fill:#6a9bcc,stroke:#141413,color:#fff
&lt;/code>&lt;/pre>
&lt;p>We first establish non-spatial benchmarks to understand the baseline price and income elasticities. Then we introduce the Spatial Durbin Model to capture spillovers, apply Wald tests to check whether a simpler spatial specification suffices, and finally add dynamic components to account for the habit-forming nature of cigarette consumption.&lt;/p>
&lt;hr>
&lt;h2 id="3-setup-and-data-loading">3. Setup and data loading&lt;/h2>
&lt;p>Before running any spatial models, we need three Stata packages: &lt;code>spmat&lt;/code> for spatial weight matrix management, &lt;code>xsmle&lt;/code> for spatial panel estimation, and &lt;code>spwmatrix&lt;/code> for weight matrix conversion. If you have not installed them, uncomment the &lt;code>net install&lt;/code> lines below.&lt;/p>
&lt;pre>&lt;code class="language-stata">clear all
macro drop _all
set more off
version 12
* Install packages (uncomment if needed)
*net install st0292, from(http://www.stata-journal.com/software/sj13-2)
*net install xsmle, from(http://fmwww.bc.edu/RePEc/bocode/x)
*net install spwmatrix, from(http://fmwww.bc.edu/RePEc/bocode/s)
&lt;/code>&lt;/pre>
&lt;h3 id="31-spatial-weight-matrix">3.1 Spatial weight matrix&lt;/h3>
&lt;p>The spatial weight matrix &lt;strong>W&lt;/strong> defines the neighborhood structure among the 46 US states. We use a binary contiguity matrix where two states are neighbors if they share a common border. The matrix is stored in a &lt;code>.dta&lt;/code> file and converted to an &lt;code>spmat&lt;/code> object with row-standardization &amp;mdash; meaning that each row sums to one, so the spatial lag of a variable equals the &lt;strong>weighted average&lt;/strong> among a state&amp;rsquo;s neighbors.&lt;/p>
&lt;pre>&lt;code class="language-stata">* Load binary contiguity W matrix and convert to row-standardized spmat object
use &amp;quot;https://github.com/quarcs-lab/data-open/raw/master/cigar/Wct_bin.dta&amp;quot;, replace
spmat dta Wst m1-m46, norm(row) replace
&lt;/code>&lt;/pre>
&lt;p>The &lt;code>spmat dta&lt;/code> command reads columns &lt;code>m1&lt;/code> through &lt;code>m46&lt;/code> from the loaded dataset and stores them as a spatial weight matrix object named &lt;code>Wst&lt;/code>. The &lt;code>norm(row)&lt;/code> option applies row-standardization, and &lt;code>replace&lt;/code> overwrites any existing matrix with the same name.&lt;/p>
&lt;h3 id="32-panel-data-setup">3.2 Panel data setup&lt;/h3>
&lt;p>The Baltagi cigarette demand dataset contains three variables measured across 46 US states and 30 years (1963&amp;ndash;1992): log per-capita cigarette consumption (&lt;code>logc&lt;/code>), log real cigarette price (&lt;code>logp&lt;/code>), and log real per-capita disposable income (&lt;code>logy&lt;/code>).&lt;/p>
&lt;pre>&lt;code class="language-stata">* Load panel data
use &amp;quot;https://github.com/quarcs-lab/data-open/raw/master/cigar/baltagi_cigar.dta&amp;quot;, clear
sort year state
xtset state year
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Panel variable: state (strongly balanced)
Time variable: year, 1963 to 1992
Delta: 1 unit
&lt;/code>&lt;/pre>
&lt;p>The panel is &lt;strong>strongly balanced&lt;/strong> &amp;mdash; all 46 states are observed in all 30 years, yielding 1,380 total observations. This balanced structure simplifies estimation and avoids the complications of missing data.&lt;/p>
&lt;h3 id="33-panel-summary-statistics">3.3 Panel summary statistics&lt;/h3>
&lt;p>The &lt;code>xtsum&lt;/code> command decomposes each variable&amp;rsquo;s variation into between-state and within-state components &amp;mdash; a key diagnostic for understanding what panel models can and cannot identify.&lt;/p>
&lt;pre>&lt;code class="language-stata">xtsum
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Variable | Mean Std. dev. Min Max | Observations
-----------------+--------------------------------------------+----------------
logc overall | 4.625563 .2538233 3.736352 5.399758 | N = 1380
between | .225498 4.057739 5.19628 | n = 46
within | .1254968 4.110718 5.070093 | T = 30
| |
logp overall | 3.648067 .3364439 2.579455 4.588055 | N = 1380
between | .1927783 3.22723 4.021831 | n = 46
within | .2798008 2.780289 4.372397 | T = 30
| |
logy overall | 1.615786 .248717 .8676362 2.253795 | N = 1380
between | .1363281 1.294913 2.063736 | n = 46
within | .2098697 1.035539 2.106283 | T = 30
&lt;/code>&lt;/pre>
&lt;h3 id="variables">Variables&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Variable&lt;/th>
&lt;th>Description&lt;/th>
&lt;th>Mean&lt;/th>
&lt;th>Std. Dev.&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>logc&lt;/code>&lt;/td>
&lt;td>Log per-capita cigarette consumption (packs)&lt;/td>
&lt;td>4.626&lt;/td>
&lt;td>0.254&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>logp&lt;/code>&lt;/td>
&lt;td>Log real price per pack (cents)&lt;/td>
&lt;td>3.648&lt;/td>
&lt;td>0.336&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>logy&lt;/code>&lt;/td>
&lt;td>Log real per-capita disposable income&lt;/td>
&lt;td>1.616&lt;/td>
&lt;td>0.249&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Mean log consumption is 4.63, corresponding to roughly 102 packs per capita per year. The between-state standard deviation of &lt;code>logc&lt;/code> (0.225) is larger than the within-state standard deviation (0.125), indicating that cross-state differences in consumption levels are more pronounced than changes within a single state over time. For &lt;code>logp&lt;/code>, the pattern reverses &amp;mdash; within-state variation (0.280) exceeds between-state variation (0.193), reflecting the fact that real prices changed substantially over this 30-year period due to tax policy changes and inflation. This decomposition foreshadows why fixed effects models, which exploit within-state variation, may produce different elasticity estimates than pooled models.&lt;/p>
&lt;hr>
&lt;h2 id="4-non-spatial-panel-models">4. Non-spatial panel models&lt;/h2>
&lt;p>Before introducing spatial dependence, we estimate four standard panel specifications to establish baseline price and income elasticities. Each model relaxes a different assumption about unobserved heterogeneity, and comparing their estimates reveals how sensitive the results are to the treatment of state-level and time-level confounders.&lt;/p>
&lt;h3 id="41-pooled-ols">4.1 Pooled OLS&lt;/h3>
&lt;p>Pooled OLS treats all 1,380 observations as independent, ignoring the panel structure entirely. It provides a naive benchmark.&lt;/p>
&lt;pre>&lt;code class="language-stata">reg logc logp logy
estimates store pool
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text"> Source | SS df MS Number of obs = 1,380
-------------+---------------------------------- F(2, 1377) = 199.28
Model | 21.564818 2 10.7824090 Prob &amp;gt; F = 0.0000
Residual | 74.518523 1,377 .054116576 R-squared = 0.2244
-------------+---------------------------------- Adj R-squared = 0.2233
Total | 96.083341 1,379 .069676098 Root MSE = .23284
------------------------------------------------------------------------------
logc | Coefficient Std. err. t P&amp;gt;|t| [95% conf. interval]
-------------+----------------------------------------------------------------
logp | -.3857227 .0309752 -12.45 0.000 -.4464987 -.3249467
logy | .3724439 .0264568 14.08 0.000 .3205328 .4243551
_cons | 4.396312 .0531992 82.64 0.000 4.291951 4.500674
------------------------------------------------------------------------------
&lt;/code>&lt;/pre>
&lt;p>Pooled OLS estimates a price elasticity of &lt;strong>-0.386&lt;/strong> and an income elasticity of &lt;strong>0.372&lt;/strong>, both statistically significant at the 1% level. However, the R-squared is only 0.224, and more importantly, this model assumes no systematic differences across states &amp;mdash; an untenable assumption given the large between-state variation we observed in the summary statistics.&lt;/p>
&lt;h3 id="42-region-fixed-effects">4.2 Region fixed effects&lt;/h3>
&lt;p>Region (state) fixed effects control for all time-invariant state characteristics &amp;mdash; geographic location, cultural attitudes toward smoking, historical tobacco production, and any other state-specific factor that does not change over the sample period.&lt;/p>
&lt;pre>&lt;code class="language-stata">xtreg logc logp logy, fe
estimates store rfe
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Fixed-effects (within) regression Number of obs = 1,380
Group variable: state Number of groups = 46
R-squared: Obs per group:
Within = 0.4059 min = 30
Between = 0.0681 avg = 30.0
Overall = 0.1050 max = 30
F(2,1332) = 455.52
corr(u_i, Xb) = -0.8072 Prob &amp;gt; F = 0.0000
------------------------------------------------------------------------------
logc | Coefficient Std. err. t P&amp;gt;|t| [95% conf. interval]
-------------+----------------------------------------------------------------
logp | -.2307217 .0276419 -8.35 0.000 -.2849426 -.1765008
logy | -.0145419 .0389849 -0.37 0.709 -.0910300 .0619462
_cons | 4.619736 .0542965 85.09 0.000 4.513180 4.726293
------------------------------------------------------------------------------
sigma_u | .21834832
sigma_e | .09498463
rho | .84090063 (fraction of variance due to u_i)
------------------------------------------------------------------------------
F test that all u_i=0: F(45, 1332) = 85.78 Prob &amp;gt; F = 0.0000
&lt;/code>&lt;/pre>
&lt;p>After controlling for state fixed effects, the price elasticity drops to &lt;strong>-0.231&lt;/strong> &amp;mdash; substantially smaller in magnitude than the pooled OLS estimate of -0.386. This difference reveals that much of the apparent price sensitivity in pooled OLS was driven by &lt;strong>cross-state composition effects&lt;/strong>: low-price states tend to have higher consumption for reasons unrelated to price (e.g., tobacco-producing states have both lower prices and stronger smoking cultures). The income elasticity becomes statistically insignificant at &lt;strong>-0.015&lt;/strong> (p = 0.709), suggesting that within-state income changes over time do not strongly predict consumption changes once state-level heterogeneity is absorbed. The F-test for joint significance of state fixed effects is overwhelming (F = 85.78, p &amp;lt; 0.001), confirming that state heterogeneity is substantial.&lt;/p>
&lt;h3 id="43-time-fixed-effects">4.3 Time fixed effects&lt;/h3>
&lt;p>Time fixed effects control for shocks common to all states in a given year &amp;mdash; federal anti-smoking campaigns, national health reports (such as the 1964 Surgeon General&amp;rsquo;s report), and macroeconomic fluctuations.&lt;/p>
&lt;pre>&lt;code class="language-stata">reg logc logp logy i.year
estimates store tfe
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text"> Source | SS df MS Number of obs = 1,380
-------------+---------------------------------- F(31, 1348) = 41.04
Model | 48.7107267 31 1.57131054 Prob &amp;gt; F = 0.0000
Residual | 47.3726143 1,348 .03514290 R-squared = 0.5070
-------------+---------------------------------- Adj R-squared = 0.4957
Total | 96.083341 1,379 .069676098 Root MSE = .18747
------------------------------------------------------------------------------
logc | Coefficient Std. err. t P&amp;gt;|t| [95% conf. interval]
-------------+----------------------------------------------------------------
logp | -.8612867 .0389729 -22.10 0.000 -.9377676 -.7848058
logy | .8045032 .0466019 17.26 0.000 .7130647 .8959417
_cons | 3.958816 .0638297 62.02 0.000 3.833551 4.084081
------------------------------------------------------------------------------
&lt;/code>&lt;/pre>
&lt;p>With time fixed effects, the price elasticity jumps to &lt;strong>-0.861&lt;/strong> and the income elasticity to &lt;strong>0.805&lt;/strong> &amp;mdash; both much larger in magnitude than the pooled OLS estimates. By removing common year-level trends (such as the secular decline in smoking rates after the Surgeon General&amp;rsquo;s report), the model isolates cross-state differences in a given year. The R-squared increases to 0.507, a substantial improvement over pooled OLS.&lt;/p>
&lt;h3 id="44-two-way-fixed-effects">4.4 Two-way fixed effects&lt;/h3>
&lt;p>Two-way fixed effects combine state and time dummies, controlling simultaneously for state-specific time-invariant factors and year-specific common shocks. This is the most thorough non-spatial specification and serves as our benchmark.&lt;/p>
&lt;pre>&lt;code class="language-stata">xtreg logc logp logy i.year, fe
estimates store rtfe
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Fixed-effects (within) regression Number of obs = 1,380
Group variable: state Number of groups = 46
R-squared: Obs per group:
Within = 0.7891 min = 30
Between = 0.0121 avg = 30.0
Overall = 0.0456 max = 30
F(31,1303) = 157.60
corr(u_i, Xb) = -0.5688 Prob &amp;gt; F = 0.0000
------------------------------------------------------------------------------
logc | Coefficient Std. err. t P&amp;gt;|t| [95% conf. interval]
-------------+----------------------------------------------------------------
logp | -.4020279 .0272553 -14.75 0.000 -.4555018 -.3485541
logy | .1193476 .0478095 2.50 0.013 .0255202 .2131749
_cons | 4.515994 .0533810 84.59 0.000 4.411254 4.620733
------------------------------------------------------------------------------
sigma_u | .21428785
sigma_e | .05601281
rho | .93607854 (fraction of variance due to u_i)
------------------------------------------------------------------------------
&lt;/code>&lt;/pre>
&lt;p>The two-way FE model yields a price elasticity of &lt;strong>-0.402&lt;/strong> and an income elasticity of &lt;strong>0.119&lt;/strong>. The within R-squared is 0.789, a dramatic improvement over the region-only FE model (0.406), indicating that year effects absorb a large share of temporal variation. The price elasticity is roughly intermediate between the region-FE (-0.231) and time-FE (-0.861) estimates, illustrating how the choice of fixed effects changes the identifying variation and the resulting elasticity.&lt;/p>
&lt;h3 id="45-comparison-of-non-spatial-models">4.5 Comparison of non-spatial models&lt;/h3>
&lt;pre>&lt;code class="language-stata">estimates table pool rfe tfe rtfe, b(%7.2f) star(0.1 0.05 0.01) stf(%9.0f)
&lt;/code>&lt;/pre>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;/th>
&lt;th>Pooled OLS&lt;/th>
&lt;th>Region FE&lt;/th>
&lt;th>Time FE&lt;/th>
&lt;th>Two-way FE&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>logp&lt;/code>&lt;/td>
&lt;td>-0.39***&lt;/td>
&lt;td>-0.23***&lt;/td>
&lt;td>-0.86***&lt;/td>
&lt;td>-0.40***&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>logy&lt;/code>&lt;/td>
&lt;td>0.37***&lt;/td>
&lt;td>-0.01&lt;/td>
&lt;td>0.80***&lt;/td>
&lt;td>0.12**&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>R-sq&lt;/td>
&lt;td>0.224&lt;/td>
&lt;td>0.406&lt;/td>
&lt;td>0.507&lt;/td>
&lt;td>0.789&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The four specifications tell a coherent story: price has a &lt;strong>consistently negative&lt;/strong> effect on cigarette consumption, but the magnitude varies from -0.23 (region FE) to -0.86 (time FE) depending on which sources of variation are exploited. The two-way FE estimate of -0.40 is the most credible non-spatial benchmark because it controls for both state heterogeneity and common time trends. However, all four models assume that each state&amp;rsquo;s consumption depends only on its &lt;strong>own&lt;/strong> price and income &amp;mdash; an assumption we will relax in the next section.&lt;/p>
&lt;hr>
&lt;h2 id="5-why-spatial-models">5. Why spatial models?&lt;/h2>
&lt;p>Even with two-way fixed effects, the models above ignore a potentially important channel: &lt;strong>spatial spillovers&lt;/strong>. If Virginia raises its cigarette tax, smokers in bordering states might change their behavior too &amp;mdash; either because they no longer cross into Virginia to buy cheaper cigarettes, or because Virginia&amp;rsquo;s policy signals a broader regional trend. Similarly, a rise in income in one state may increase consumption in neighboring states through commuting, trade, and social networks.&lt;/p>
&lt;p>The &lt;strong>Spatial Durbin Model (SDM)&lt;/strong> is a flexible framework that captures these spillovers through two channels:&lt;/p>
&lt;p>$$y_{it} = \rho \sum_{j=1}^{N} w_{ij} y_{jt} + x_{it} \beta + \sum_{j=1}^{N} w_{ij} x_{jt} \theta + \mu_i + \lambda_t + \varepsilon_{it}$$&lt;/p>
&lt;p>In words, this equation says that cigarette consumption in state $i$ at time $t$ depends on three spatial components: (1) the &lt;strong>spatial lag of the dependent variable&lt;/strong> $\rho W y$ &amp;mdash; how much a state&amp;rsquo;s consumption is influenced by its neighbors' consumption, (2) the &lt;strong>own effects&lt;/strong> of price and income $X \beta$, and (3) the &lt;strong>spatial lags of the explanatory variables&lt;/strong> $W X \theta$ &amp;mdash; how neighbors' prices and incomes spill over. The parameters $\mu_i$ and $\lambda_t$ are state and year fixed effects, respectively.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Symbol&lt;/th>
&lt;th>Meaning&lt;/th>
&lt;th>Code variable&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>$y_{it}$&lt;/td>
&lt;td>Log cigarette consumption in state $i$, year $t$&lt;/td>
&lt;td>&lt;code>logc&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$\rho$&lt;/td>
&lt;td>Spatial autoregressive parameter (neighbor consumption effect)&lt;/td>
&lt;td>&lt;code>[Spatial]rho&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$w_{ij}$&lt;/td>
&lt;td>Element of the row-standardized weight matrix&lt;/td>
&lt;td>&lt;code>Wst&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$x_{it}$&lt;/td>
&lt;td>Own price and income&lt;/td>
&lt;td>&lt;code>logp&lt;/code>, &lt;code>logy&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$\beta$&lt;/td>
&lt;td>Own-variable coefficients&lt;/td>
&lt;td>&lt;code>[Main]logp&lt;/code>, &lt;code>[Main]logy&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$\theta$&lt;/td>
&lt;td>Spatial lag coefficients (neighbor effects of X)&lt;/td>
&lt;td>&lt;code>[Wx]logp&lt;/code>, &lt;code>[Wx]logy&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>A key advantage of the SDM is that it &lt;strong>nests&lt;/strong> three simpler spatial models as special cases. This means we can start with the general SDM and then test whether the data supports reducing it to a simpler specification.&lt;/p>
&lt;pre>&lt;code class="language-mermaid">graph TD
SDM[&amp;quot;&amp;lt;b&amp;gt;Spatial Durbin Model (SDM)&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;y = ρWy + Xβ + WXθ + ε&amp;lt;br/&amp;gt;&amp;lt;i&amp;gt;Most general&amp;lt;/i&amp;gt;&amp;quot;]
SAR[&amp;quot;&amp;lt;b&amp;gt;SAR&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;y = ρWy + Xβ + ε&amp;lt;br/&amp;gt;&amp;lt;i&amp;gt;θ = 0&amp;lt;/i&amp;gt;&amp;quot;]
SLX[&amp;quot;&amp;lt;b&amp;gt;SLX&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;y = Xβ + WXθ + ε&amp;lt;br/&amp;gt;&amp;lt;i&amp;gt;ρ = 0&amp;lt;/i&amp;gt;&amp;quot;]
SEM[&amp;quot;&amp;lt;b&amp;gt;SEM&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;y = Xβ + u, u = λWu + ε&amp;lt;br/&amp;gt;&amp;lt;i&amp;gt;θ + ρβ = 0&amp;lt;/i&amp;gt;&amp;quot;]
SDM --&amp;gt;|&amp;quot;θ = 0?&amp;quot;| SAR
SDM --&amp;gt;|&amp;quot;ρ = 0?&amp;quot;| SLX
SDM --&amp;gt;|&amp;quot;θ + ρβ = 0?&amp;quot;| SEM
style SDM fill:#00d4c8,stroke:#141413,color:#141413
style SAR fill:#6a9bcc,stroke:#141413,color:#fff
style SLX fill:#d97757,stroke:#141413,color:#fff
style SEM fill:#141413,stroke:#d97757,color:#fff
&lt;/code>&lt;/pre>
&lt;p>The &lt;strong>SAR&lt;/strong> (Spatial Autoregressive) model restricts $\theta = 0$, assuming that only neighbors' consumption (not their prices or incomes) matters. The &lt;strong>SLX&lt;/strong> (Spatial Lag of X) model restricts $\rho = 0$, assuming that neighbors' characteristics affect local consumption but there is no autoregressive feedback. The &lt;strong>SEM&lt;/strong> (Spatial Error Model) imposes the common factor restriction $\theta + \rho \beta = 0$, implying that spatial dependence operates entirely through correlated errors rather than substantive spillovers. In Section 7, we will use Wald tests to determine which, if any, of these restrictions the data supports.&lt;/p>
&lt;hr>
&lt;h2 id="6-spatial-durbin-model-sdm">6. Spatial Durbin Model (SDM)&lt;/h2>
&lt;h3 id="61-sdm-with-two-way-fixed-effects">6.1 SDM with two-way fixed effects&lt;/h3>
&lt;p>We now estimate the full Spatial Durbin Model with both state and year fixed effects. The &lt;code>xsmle&lt;/code> command performs maximum likelihood estimation for spatial panel models. The option &lt;code>type(both)&lt;/code> specifies two-way fixed effects, &lt;code>mod(sdm)&lt;/code> selects the Spatial Durbin specification, and &lt;code>effects nsim(999)&lt;/code> computes direct and indirect effects using 999 Monte Carlo simulations.&lt;/p>
&lt;pre>&lt;code class="language-stata">xsmle logc logp logy, fe type(both) wmat(Wst) mod(sdm) effects nsim(999) nolog
estimates store sdm1
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Spatial Durbin model with fixed-effects Number of obs = 1,380
Group variable: state Number of groups = 46
Time variable: year
Obs per group:
min = 30
avg = 30.0
max = 30
Wald chi2(4) = 379.19
Log-likelihood = 1971.5204 Prob &amp;gt; chi2 = 0.0000
------------------------------------------------------------------------------
logc | Coefficient Std. err. z P&amp;gt;|z| [95% conf. interval]
-------------+----------------------------------------------------------------
Main |
logp | -.3068973 .0282114 -10.88 0.000 -.3621907 -.2516039
logy | .0781427 .0481269 1.62 0.104 -.0161843 .1724697
-------------+----------------------------------------------------------------
Wx |
logp | -.2060671 .0649703 -3.17 0.002 -.3334065 -.0787277
logy | .1803542 .0885162 2.04 0.042 .0068656 .3538428
-------------+----------------------------------------------------------------
Spatial |
rho | .2649571 .0327948 8.08 0.000 .2006804 .3292339
-------------+----------------------------------------------------------------
sigma2_e| .0027866
------------------------------------------------------------------------------
Direct | -.3131508 .0285649 -10.96 0.000 -.3691370 -.2571645
Indirect | -.3138174 .0812337 -3.86 0.000 -.4730325 -.1546023
Total | -.6269682 .0866710 -7.23 0.000 -.7968403 -.4570961
|
Direct | .0941302 .0488720 1.93 0.054 -.0016572 .1899176
Indirect | .2683417 .1099814 2.44 0.015 .0527821 .4839013
Total | .3624719 .1216523 2.98 0.003 .1240378 .6009060
&lt;/code>&lt;/pre>
&lt;p>The spatial autoregressive parameter $\rho$ is &lt;strong>0.265&lt;/strong> (z = 8.08, p &amp;lt; 0.001), indicating substantial positive spatial dependence &amp;mdash; states with higher-consuming neighbors tend to consume more themselves, even after controlling for own prices and income. The own price coefficient (&lt;code>[Main]logp&lt;/code>) is -0.307, while the spatial lag of neighbors' prices (&lt;code>[Wx]logp&lt;/code>) is -0.206, meaning that higher prices in neighboring states also reduce local consumption. This is consistent with the cross-border shopping hypothesis: when neighbors' prices rise, there are fewer opportunities for local consumers to shop across borders, reinforcing the local price effect.&lt;/p>
&lt;p>The &lt;strong>direct effect&lt;/strong> of price is -0.313, meaning that a 1% increase in a state&amp;rsquo;s own price reduces its consumption by 0.31%. The &lt;strong>indirect (spillover) effect&lt;/strong> of price is -0.314, nearly as large as the direct effect. This means that when all neighboring states raise prices by 1%, the resulting reduction in consumption in the focal state is comparable to the state raising its own price. The &lt;strong>total effect&lt;/strong> of price is -0.627 &amp;mdash; much larger than the two-way FE estimate of -0.402, revealing that non-spatial models substantially underestimate the true price sensitivity of cigarette demand.&lt;/p>
&lt;h3 id="62-lee-and-yu-bias-correction">6.2 Lee and Yu bias correction&lt;/h3>
&lt;p>In spatial panels with fixed effects, the maximum likelihood estimator suffers from the &lt;strong>incidental parameters problem&lt;/strong> &amp;mdash; the number of fixed effect parameters grows with the number of states, which introduces a bias term of order $1/T$. With $T = 30$ years, this bias may be non-negligible. Lee and Yu (2010) proposed a bias correction procedure that adjusts the ML estimates to eliminate the leading bias term.&lt;/p>
&lt;pre>&lt;code class="language-stata">xsmle logc logp logy, fe type(both) leeyu wmat(Wst) mod(sdm) effects nsim(999) nolog
estimates store sdm2
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Spatial Durbin model with fixed-effects (Lee-Yu) Number of obs = 1,334
Group variable: state Number of groups = 46
Time variable: year
Obs per group:
min = 29
avg = 29.0
max = 29
Wald chi2(4) = 392.50
Log-likelihood = 1932.4681 Prob &amp;gt; chi2 = 0.0000
------------------------------------------------------------------------------
logc | Coefficient Std. err. z P&amp;gt;|z| [95% conf. interval]
-------------+----------------------------------------------------------------
Main |
logp | -.3044782 .0283901 -10.72 0.000 -.3601218 -.2488346
logy | .0770150 .0486311 1.58 0.113 -.0183001 .1723301
-------------+----------------------------------------------------------------
Wx |
logp | -.2083124 .0654876 -3.18 0.001 -.3366657 -.0799591
logy | .1869831 .0894718 2.09 0.037 .0116216 .3623446
-------------+----------------------------------------------------------------
Spatial |
rho | .2596348 .0332441 7.81 0.000 .1944776 .3247920
-------------+----------------------------------------------------------------
sigma2_e| .0027512
------------------------------------------------------------------------------
Direct | -.3104271 .0287814 -10.79 0.000 -.3668377 -.2540166
Indirect | -.3122946 .0825781 -3.78 0.000 -.4741447 -.1504446
Total | -.6227218 .0878439 -7.09 0.000 -.7948927 -.4505509
|
Direct | .0935487 .0494610 1.89 0.059 -.0033931 .1904905
Indirect | .2739264 .1115282 2.46 0.014 .0553351 .4925177
Total | .3674751 .1235608 2.97 0.003 .1253004 .6096498
&lt;/code>&lt;/pre>
&lt;p>The Lee-Yu correction uses $N \times (T-1) = 46 \times 29 = 1{,}334$ observations (one time period is lost in the transformation). The corrected estimates are very close to the uncorrected ones: $\rho$ changes from 0.265 to &lt;strong>0.260&lt;/strong>, the own price coefficient from -0.307 to -0.304, and the total price effect from -0.627 to &lt;strong>-0.623&lt;/strong>. This stability is reassuring &amp;mdash; with $T = 30$, the bias is already small. The closeness of the two sets of estimates provides confidence that the standard ML estimates are reliable for this dataset.&lt;/p>
&lt;h3 id="63-comparison">6.3 Comparison&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;/th>
&lt;th>SDM (standard)&lt;/th>
&lt;th>SDM (Lee-Yu)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>$\rho$&lt;/td>
&lt;td>0.265***&lt;/td>
&lt;td>0.260***&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>logp&lt;/code> (own)&lt;/td>
&lt;td>-0.307***&lt;/td>
&lt;td>-0.304***&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>logy&lt;/code> (own)&lt;/td>
&lt;td>0.078&lt;/td>
&lt;td>0.077&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>W*logp&lt;/code> (neighbors)&lt;/td>
&lt;td>-0.206***&lt;/td>
&lt;td>-0.208***&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>W*logy&lt;/code> (neighbors)&lt;/td>
&lt;td>0.180**&lt;/td>
&lt;td>0.187**&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Direct price effect&lt;/td>
&lt;td>-0.313***&lt;/td>
&lt;td>-0.310***&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Indirect price effect&lt;/td>
&lt;td>-0.314***&lt;/td>
&lt;td>-0.312***&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Total price effect&lt;/td>
&lt;td>-0.627***&lt;/td>
&lt;td>-0.623***&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The two sets of estimates are nearly identical, confirming that the incidental parameters bias is negligible with 30 time periods. For the remainder of this tutorial, we use the Lee-Yu corrected estimates as our preferred specification.&lt;/p>
&lt;hr>
&lt;h2 id="7-wald-specification-tests">7. Wald specification tests&lt;/h2>
&lt;p>The SDM is the most general model in the spatial panel family, nesting SAR, SLX, and SEM as special cases. Before accepting the full SDM, we should test whether the data supports a simpler specification. We do this by testing the parameter restrictions that define each nested model. If the restrictions are rejected, the simpler model is inadequate and we should retain the SDM.&lt;/p>
&lt;p>We first re-estimate the SDM with the Lee-Yu correction (the &lt;code>quietly&lt;/code> prefix suppresses output since we already displayed these results).&lt;/p>
&lt;pre>&lt;code class="language-stata">quietly xsmle logc logp logy, fe type(both) leeyu wmat(Wst) mod(sdm) effects nsim(999) nolog
&lt;/code>&lt;/pre>
&lt;h3 id="71-can-the-sdm-reduce-to-sar">7.1 Can the SDM reduce to SAR?&lt;/h3>
&lt;p>The SAR model restricts $\theta = 0$ &amp;mdash; that is, the spatial lags of the explanatory variables are zero. Under SAR, only neighbors' consumption matters, not their prices or incomes directly. We test this with a joint Wald test on the &lt;code>[Wx]&lt;/code> coefficients.&lt;/p>
&lt;pre>&lt;code class="language-stata">* Wald test: Reduce to SAR? (NO if p &amp;lt; 0.05)
test ([Wx]logp = 0) ([Wx]logy = 0)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text"> ( 1) [Wx]logp = 0
( 2) [Wx]logy = 0
chi2( 2) = 12.87
Prob &amp;gt; chi2 = 0.0016
&lt;/code>&lt;/pre>
&lt;p>The Wald test &lt;strong>rejects&lt;/strong> the SAR restriction (chi2 = 12.87, p = 0.002). This means that neighbors' prices and incomes have direct effects on local consumption beyond their influence through the spatial lag of consumption. Dropping the $WX$ terms from the model would misspecify the spatial dependence structure.&lt;/p>
&lt;h3 id="72-can-the-sdm-reduce-to-slx">7.2 Can the SDM reduce to SLX?&lt;/h3>
&lt;p>The SLX model restricts $\rho = 0$ &amp;mdash; there is no spatial autoregressive feedback through the dependent variable. Under SLX, neighbors' characteristics affect local consumption directly, but the spatial multiplier effect (where shocks propagate through the network) is absent.&lt;/p>
&lt;pre>&lt;code class="language-stata">* Wald test: Reduce to SLX? (NO if p &amp;lt; 0.05)
test ([Spatial]rho = 0)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text"> ( 1) [Spatial]rho = 0
chi2( 1) = 61.04
Prob &amp;gt; chi2 = 0.0000
&lt;/code>&lt;/pre>
&lt;p>The Wald test &lt;strong>overwhelmingly rejects&lt;/strong> the SLX restriction (chi2 = 61.04, p &amp;lt; 0.001). The spatial autoregressive parameter $\rho$ is far from zero, confirming that there is a genuine feedback mechanism: a shock to consumption in one state propagates to its neighbors, which in turn affects their neighbors, creating a spatial multiplier.&lt;/p>
&lt;h3 id="73-can-the-sdm-reduce-to-sem">7.3 Can the SDM reduce to SEM?&lt;/h3>
&lt;p>The SEM (Spatial Error Model) imposes the common factor restriction $\theta + \rho \beta = 0$. Under this restriction, the spatial dependence is purely a &lt;strong>nuisance&lt;/strong> &amp;mdash; it enters through correlated error terms rather than through substantive economic spillovers. If SEM is adequate, the apparent spillover effects are an artifact of omitted spatially correlated variables, not genuine cross-border interactions.&lt;/p>
&lt;pre>&lt;code class="language-stata">* Wald test: Reduce to SEM? (NO if p &amp;lt; 0.05)
testnl ([Wx]logp = -[Spatial]rho*[Main]logp) ([Wx]logy = -[Spatial]rho*[Main]logy)
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text"> (1) [Wx]logp = -[Spatial]rho*[Main]logp
(2) [Wx]logy = -[Spatial]rho*[Main]logy
chi2( 2) = 8.49
Prob &amp;gt; chi2 = 0.0143
&lt;/code>&lt;/pre>
&lt;p>The Wald test &lt;strong>rejects&lt;/strong> the SEM common factor restriction (chi2 = 8.49, p = 0.014). The spatial dependence in cigarette demand is not merely a nuisance in the error term &amp;mdash; it reflects &lt;strong>substantive economic spillovers&lt;/strong> across state borders. This is exactly what economic theory predicts: cross-border shopping creates genuine causal links between neighboring states' prices and local consumption.&lt;/p>
&lt;h3 id="74-summary-of-specification-tests">7.4 Summary of specification tests&lt;/h3>
&lt;pre>&lt;code class="language-mermaid">graph TD
SDM[&amp;quot;&amp;lt;b&amp;gt;Spatial Durbin Model (SDM)&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;RETAINED&amp;quot;]
SAR[&amp;quot;&amp;lt;b&amp;gt;SAR&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;θ = 0&amp;lt;br/&amp;gt;Rejected&amp;lt;br/&amp;gt;p = 0.002&amp;quot;]
SLX[&amp;quot;&amp;lt;b&amp;gt;SLX&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;ρ = 0&amp;lt;br/&amp;gt;Rejected&amp;lt;br/&amp;gt;p &amp;lt; 0.001&amp;quot;]
SEM[&amp;quot;&amp;lt;b&amp;gt;SEM&amp;lt;/b&amp;gt;&amp;lt;br/&amp;gt;θ + ρβ = 0&amp;lt;br/&amp;gt;Rejected&amp;lt;br/&amp;gt;p = 0.014&amp;quot;]
SDM --&amp;gt;|&amp;quot;chi2 = 12.87&amp;quot;| SAR
SDM --&amp;gt;|&amp;quot;chi2 = 61.04&amp;quot;| SLX
SDM --&amp;gt;|&amp;quot;chi2 = 8.49&amp;quot;| SEM
style SDM fill:#00d4c8,stroke:#141413,color:#141413
style SAR fill:#d97757,stroke:#141413,color:#fff
style SLX fill:#d97757,stroke:#141413,color:#fff
style SEM fill:#d97757,stroke:#141413,color:#fff
&lt;/code>&lt;/pre>
&lt;p>All three Wald tests reject the restricted models. The SDM cannot be simplified to SAR (neighbors' X variables matter), SLX (the autoregressive feedback matters), or SEM (the spatial dependence is substantive, not a nuisance). The &lt;strong>full SDM is the appropriate specification&lt;/strong> for modeling cigarette demand across US states. This result confirms that spatial spillovers in cigarette consumption operate through multiple channels simultaneously: direct cross-border effects of neighbors' prices and incomes, and feedback effects through the spatial lag of consumption itself.&lt;/p>
&lt;hr>
&lt;h2 id="8-dynamic-spatial-panel-models">8. Dynamic spatial panel models&lt;/h2>
&lt;p>Cigarette consumption is well known to be &lt;strong>habit-forming&lt;/strong> &amp;mdash; past consumption is a strong predictor of current consumption because of nicotine addiction. Standard (static) spatial models ignore this temporal persistence, which may bias the spatial parameter estimates. Dynamic spatial panel models extend the SDM by including lagged values of consumption, allowing us to separate habit persistence from spatial spillovers.&lt;/p>
&lt;p>The &lt;code>xsmle&lt;/code> package supports three dynamic specifications through the &lt;code>dlag()&lt;/code> option:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;code>dlag()&lt;/code>&lt;/th>
&lt;th>Dynamic term added&lt;/th>
&lt;th>Interpretation&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>1&lt;/td>
&lt;td>$\tau \cdot y_{i,t-1}$&lt;/td>
&lt;td>Temporal lag: own past consumption&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>2&lt;/td>
&lt;td>$\psi \cdot \sum_j w_{ij} y_{j,t-1}$&lt;/td>
&lt;td>Spatiotemporal lag: neighbors' past consumption&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>3&lt;/td>
&lt;td>Both $\tau \cdot y_{i,t-1}$ and $\psi \cdot \sum_j w_{ij} y_{j,t-1}$&lt;/td>
&lt;td>Full dynamic: own + neighbors' past consumption&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The most general dynamic SDM (with &lt;code>dlag(3)&lt;/code>) extends the static equation from Section 5 by adding two lagged terms:&lt;/p>
&lt;p>$$y_{it} = \tau \, y_{i,t-1} + \psi \sum_{j=1}^{N} w_{ij} \, y_{j,t-1} + \rho \sum_{j=1}^{N} w_{ij} \, y_{jt} + x_{it} \beta + \sum_{j=1}^{N} w_{ij} \, x_{jt} \theta + \mu_i + \lambda_t + \varepsilon_{it}$$&lt;/p>
&lt;p>In words, this equation says that a state&amp;rsquo;s cigarette consumption depends on its &lt;strong>own past consumption&lt;/strong> ($\tau y_{i,t-1}$, capturing habit persistence), the &lt;strong>average past consumption of its neighbors&lt;/strong> ($\psi W y_{t-1}$, capturing spatiotemporal diffusion), and all the contemporaneous spatial terms from the static SDM. The parameter $\tau$ measures how strongly last year&amp;rsquo;s smoking predicts this year&amp;rsquo;s &amp;mdash; think of it as the &amp;ldquo;addiction coefficient.&amp;rdquo; The parameter $\psi$ captures whether neighbors' past behavior diffuses across borders over time.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Symbol&lt;/th>
&lt;th>Meaning&lt;/th>
&lt;th>Code variable&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>$\tau$&lt;/td>
&lt;td>Temporal lag (habit persistence)&lt;/td>
&lt;td>&lt;code>[Temporal]tau&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$\psi$&lt;/td>
&lt;td>Spatiotemporal lag (neighbors' past consumption)&lt;/td>
&lt;td>&lt;code>[Temporal]psi&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$y_{i,t-1}$&lt;/td>
&lt;td>Own consumption last year&lt;/td>
&lt;td>&lt;code>dlag(1)&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$W y_{t-1}$&lt;/td>
&lt;td>Average neighbors' consumption last year&lt;/td>
&lt;td>&lt;code>dlag(2)&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="81-non-dynamic-sdm-baseline">8.1 Non-dynamic SDM (baseline)&lt;/h3>
&lt;p>We re-estimate the static SDM as a baseline for comparison with the dynamic specifications.&lt;/p>
&lt;pre>&lt;code class="language-stata">xsmle logc logp logy, fe type(both) wmat(Wst) mod(sdm) effects nsim(999) nolog
eststo SDM0
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Spatial Durbin model with fixed-effects Number of obs = 1,380
------------------------------------------------------------------------------
logc | Coefficient Std. err. z P&amp;gt;|z| [95% conf. interval]
-------------+----------------------------------------------------------------
Main |
logp | -.3068973 .0282114 -10.88 0.000 -.3621907 -.2516039
logy | .0781427 .0481269 1.62 0.104 -.0161843 .1724697
Wx |
logp | -.2060671 .0649703 -3.17 0.002 -.3334065 -.0787277
logy | .1803542 .0885162 2.04 0.042 .0068656 .3538428
Spatial |
rho | .2649571 .0327948 8.08 0.000 .2006804 .3292339
------------------------------------------------------------------------------
&lt;/code>&lt;/pre>
&lt;h3 id="82-dynamic-sdm-with-temporal-lag-tau-cdot-y_it-1">8.2 Dynamic SDM with temporal lag ($\tau \cdot y_{i,t-1}$)&lt;/h3>
&lt;p>Adding the temporal lag of own consumption captures habit persistence &amp;mdash; the tendency for this year&amp;rsquo;s smoking to depend on last year&amp;rsquo;s smoking, holding prices and income constant.&lt;/p>
&lt;pre>&lt;code class="language-stata">xsmle logc logp logy, dlag(1) fe type(both) wmat(Wst) mod(sdm) effects nsim(999) nolog
eststo dySDM1
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Dynamic Spatial Durbin model with fixed-effects Number of obs = 1,334
------------------------------------------------------------------------------
logc | Coefficient Std. err. z P&amp;gt;|z| [95% conf. interval]
-------------+----------------------------------------------------------------
Main |
logp | -.1516305 .0226714 -6.69 0.000 -.1960657 -.1071954
logy | .0285493 .0376124 0.76 0.448 -.0451697 .1022683
Wx |
logp | -.0714289 .0521683 -1.37 0.171 -.1736769 .0308190
logy | .0592735 .0706984 0.84 0.402 -.0792929 .1978399
Spatial |
rho | .1021753 .0307624 3.32 0.001 .0418821 .1624685
Temporal |
tau | .6543218 .0196285 33.33 0.000 .6158507 .6927928
------------------------------------------------------------------------------
&lt;/code>&lt;/pre>
&lt;p>The temporal lag coefficient $\tau$ is &lt;strong>0.654&lt;/strong> (z = 33.33, p &amp;lt; 0.001) &amp;mdash; a very strong habit persistence effect. Controlling for last year&amp;rsquo;s consumption dramatically reduces the other coefficients: the own price effect drops from -0.307 to &lt;strong>-0.152&lt;/strong>, and the spatial autoregressive parameter $\rho$ falls from 0.265 to &lt;strong>0.102&lt;/strong>. This means that much of the apparent spatial dependence in the static SDM was actually capturing &lt;strong>temporal autocorrelation&lt;/strong> that manifests spatially. The spatial lag of neighbors' prices (&lt;code>[Wx]logp&lt;/code>) becomes insignificant (p = 0.171), suggesting that once habit persistence is controlled for, the direct cross-border price spillover weakens considerably.&lt;/p>
&lt;h3 id="83-dynamic-sdm-with-spatiotemporal-lag-psi-cdot-w-cdot-y_it-1">8.3 Dynamic SDM with spatiotemporal lag ($\psi \cdot W \cdot y_{i,t-1}$)&lt;/h3>
&lt;p>Instead of own past consumption, this specification includes the spatial lag of past consumption &amp;mdash; how much neighbors smoked last year.&lt;/p>
&lt;pre>&lt;code class="language-stata">xsmle logc logp logy, dlag(2) fe type(both) wmat(Wst) mod(sdm) effects nsim(999) nolog
eststo dySDM2
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Dynamic Spatial Durbin model with fixed-effects Number of obs = 1,334
------------------------------------------------------------------------------
logc | Coefficient Std. err. z P&amp;gt;|z| [95% conf. interval]
-------------+----------------------------------------------------------------
Main |
logp | -.2981475 .0280193 -10.64 0.000 -.3530643 -.2432307
logy | .0637218 .0478561 1.33 0.183 -.0300745 .1575181
Wx |
logp | -.1425379 .0647518 -2.20 0.028 -.2694490 -.0156268
logy | .1320869 .0888243 1.49 0.137 -.0420055 .3061793
Spatial |
rho | .1523264 .0369871 4.12 0.000 .0798330 .2248199
Temporal |
psi | .2712508 .0339714 7.98 0.000 .2046680 .3378335
------------------------------------------------------------------------------
&lt;/code>&lt;/pre>
&lt;p>The spatiotemporal lag coefficient $\psi$ is &lt;strong>0.271&lt;/strong> (z = 7.98, p &amp;lt; 0.001), indicating that neighbors' past consumption does have a positive effect on current consumption. However, this effect is weaker than the own temporal lag ($\tau = 0.654$ in the previous specification). The spatial autoregressive parameter drops to $\rho = 0.152$, and the own price coefficient stays close to the static SDM value at -0.298.&lt;/p>
&lt;h3 id="84-full-dynamic-sdm-tau-cdot-y_it-1--psi-cdot-w-cdot-y_it-1">8.4 Full dynamic SDM ($\tau \cdot y_{i,t-1} + \psi \cdot W \cdot y_{i,t-1}$)&lt;/h3>
&lt;p>The most general dynamic specification includes both the temporal lag and the spatiotemporal lag.&lt;/p>
&lt;pre>&lt;code class="language-stata">xsmle logc logp logy, dlag(3) fe type(both) wmat(Wst) mod(sdm) effects nsim(999) nolog
eststo dySDM3
&lt;/code>&lt;/pre>
&lt;pre>&lt;code class="language-text">Dynamic Spatial Durbin model with fixed-effects Number of obs = 1,334
------------------------------------------------------------------------------
logc | Coefficient Std. err. z P&amp;gt;|z| [95% conf. interval]
-------------+----------------------------------------------------------------
Main |
logp | -.1498627 .0226523 -6.62 0.000 -.1942603 -.1054651
logy | .0271398 .0376004 0.72 0.470 -.0465556 .1008351
Wx |
logp | -.0636842 .0524156 -1.21 0.224 -.1664169 .0390485
logy | .0471982 .0712803 0.66 0.508 -.0925087 .1869052
Spatial |
rho | .0803516 .0322458 2.49 0.013 .0171509 .1435524
Temporal |
tau | .6389621 .0208541 30.64 0.000 .5980889 .6798353
psi | .0494172 .0325896 1.52 0.130 -.0144571 .1132915
------------------------------------------------------------------------------
&lt;/code>&lt;/pre>
&lt;p>In the full dynamic model, the temporal lag dominates: $\tau = 0.639$ (z = 30.64, p &amp;lt; 0.001), while the spatiotemporal lag $\psi = 0.049$ is &lt;strong>not statistically significant&lt;/strong> (p = 0.130). This indicates that a state&amp;rsquo;s own past consumption is the primary driver of temporal persistence, and neighbors' past consumption does not add meaningful additional information once own habit persistence is controlled for. The spatial autoregressive parameter further drops to $\rho = 0.080$, and the spatial lags of price and income become insignificant.&lt;/p>
&lt;h3 id="85-comparison-of-dynamic-models">8.5 Comparison of dynamic models&lt;/h3>
&lt;pre>&lt;code class="language-stata">esttab SDM0 dySDM1 dySDM2 dySDM3, mtitle(&amp;quot;SDM&amp;quot; &amp;quot;dySDM1&amp;quot; &amp;quot;dySDM2&amp;quot; &amp;quot;dySDM3&amp;quot;)
&lt;/code>&lt;/pre>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>&lt;/th>
&lt;th>SDM (static)&lt;/th>
&lt;th>dySDM1 ($\tau$)&lt;/th>
&lt;th>dySDM2 ($\psi$)&lt;/th>
&lt;th>dySDM3 ($\tau + \psi$)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>logp&lt;/code> (own)&lt;/td>
&lt;td>-0.307***&lt;/td>
&lt;td>-0.152***&lt;/td>
&lt;td>-0.298***&lt;/td>
&lt;td>-0.150***&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>logy&lt;/code> (own)&lt;/td>
&lt;td>0.078&lt;/td>
&lt;td>0.029&lt;/td>
&lt;td>0.064&lt;/td>
&lt;td>0.027&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>W*logp&lt;/code>&lt;/td>
&lt;td>-0.206***&lt;/td>
&lt;td>-0.071&lt;/td>
&lt;td>-0.143**&lt;/td>
&lt;td>-0.064&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>W*logy&lt;/code>&lt;/td>
&lt;td>0.180**&lt;/td>
&lt;td>0.059&lt;/td>
&lt;td>0.132&lt;/td>
&lt;td>0.047&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$\rho$&lt;/td>
&lt;td>0.265***&lt;/td>
&lt;td>0.102***&lt;/td>
&lt;td>0.152***&lt;/td>
&lt;td>0.080**&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$\tau$ (own lag)&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>0.654***&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>0.639***&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$\psi$ (spatial lag)&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>&amp;mdash;&lt;/td>
&lt;td>0.271***&lt;/td>
&lt;td>0.049&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The comparison reveals a clear pattern. First, &lt;strong>habit persistence is the dominant dynamic force&lt;/strong>: $\tau$ is large and highly significant whether estimated alone (0.654) or jointly with $\psi$ (0.639), while $\psi$ loses significance once $\tau$ is included. Second, &lt;strong>controlling for habit persistence substantially attenuates spatial spillover estimates&lt;/strong>: the spatial autoregressive parameter $\rho$ falls from 0.265 (static) to 0.080 (full dynamic), and the spatial lags of price and income become insignificant. This suggests that the static SDM&amp;rsquo;s spillover estimates partly capture omitted temporal dynamics. Third, the &lt;strong>short-run price elasticity&lt;/strong> in the dynamic model (-0.150) is about half the static estimate (-0.307), but the long-run price elasticity &amp;mdash; computed as $\beta / (1 - \tau)$ &amp;mdash; is approximately $-0.150 / (1 - 0.639) = -0.416$, close to the static estimate. The static SDM conflates short-run and long-run responses into a single coefficient.&lt;/p>
&lt;hr>
&lt;h2 id="9-discussion">9. Discussion&lt;/h2>
&lt;p>This tutorial demonstrates that &lt;strong>spatial dependence matters&lt;/strong> for modeling cigarette demand across US states. The Wald tests in Section 7 conclusively reject all three restricted spatial models (SAR, SLX, SEM), confirming that the Spatial Durbin Model is the appropriate specification. The total price effect in the static SDM (-0.627) is more than 50% larger than the two-way FE estimate (-0.402), revealing that non-spatial models systematically understate the true price sensitivity of cigarette demand by ignoring cross-border spillovers.&lt;/p>
&lt;p>The dynamic extensions in Section 8 provide important nuance. Once habit persistence is controlled for ($\tau \approx 0.65$), the spatial autoregressive parameter drops by two-thirds (from 0.265 to 0.080), and many spatial lag coefficients lose statistical significance. This does not mean spatial dependence is unimportant &amp;mdash; rather, it means that the &lt;strong>static SDM conflates temporal and spatial dynamics&lt;/strong>. In the dynamic model, the short-run own price elasticity is -0.15 and the long-run elasticity is approximately -0.42, offering policymakers a clearer picture of how quickly cigarette taxation takes effect.&lt;/p>
&lt;p>From a policy perspective, these results carry a direct implication: &lt;strong>state-level tobacco taxation has cross-border spillover effects that policymakers must consider&lt;/strong>. When a single state raises its cigarette tax, the demand reduction is partially offset by cross-border shopping. However, when neighboring states raise taxes simultaneously, the total demand reduction is amplified. This supports the case for coordinated regional or federal tobacco taxation rather than isolated state-level policies. The finding that habit persistence is the dominant dynamic force ($\tau \approx 0.65$) also suggests that the full impact of a tax increase takes several years to materialize, as consumers slowly adjust their consumption habits.&lt;/p>
&lt;hr>
&lt;h2 id="10-summary-and-next-steps">10. Summary and next steps&lt;/h2>
&lt;p>This tutorial covered the complete workflow for spatial panel regression in Stata &amp;mdash; from loading a spatial weight matrix and estimating non-spatial benchmarks, through the full Spatial Durbin Model with Wald specification tests, to dynamic spatial extensions. The key takeaways are:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Non-spatial models understate price sensitivity.&lt;/strong> The two-way FE price elasticity is -0.40, but the SDM total effect is -0.63 &amp;mdash; a 57% increase that reflects cross-border spillovers ignored by standard panel models.&lt;/li>
&lt;li>&lt;strong>The SDM cannot be simplified.&lt;/strong> All three Wald tests reject the SAR, SLX, and SEM restrictions, meaning that spatial dependence operates through multiple channels simultaneously: neighbors' consumption ($\rho$), neighbors' prices ($\theta_{logp}$), and neighbors' income ($\theta_{logy}$).&lt;/li>
&lt;li>&lt;strong>Habit persistence dominates temporal dynamics.&lt;/strong> The temporal lag coefficient $\tau \approx 0.65$ is large and robust, while the spatiotemporal lag $\psi$ loses significance once $\tau$ is included. Static spatial models overstate contemporaneous spillovers by absorbing temporal autocorrelation.&lt;/li>
&lt;li>&lt;strong>Short-run vs. long-run elasticities differ substantially.&lt;/strong> The dynamic SDM&amp;rsquo;s short-run price elasticity (-0.15) is less than half its long-run counterpart (-0.42), information that is lost in static specifications.&lt;/li>
&lt;/ul>
&lt;p>For further study, consider applying these methods to other spatial datasets or exploring alternative spatial specifications. The companion tutorial on &lt;a href="https://carlos-mendez.org/post/stata_sp_regression_cross_section/">cross-sectional spatial regression&lt;/a> covers the spatial models available for single-period data, including the full taxonomy of SAR, SEM, SLX, SDM, SDEM, and SAC models. For datasets where unobserved common factors (macroeconomic shocks, regulatory changes) may drive cross-sectional dependence beyond what the spatial weight matrix captures, see the &lt;a href="https://carlos-mendez.org/post/stata_spxtivdfreg/">spatial dynamic panels with common factors&lt;/a> tutorial, which uses the &lt;code>spxtivdfreg&lt;/code> package to combine spatial lags with defactored IV estimation. For Python implementations of spatial econometrics, see the PySAL ecosystem and the &lt;code>spreg&lt;/code> package.&lt;/p>
&lt;hr>
&lt;h2 id="11-exercises">11. Exercises&lt;/h2>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Alternative weight matrix.&lt;/strong> Replace the binary contiguity matrix with an inverse-distance weight matrix. Re-estimate the SDM and compare the spatial autoregressive parameter $\rho$ and the indirect effects. Does the choice of weight matrix change the substantive conclusions about cross-border spillovers?&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>SAR vs. SDM direct comparison.&lt;/strong> Estimate a SAR model (&lt;code>mod(sar)&lt;/code> in &lt;code>xsmle&lt;/code>) with two-way fixed effects and the Lee-Yu correction. Compare its price elasticity to the SDM. Given that the Wald test rejected the SAR restriction, how different are the elasticity estimates in practice?&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Subsample analysis.&lt;/strong> Split the sample into two periods (1963&amp;ndash;1977 and 1978&amp;ndash;1992) and estimate the SDM separately for each. Did the spatial dependence structure of cigarette demand change over time? What historical events (e.g., the Surgeon General&amp;rsquo;s reports, the rise of anti-smoking legislation) might explain differences between the two periods?&lt;/p>
&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="references">References&lt;/h2>
&lt;ol>
&lt;li>&lt;a href="https://link.springer.com/book/10.1007/978-3-030-53953-5" target="_blank" rel="noopener">Baltagi, B. H. (2021). &lt;em>Econometric Analysis of Panel Data&lt;/em> (6th ed.). Springer.&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://link.springer.com/book/10.1007/978-3-642-40340-8" target="_blank" rel="noopener">Elhorst, J. P. (2014). &lt;em>Spatial Econometrics: From Cross-Sectional Data to Spatial Panels&lt;/em>. Springer.&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://doi.org/10.1201/9781420064254" target="_blank" rel="noopener">LeSage, J. P. &amp;amp; Pace, R. K. (2009). &lt;em>Introduction to Spatial Econometrics&lt;/em>. Chapman &amp;amp; Hall/CRC.&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://doi.org/10.1016/j.jeconom.2009.08.001" target="_blank" rel="noopener">Lee, L. F. &amp;amp; Yu, J. (2010). Estimation of spatial autoregressive panel data models with fixed effects. &lt;em>Journal of Econometrics&lt;/em>, 154(2), 165&amp;ndash;185.&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://doi.org/10.1177/1536867X1701700109" target="_blank" rel="noopener">Belotti, F., Hughes, G., &amp;amp; Mortari, A. P. (2017). Spatial panel-data models using Stata. &lt;em>Stata Journal&lt;/em>, 17(1), 139&amp;ndash;180.&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/quarcs-lab/data-open/tree/master/cigar" target="_blank" rel="noopener">Baltagi cigarette demand dataset &amp;ndash; QUARCS Lab open data repository.&lt;/a>&lt;/li>
&lt;/ol></description></item><item><title>Spatial inequality dynamics</title><link>https://carlos-mendez.org/post/python_gds_spatial_inequality/</link><pubDate>Sun, 27 Aug 2023 00:00:00 +0000</pubDate><guid>https://carlos-mendez.org/post/python_gds_spatial_inequality/</guid><description/></item><item><title>Introduction to spatial data science</title><link>https://carlos-mendez.org/post/python_intro_spatial_data_science/</link><pubDate>Mon, 01 Apr 2019 00:00:00 +0000</pubDate><guid>https://carlos-mendez.org/post/python_intro_spatial_data_science/</guid><description>&lt;p>Introduction to spatial data science with Python&lt;/p></description></item></channel></rss>