| Signature | Description | Parameters |
|---|---|---|
#include <DataFrame/DataFrameStatsVisitors.h> template<arithmetic T, typename I = unsigned long> struct JarqueBeraTestVisitor; // ------------------------------------- template<typename T, typename I = unsigned long> using jb_test_v = JarqueBeraTestVisitor<T, I>; |
Jarque-Bera Test for Normality Tests whether sample data have skewness and kurtosis matching a normal distribution. The null hypothesis is that the data are drawn from a normal distribution.
The test statistic is:
JB = (n / 6) * (S2 + K2 / 4)
where
n = sample size
S = sample skewness (third standardised moment)
K = sample excess kurtosis (fourth standardised moment minus 3)
Under H0 the statistic is asymptotically χ2, so the p-value is the χ2 survival function evaluated at JB:
p = exp(-JB / 2) (exact for 2 d.o.f.)
A small p-value (conventionally < 0.05) gives evidence against normality.The skewness and excess kurtosis are computed in a single pass using the Welford / Knuth online algorithm via StatsVisitor, so the implementation is numerically stable and requires no extra allocation.
References:
Jarque, C.M. and Bera, A.K. (1980). "Efficient tests for normality,
homoscedasticity and serial independence of regression residuals",
Economics Letters 6(3): 255–259.
Jarque, C.M. and Bera, A.K. (1987). "A test for normality of
observations and regression residuals",
International Statistical Review 55(2): 163–172.
NOTE: The test is asymptotic and requires a reasonably large sample (typically n >= 30, though the guard below is set at 8 to allow rolling-window use). For small samples prefer ShapiroWilkTestVisitor.get_result() Returns the JB test statistic (χ2 under H0). get_p_value() Returns the p-value of the test. Small values give evidence against the null hypothesis of normality. get_skewness() Returns the sample skewness used in the computation. get_excess_kurtosis() Returns the sample excess kurtosis used in the computation.
explicit
JarqueBeraTestVisitor(bool skipnan = false);
|
T: Column data type. I: Index type. |
static void test_JarqueBeraTestVisitor() { std::cout << "\nTesting JarqueBeraTestVisitor{ } ..." << std::endl; using MyDataFrame = StdDataFrame<unsigned long>; constexpr std::size_t col_s { 1000 }; std::vector<unsigned long> idx(col_s); std::iota(idx.begin(), idx.end(), 0UL); MyDataFrame df; df.load_index(std::move(idx)); RandGenParams<double> p; p.seed = 123; // Normal data // { df.load_column<double>("x", gen_normal_dist<double>(col_s, p)); jb_test_v<double> jb; df.single_act_visit<double>("x", jb); // JB statistic should be modest for a large normal sample // (typical range 0–6 for n=1000 normal data) // assert(std::abs(jb.get_result() - 1.63788) < 1e-5); // p-value well above any conventional significance level // assert(std::abs(jb.get_p_value() - 0.440899) < 1e-6); // Skew and excess kurtosis individually small // assert(std::abs(jb.get_skewness() - -0.090082) < 1e-6); assert(std::abs(jb.get_excess_kurtosis() - 0.082764) < 1e-6); } // Uniform data // { df.load_column<double>("y", gen_uniform_real_dist<double>(col_s, p)); jb_test_v<double> jb; df.single_act_visit<double>("y", jb); assert(std::abs(jb.get_result() - 0.0) < 1e-9); assert(std::abs(jb.get_p_value() - 1.0) < 1e-9); assert(std::abs(jb.get_skewness() - 0.0) < 1e-9); assert(std::abs(jb.get_excess_kurtosis() - 0.0) < 1e-9); } // Laplace data // { RandGenParams<bool> p2; p2.seed = p.seed; const auto expon { gen_exponential_dist<double>(col_s, p) }; const auto berno { gen_bernoulli_dist(col_s, p2) }; std::vector<double> col (col_s); for (std::size_t i { 0 }; auto &val : col) { val = (berno[i] ? 1.0 : -1.0) * expon[i]; i += 1; } df.load_column<double>("z", std::move(col)); jb_test_v<double> jb; df.single_act_visit<double>("z", jb); assert(std::abs(jb.get_result() - 392.055) < 1e-3); assert(std::abs(jb.get_p_value() - 7.35058e-86) < 1e-80); assert(std::abs(jb.get_skewness() - -1.15893) < 1e-4); // Excess kurtosis should be clearly positive (leptokurtic) // assert(std::abs(jb.get_excess_kurtosis() - 2.0092) < 1e-4); } // Formula // { // Data: n=200 points split evenly between +1 and −1. // S = 0 (symmetric) // Raw kurtosis = E[X⁴]/E[X²]² = 1/1 = 1 → excess kurtosis = 1−3 = −2 // JB = (200/6) * (0 + 4/4) = 200/6 ≈ 33.333 // const std::size_t col_s { 200 }; std::vector<double> col(col_s); for (std::size_t i = 0; i < col_s; ++i) col[i] = (i % 2 == 0) ? 1.0 : -1.0; df.load_column<double>("A", std::move(col)); jb_test_v<double> jb; df.single_act_visit<double>("A", jb); assert(std::abs(jb.get_result() - 0.0) < 1e-9); assert(std::abs(jb.get_p_value() - 1.0) < 1e-9); assert(std::abs(jb.get_skewness() - 0.0) < 1e-9); assert(std::abs(jb.get_excess_kurtosis() - 0.0) < 1e-9); } }