Skip to content

Commit 5e90bae

Browse files
committed
Merge pull request #41 from josepablocam/ljungbox
Ljung-Box test
2 parents 4a7a7a3 + 09fdd40 commit 5e90bae

File tree

2 files changed

+37
-1
lines changed

2 files changed

+37
-1
lines changed

src/main/scala/com/cloudera/sparkts/TimeSeriesStatisticalTests.scala

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,27 @@ object TimeSeriesStatisticalTests {
272272
val bgstat = nObs * auxOLS.calculateRSquared()
273273
(bgstat, 1 - new ChiSquaredDistribution(maxLag).cumulativeProbability(bgstat))
274274
}
275-
275+
276+
/**
277+
* Ljung-Box test for serial correlation in residuals up to lag `maxLag`. The null hypothesis
278+
* is that values are independently distributed up to the given lag. The alternate hypothesis
279+
* is that serial correlation is present. The test statistic follows a Chi-Squared distribution
280+
* with `maxLag` degrees of freedom. See [[https://en.wikipedia.org/wiki/Ljung%E2%80%93Box_test]]
281+
* for more information.
282+
* @param residuals
283+
* @return the test statistic and the p-value associated with it.
284+
*/
285+
def lbtest(residuals: Vector[Double], maxLag: Int): (Double, Double) = {
286+
val autoCorrs = UnivariateTimeSeries.autocorr(residuals, maxLag)
287+
val n = residuals.length
288+
val adjAutoCorrs = autoCorrs.toArray.zipWithIndex.map { case (p, k) =>
289+
(p * p) / (n - k - 1)
290+
}
291+
val testStatistic = n * (n + 2) * adjAutoCorrs.sum
292+
val pValue = 1 - new ChiSquaredDistribution(maxLag).cumulativeProbability(testStatistic)
293+
(testStatistic, pValue)
294+
}
295+
276296
/**
277297
* Breusch-Pagan test for heteroskedasticity in a model
278298
* The statistic follows a X^2 distribution with (# of regressors - 1) degrees of freedom

src/test/scala/com/cloudera/sparkts/TimeSeriesStatisticalTestsSuite.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,20 @@ class TimeSeriesStatisticalTestsSuite extends FunSuite with ShouldMatchers {
8686
// there should be evidence of heteroskedasticity
8787
bptest(new DenseVector(resids2), new DenseMatrix(x.length, 1, x))._2 should be < pthreshold
8888
}
89+
90+
test("ljung-box test") {
91+
val rand = new MersenneTwister(5L)
92+
val n = 100
93+
val indep = Array.fill(n)(rand.nextGaussian)
94+
val vecIndep = new DenseVector(indep)
95+
val (stat1, pval1) = lbtest(vecIndep, 1)
96+
pval1 > 0.05
97+
98+
// serially correlated
99+
val coef = 0.3
100+
val dep = indep.scanLeft(0.0) { case (prior, curr) => prior * coef + curr }.tail
101+
val vecDep = new DenseVector(dep)
102+
val (stat2, pval2) = lbtest(vecDep, 2)
103+
pval2 < 0.05
104+
}
89105
}

0 commit comments

Comments
 (0)