Skip to content

BUG: Use the differenced residuals for FirstDifferenceOLS.pesaran_cd - #771

Merged
bashtage merged 2 commits into
mainfrom
lm71-fix-fd-pesaran-cd
Oct 12, 2026
Merged

bashtage merged 2 commits into
mainfrom
lm71-fix-fd-pesaran-cd

Conversation

@bashtage

Copy link
Copy Markdown
Owner

PanelResults.pesaran_cd (#739, not released) computed the Pesaran CD statistic
from the idiosyncratic shocks of every model, and #761 documented that for
FirstDifferenceOLS these are y - x b in levels. They contain the entity effect
and are as persistent as the errors in levels. First differences are used when
the errors are persistent, for example a random walk. The sample correlations
over time of series like these are large even for independent entities, and the
test rejects too often.

FirstDifferenceOLS now uses its resids, the residuals of the differenced
equation, which are not persistent in that case. This is the statistic that
plm::pcdtest gives for a pooled regression of the differenced data. pcdtest
cannot be applied to an fd model of plm: it fails with "argument 1 is not a
vector". As for the other models, the weights of a weighted model are not
applied to the shocks. No other model changes.

Size of the 5% test in percent of 1,000 samples per cell, for
y = 1 + 0.5 x + a_i + e_it with standard normal x and a_i, and independent
entities. "levels" is the statistic before this change and "differenced" the
statistic of FirstDifferenceOLS.pesaran_cd now:

                independent errors          random walk errors
                levels    differenced       levels    differenced
N=20,  T=5       7.2         7.4              6.5         7.2
N=20,  T=10      5.4         6.9             11.6         5.8
N=20,  T=20      6.7        10.0             28.8         6.0
N=100, T=5       7.6         7.7              8.9         7.0
N=100, T=10      6.3         7.8             10.5         5.0
N=100, T=20      5.7         9.2             22.9         4.7

With random walk errors, for which first differences are efficient, the levels
statistic over-rejects (8.9% to 28.8%, except 6.5% at N = 20, T = 5), more as T
grows, while the differenced residuals have about the right size (4.7% to 7.2%).
With independent errors the differenced residuals are MA(1) with autocorrelation
-0.5, and the test is oversized (6.9% to 10.0%, tending to about 11% as T grows),
where the levels statistic gives 5.4% to 7.6%.

A model with two periods has one difference for each entity, so that no pair of
entities has two overlapping residuals. pesaran_cd returns an
InapplicableTestStatistic for it, as for any panel where no pair of entities has
two overlapping observations. It returned a statistic computed from the two
shocks in levels.

pesaran_cd is new in 7.1, so no result of 7.0 changes. The Notes of the
docstring say which series is used.

The reference values are from R 4.6.1 and plm 2.6.7. The script
tests/panel/results/pesaran-cd-fd-reference.R simulates six panels (balanced
with independent shocks, with a common factor and with random walk shocks, and
unbalanced with entry and exit, with missing periods and sparse), and writes
them and the reference values to CSV files. It differences adjacent periods
with the diff of plm, estimates the first difference regression, and computes
the statistic from the definition in two ways (matrix of correlations and loop
over pairs). Both equal plm::pcdtest of the regression. Where no entity has a
missing period inside its range, it also checks that model = "fd" of plm has
the same coefficient and the same number of residuals. model = "fd" differences
the observations that follow each other in the data, which is across a missing
period, whereas FirstDifferenceOLS differences adjacent periods only, so the
panels with missing periods use diff.

linearmodels agrees with the reference to 1e-14 on all six panels (the estimate,
the number of differences and pairs, the statistic and the p-value). The
statistic of the shocks in levels differs by 0.07 to 1.7.

Tests, in test_results_pesaran_cd.py unless noted:

  • test_pesaran_cd_first_difference_plm: the six panels against R, with the
    number of pairs, and the statistic of the levels, which is not the same.
  • test_pesaran_cd_first_difference_closed_form: a balanced panel against
    sqrt(2 (T - 1) / (N (N - 1))) times the sum of the correlations from numpy.
  • test_pesaran_cd_first_difference_two_periods.
  • test_pesaran_cd_applicability and test_pesaran_cd_all_estimators (in
    test_results.py) used the shocks in levels as the reference for
    FirstDifferenceOLS and now use resids. A weighted FirstDifferenceOLS is
    added to the first.

11 of the 67 pesaran_cd tests fail on origin/main, all for FirstDifferenceOLS.

PanelResults.pesaran_cd (#739, not released) computed the Pesaran CD statistic
from the idiosyncratic shocks of every model, and #761 documented that for
FirstDifferenceOLS these are y - x b in levels. They contain the entity effect
and are as persistent as the errors in levels. First differences are used when
the errors are persistent, for example a random walk. The sample correlations
over time of series like these are large even for independent entities, and the
test rejects too often.

FirstDifferenceOLS now uses its resids, the residuals of the differenced
equation, which are not persistent in that case. This is the statistic that
plm::pcdtest gives for a pooled regression of the differenced data. pcdtest
cannot be applied to an fd model of plm: it fails with "argument 1 is not a
vector". As for the other models, the weights of a weighted model are not
applied to the shocks. No other model changes.

Size of the 5% test in percent of 1,000 samples per cell, for
y = 1 + 0.5 x + a_i + e_it with standard normal x and a_i, and independent
entities. "levels" is the statistic before this change and "differenced" the
statistic of FirstDifferenceOLS.pesaran_cd now:

                    independent errors          random walk errors
                    levels    differenced       levels    differenced
    N=20,  T=5       7.2         7.4              6.5         7.2
    N=20,  T=10      5.4         6.9             11.6         5.8
    N=20,  T=20      6.7        10.0             28.8         6.0
    N=100, T=5       7.6         7.7              8.9         7.0
    N=100, T=10      6.3         7.8             10.5         5.0
    N=100, T=20      5.7         9.2             22.9         4.7

With random walk errors, for which first differences are efficient, the levels
statistic over-rejects (8.9% to 28.8%, except 6.5% at N = 20, T = 5), more as T
grows, while the differenced residuals have about the right size (4.7% to 7.2%).
With independent errors the differenced residuals are MA(1) with autocorrelation
-0.5, and the test is oversized (6.9% to 10.0%, tending to about 11% as T grows),
where the levels statistic gives 5.4% to 7.6%.

A model with two periods has one difference for each entity, so that no pair of
entities has two overlapping residuals. pesaran_cd returns an
InapplicableTestStatistic for it, as for any panel where no pair of entities has
two overlapping observations. It returned a statistic computed from the two
shocks in levels.

pesaran_cd is new in 7.1, so no result of 7.0 changes. The Notes of the
docstring say which series is used.

The reference values are from R 4.6.1 and plm 2.6.7. The script
tests/panel/results/pesaran-cd-fd-reference.R simulates six panels (balanced
with independent shocks, with a common factor and with random walk shocks, and
unbalanced with entry and exit, with missing periods and sparse), and writes
them and the reference values to CSV files. It differences adjacent periods
with the diff of plm, estimates the first difference regression, and computes
the statistic from the definition in two ways (matrix of correlations and loop
over pairs). Both equal plm::pcdtest of the regression. Where no entity has a
missing period inside its range, it also checks that model = "fd" of plm has
the same coefficient and the same number of residuals. model = "fd" differences
the observations that follow each other in the data, which is across a missing
period, whereas FirstDifferenceOLS differences adjacent periods only, so the
panels with missing periods use diff.

linearmodels agrees with the reference to 1e-14 on all six panels (the estimate,
the number of differences and pairs, the statistic and the p-value). The
statistic of the shocks in levels differs by 0.07 to 1.7.

Tests, in test_results_pesaran_cd.py unless noted:

* test_pesaran_cd_first_difference_plm: the six panels against R, with the
  number of pairs, and the statistic of the levels, which is not the same.
* test_pesaran_cd_first_difference_closed_form: a balanced panel against
  sqrt(2 (T - 1) / (N (N - 1))) times the sum of the correlations from numpy.
* test_pesaran_cd_first_difference_two_periods.
* test_pesaran_cd_applicability and test_pesaran_cd_all_estimators (in
  test_results.py) used the shocks in levels as the reference for
  FirstDifferenceOLS and now use resids. A weighted FirstDifferenceOLS is
  added to the first.

11 of the 67 pesaran_cd tests fail on origin/main, all for FirstDifferenceOLS.
If the errors in levels are serially uncorrelated, the residuals of
FirstDifferenceOLS are an MA(1) with autocorrelation -0.5. The asymptotic
variance of each scaled pairwise correlation is then 1 + 2(0.5)(0.5) = 1.5
instead of 1, so a nominal 5% test rejects 2(1 - Phi(1.96 / sqrt(1.5))), about
11% of the time. The simulations in the previous commit give 6.9% to 10.0% for
T from 5 to 20. The Notes now say so, and suggest PanelOLS with entity effects
in that case: its residuals have the same sample correlations as the errors.
problem in testing for remaining cross-section correlation.
Journal of Business & Economic Statistics, 40(3), 1191-1203.
"""
from linearmodels.panel.model import FirstDifferenceOLS # noqa: PLC0415
@codecov

codecov Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.59%. Comparing base (121d869) to head (359d6ae).
⚠️ Report is 10 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #771      +/-   ##
==========================================
- Coverage   99.60%   99.59%   -0.01%     
==========================================
  Files         127      128       +1     
  Lines       23832    24231     +399     
  Branches     1783     1812      +29     
==========================================
+ Hits        23737    24132     +395     
- Misses         35       39       +4     
  Partials       60       60              
Flag Coverage Δ
adder 99.57% <100.00%> (-0.02%) ⬇️
subtractor 99.57% <100.00%> (-0.02%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@bashtage
bashtage merged commit 2d9c91b into main Oct 12, 2026
23 of 24 checks passed
@bashtage
bashtage deleted the lm71-fix-fd-pesaran-cd branch October 12, 2026 13:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants