# Differing P-Values on When Comparing lm() and pcor.test() for Partial Correlation

While reviewing partial correlation I ran across two methods for computation: use of bivariate linear regression (lm()) and partial correlation(pcor.test() from the ppcor library). I assumed the two procedures were mathematically identical. However the when I inspected the p-values I noticed a difference. Given that the t-scores are identical I assume that this a difference in the degrees of freedom.

Is this assumption correct? Regardless of whether the assumption is correct why does this occur?

All things being equal (normalcy, sampling adequacy, etc.), which method produces a more accurate estimate of the statistical significance?

From mtcars:

reg1 <- lm(mpg ~ disp)
resid1 <- resid(reg1)

reg2 <- lm(hp ~ disp)
resid2 <- resid(reg2)

cor.test(resid1, resid2, method = "pearson")


Yields

    Pearson's product-moment correlation

data:  resid1 and resid2
t = -1.8875, df = 30, p-value = 0.06881
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
-0.60568622  0.02582685
sample estimates:
cor
-0.3258012


However:

pcor.test(mpg, hp, disp)


Yields

    estimate    p.value statistic  n gp  Method
1 -0.3258012 0.07367905 -1.855746 32  1 pearson


## 2 Answers

lm uses a $$t$$ distribution for the test statistic with 30 df, which looks correct.

pcor.test uses a $$t$$ distribution with what seems to be 20 df.

> pt(-1.88746,20)*2
[1] 0.07369715


I can't work out how it gets 20 df: the formula in the source is n-2-gp where n=32, gp=1. Given that, I'd trust the lm one more.

• Potentially silly question - but aren't the t-values different, rather than the df? pcor.test reports t = -1.855746, and then uses 29 df. If so, do you know why the t-values would be different? Mar 9, 2023 at 16:49
• Answered my own comment below - but if you agree with my analysis, you might want to edit this answer? Mar 9, 2023 at 18:35

The degrees of freedom are indeed the cause of the difference - but this already affects the calculation of the t-values, which is where most of the difference comes from.

For correlations (e.g., in R's cor.test()), they are calculated as t = sqrt(df) * r / sqrt(1 - r^2) where df = n-2. Since partial correlations require one additional parameter, there the df should be n-3.

The original example gives a partial correlation coefficient of 0.3258012, so that the t-value should be sqrt(29) * 0.3258012 / sqrt(1 - 0.3258012^2) = 1.855746 - which is that reported by pcor.test

Whether this t-value is now tested with 29 or 30 df only makes a difference in the 4th digit of the p-value - and pcor correctly uses 29 pt(1.855746, 29, lower = F) * 2 precisely yields 0.07367905