Repository: JuliaStats/HypothesisTests.jl
Pull request #165 (2019-10-09 09:43:43)

Waldwolf Wolfowitz Test
This is just #152 except I care about the git this time. I've tested the code and docs- this should have a much cleaner git history (@nalimilan)


Patch:
diff --git a/docs/Project.toml b/docs/Project.toml
index d7a1eef..c278538 100644
--- a/docs/Project.toml
+++ b/docs/Project.toml
@@ -1,6 +1,5 @@
 [deps]
 Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
-HypothesisTests = "09f84164-cd44-5f33-b23f-e6b0d136a0d5"
 
 [compat]
 Documenter = "~0.20"
diff --git a/docs/src/nonparametric.md b/docs/src/nonparametric.md
index 7b31b22..816b069 100644
--- a/docs/src/nonparametric.md
+++ b/docs/src/nonparametric.md
@@ -21,7 +21,7 @@ BinomialTest
 FisherExactTest
 ```
 
-## Kolmogorov–Smirnov test
+## Kolmogorov-Smirnov test
 
 Available are an exact one-sample test and approximate (i.e. asymptotic) one- and two-sample
 tests.
@@ -52,6 +52,12 @@ ApproximateMannWhitneyUTest
 SignTest
 ```
 
+## Wald-Wolfowitz independence test
+
+```@docs
+WaldWolfowitzTest
+```
+
 ## Wilcoxon signed rank test
 
 ```@docs
diff --git a/src/wald_wolfowitz.jl b/src/wald_wolfowitz.jl
new file mode 100644
index 0000000..c847f46
--- /dev/null
+++ b/src/wald_wolfowitz.jl
@@ -0,0 +1,55 @@
+export WaldWolfowitzTest
+
+struct WaldWolfowitzTest{T <: Real} <: HypothesisTest
+    nabove::Int     # Number of points above median (or of value a)
+    nbelow::Int     # Number of points below median (or of value b)
+    nruns::Int      # Number of runs
+    μ::T            # Expected mean
+    σ::T            # Expected variance
+    z::T            # test z-statistic from Normal distribution
+end
+
+testname(::WaldWolfowitzTest) = "Wald-Wolfowitz Test"
+population_param_of_interest(x::WaldWolfowitzTest) = ("Number of runs", x.μ, x.nruns) # parameter of interest: name, value under h0, point estimate
+default_tail(::WaldWolfowitzTest) = :both
+pvalue(test::WaldWolfowitzTest; tail=:both) = pvalue(Normal(0.0, 1.0), test.z; tail=tail)
+
+
+function show_params(io::IO, x::WaldWolfowitzTest, ident="")
+    println(io, ident, "number of runs:  $(x.nruns)")
+    println(io, ident, "z-statistic:     $(x.z)")
+end
+
+"""
+    WaldWolfowitzTest(x::AbstractVector{Bool})
+    WaldWolfowitzTest(x::AbstractVector{<:Real})
+
+Perform the Wald-Wolfowitz (or Runs) test of the null hypothesis that the given data is random, or independently sampled.
+The data can come as many-valued or two-valued (Boolean). If many-valued, the sample is transformed by labelling each
+element as above or below the median.
+
+Implements: [`pvalue`](@ref)
+"""
+function WaldWolfowitzTest(x::AbstractVector{Bool})
+    n = length(x)
+    nabove = sum(x)
+    nbelow = n - nabove
+    
+    # Get the expected value and standard deviation
+    μ = 1 + 2 * nabove * (nbelow / n)
+    σ = sqrt((μ - 1) * (μ - 2) / (n - 1))
+
+    # Get the number of runs
+    nruns = 1
+    for k in 1:(n - 1)
+        @inbounds if x[k] != x[k + 1]
+            nruns += 1
+        end
+    end
+
+    # calculate simple z-statistic
+    z = (nruns - μ) / σ
+    WaldWolfowitzTest(nabove, nbelow, nruns, μ, σ, z)
+end
+
+WaldWolfowitzTest(x::AbstractVector{<:Real}) = WaldWolfowitzTest(x .>= median(x))


Test patch:
diff --git a/src/HypothesisTests.jl b/src/HypothesisTests.jl
index e83b5b3..0963fc0 100644
--- a/src/HypothesisTests.jl
+++ b/src/HypothesisTests.jl
@@ -187,4 +187,5 @@ include("durbin_watson.jl")
 include("permutation.jl")
 include("hotelling.jl")
 include("bartlett.jl")
+include("wald_wolfowitz.jl")
 end
diff --git a/test/runtests.jl b/test/runtests.jl
index dd216d1..7b2fe5c 100644
--- a/test/runtests.jl
+++ b/test/runtests.jl
@@ -14,6 +14,7 @@ end
 
 include("anderson_darling.jl")
 include("augmented_dickey_fuller.jl")
+include("bartlett.jl")
 include("binomial.jl")
 include("box_test.jl")
 include("breusch_godfrey.jl")
@@ -22,6 +23,7 @@ include("common.jl")
 include("durbin_watson.jl")
 include("fisher.jl")
 include("jarque_bera.jl")
+include("hotelling.jl")
 include("kolmogorov_smirnov.jl")
 include("kruskal_wallis.jl")
 include("mann_whitney.jl")
@@ -29,7 +31,6 @@ include("permutation.jl")
 include("power_divergence.jl")
 include("show.jl")
 include("t.jl")
+include("wald_wolfowitz.jl")
 include("wilcoxon.jl")
 include("z.jl")
-include("hotelling.jl")
-include("bartlett.jl")
diff --git a/test/wald_wolfowitz.jl b/test/wald_wolfowitz.jl
new file mode 100644
index 0000000..244c04a
--- /dev/null
+++ b/test/wald_wolfowitz.jl
@@ -0,0 +1,70 @@
+using HypothesisTests, Test
+using Distributions
+
+@testset "Wald-Wolfowitz test" begin
+
+@testset "Many-Valued Observations" begin
+    # Get set of dependent observations
+    x = 1:1000
+    tst = WaldWolfowitzTest(x)
+    
+    @test tst.z ≈ -31.575 atol=1e-3
+    @test pvalue(tst) ≈ 8.052e-219 atol=1e-222
+
+    # Test consistency of z-statistics
+    @test pvalue(tst) == pvalue(Normal(tst.μ, tst.σ), tst.nruns)
+    @test pvalue(tst, tail=:left) == pvalue(Normal(tst.μ, tst.σ), tst.nruns, tail=:left)
+    @test pvalue(tst, tail=:right) == pvalue(Normal(tst.μ, tst.σ), tst.nruns, tail=:right)
+    expected_output = """
+    Wald-Wolfowitz Test
+    -------------------
+    Population details:
+        parameter of interest:   Number of runs
+        value under h_0:         501.0
+        point estimate:          2
+
+    Test summary:
+        outcome with 95% confidence: reject h_0
+        two-sided p-value:           <1e-99
+
+    Details:
+        number of runs:  2
+        z-statistic:     -31.575338477995764
+    """
+    output = sprint(show, tst)
+    @test output == expected_output
+end
+
+@testset "Two-Valued Observations" begin
+    # equivalent data as above (half under median half over)
+    x = [falses(500); trues(500)]
+    tst = WaldWolfowitzTest(x)
+    
+    @test tst.z ≈ -31.575 atol=1e-3
+    @test pvalue(tst) ≈ 8.052e-219 atol=1e-222
+
+    # Test consistency of z-statistics
+    @test pvalue(tst) == pvalue(Normal(tst.μ, tst.σ), tst.nruns)
+    @test pvalue(tst, tail=:left) == pvalue(Normal(tst.μ, tst.σ), tst.nruns, tail=:left)
+    @test pvalue(tst, tail=:right) == pvalue(Normal(tst.μ, tst.σ), tst.nruns, tail=:right)
+    expected_output = """
+    Wald-Wolfowitz Test
+    -------------------
+    Population details:
+        parameter of interest:   Number of runs
+        value under h_0:         501.0
+        point estimate:          2
+
+    Test summary:
+        outcome with 95% confidence: reject h_0
+        two-sided p-value:           <1e-99
+
+    Details:
+        number of runs:  2
+        z-statistic:     -31.575338477995764
+    """
+    output = sprint(show, tst)
+    @test output == expected_output
+end
+
+end