chi_square_test.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. // Copyright 2017 The Abseil Authors.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // https://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #include "absl/random/internal/chi_square.h"
  15. #include <algorithm>
  16. #include <cstddef>
  17. #include <cstdint>
  18. #include <iterator>
  19. #include <numeric>
  20. #include <vector>
  21. #include "gtest/gtest.h"
  22. #include "absl/base/macros.h"
  23. using absl::random_internal::ChiSquare;
  24. using absl::random_internal::ChiSquarePValue;
  25. using absl::random_internal::ChiSquareValue;
  26. using absl::random_internal::ChiSquareWithExpected;
  27. namespace {
  28. TEST(ChiSquare, Value) {
  29. struct {
  30. int line;
  31. double chi_square;
  32. int df;
  33. double confidence;
  34. } const specs[] = {
  35. // Testing lookup at 1% confidence
  36. {__LINE__, 0, 0, 0.01},
  37. {__LINE__, 0.00016, 1, 0.01},
  38. {__LINE__, 1.64650, 8, 0.01},
  39. {__LINE__, 5.81221, 16, 0.01},
  40. {__LINE__, 156.4319, 200, 0.01},
  41. {__LINE__, 1121.3784, 1234, 0.01},
  42. {__LINE__, 53557.1629, 54321, 0.01},
  43. {__LINE__, 651662.6647, 654321, 0.01},
  44. // Testing lookup at 99% confidence
  45. {__LINE__, 0, 0, 0.99},
  46. {__LINE__, 6.635, 1, 0.99},
  47. {__LINE__, 20.090, 8, 0.99},
  48. {__LINE__, 32.000, 16, 0.99},
  49. {__LINE__, 249.4456, 200, 0.99},
  50. {__LINE__, 1131.1573, 1023, 0.99},
  51. {__LINE__, 1352.5038, 1234, 0.99},
  52. {__LINE__, 55090.7356, 54321, 0.99},
  53. {__LINE__, 656985.1514, 654321, 0.99},
  54. // Testing lookup at 99.9% confidence
  55. {__LINE__, 16.2659, 3, 0.999},
  56. {__LINE__, 22.4580, 6, 0.999},
  57. {__LINE__, 267.5409, 200, 0.999},
  58. {__LINE__, 1168.5033, 1023, 0.999},
  59. {__LINE__, 55345.1741, 54321, 0.999},
  60. {__LINE__, 657861.7284, 654321, 0.999},
  61. {__LINE__, 51.1772, 24, 0.999},
  62. {__LINE__, 59.7003, 30, 0.999},
  63. {__LINE__, 37.6984, 15, 0.999},
  64. {__LINE__, 29.5898, 10, 0.999},
  65. {__LINE__, 27.8776, 9, 0.999},
  66. // Testing lookup at random confidences
  67. {__LINE__, 0.000157088, 1, 0.01},
  68. {__LINE__, 5.31852, 2, 0.93},
  69. {__LINE__, 1.92256, 4, 0.25},
  70. {__LINE__, 10.7709, 13, 0.37},
  71. {__LINE__, 26.2514, 17, 0.93},
  72. {__LINE__, 36.4799, 29, 0.84},
  73. {__LINE__, 25.818, 31, 0.27},
  74. {__LINE__, 63.3346, 64, 0.50},
  75. {__LINE__, 196.211, 128, 0.9999},
  76. {__LINE__, 215.21, 243, 0.10},
  77. {__LINE__, 285.393, 256, 0.90},
  78. {__LINE__, 984.504, 1024, 0.1923},
  79. {__LINE__, 2043.85, 2048, 0.4783},
  80. {__LINE__, 48004.6, 48273, 0.194},
  81. };
  82. for (const auto& spec : specs) {
  83. SCOPED_TRACE(spec.line);
  84. // Verify all values are have at most a 1% relative error.
  85. const double val = ChiSquareValue(spec.df, spec.confidence);
  86. const double err = std::max(5e-6, spec.chi_square / 5e3); // 1 part in 5000
  87. EXPECT_NEAR(spec.chi_square, val, err) << spec.line;
  88. }
  89. // Relaxed test for extreme values, from
  90. // http://www.ciphersbyritter.com/JAVASCRP/NORMCHIK.HTM#ChiSquare
  91. EXPECT_NEAR(49.2680, ChiSquareValue(100, 1e-6), 5); // 0.000'005 mark
  92. EXPECT_NEAR(123.499, ChiSquareValue(200, 1e-6), 5); // 0.000'005 mark
  93. EXPECT_NEAR(149.449, ChiSquareValue(100, 0.999), 0.01);
  94. EXPECT_NEAR(161.318, ChiSquareValue(100, 0.9999), 0.01);
  95. EXPECT_NEAR(172.098, ChiSquareValue(100, 0.99999), 0.01);
  96. EXPECT_NEAR(381.426, ChiSquareValue(300, 0.999), 0.05);
  97. EXPECT_NEAR(399.756, ChiSquareValue(300, 0.9999), 0.1);
  98. EXPECT_NEAR(416.126, ChiSquareValue(300, 0.99999), 0.2);
  99. }
  100. TEST(ChiSquareTest, PValue) {
  101. struct {
  102. int line;
  103. double pval;
  104. double chi_square;
  105. int df;
  106. } static const specs[] = {
  107. {__LINE__, 1, 0, 0},
  108. {__LINE__, 0, 0.001, 0},
  109. {__LINE__, 1.000, 0, 453},
  110. {__LINE__, 0.134471, 7972.52, 7834},
  111. {__LINE__, 0.203922, 28.32, 23},
  112. {__LINE__, 0.737171, 48274, 48472},
  113. {__LINE__, 0.444146, 583.1234, 579},
  114. {__LINE__, 0.294814, 138.2, 130},
  115. {__LINE__, 0.0816532, 12.63, 7},
  116. {__LINE__, 0, 682.32, 67},
  117. {__LINE__, 0.49405, 999, 999},
  118. {__LINE__, 1.000, 0, 9999},
  119. {__LINE__, 0.997477, 0.00001, 1},
  120. {__LINE__, 0, 5823.21, 5040},
  121. };
  122. for (const auto& spec : specs) {
  123. SCOPED_TRACE(spec.line);
  124. const double pval = ChiSquarePValue(spec.chi_square, spec.df);
  125. EXPECT_NEAR(spec.pval, pval, 1e-3);
  126. }
  127. }
  128. TEST(ChiSquareTest, CalcChiSquare) {
  129. struct {
  130. int line;
  131. std::vector<int> expected;
  132. std::vector<int> actual;
  133. } const specs[] = {
  134. {__LINE__,
  135. {56, 234, 76, 1, 546, 1, 87, 345, 1, 234},
  136. {2, 132, 4, 43, 234, 8, 345, 8, 236, 56}},
  137. {__LINE__,
  138. {123, 36, 234, 367, 345, 2, 456, 567, 234, 567},
  139. {123, 56, 2345, 8, 345, 8, 2345, 23, 48, 267}},
  140. {__LINE__,
  141. {123, 234, 345, 456, 567, 678, 789, 890, 98, 76},
  142. {123, 234, 345, 456, 567, 678, 789, 890, 98, 76}},
  143. {__LINE__, {3, 675, 23, 86, 2, 8, 2}, {456, 675, 23, 86, 23, 65, 2}},
  144. {__LINE__, {1}, {23}},
  145. };
  146. for (const auto& spec : specs) {
  147. SCOPED_TRACE(spec.line);
  148. double chi_square = 0;
  149. for (int i = 0; i < spec.expected.size(); ++i) {
  150. const double diff = spec.actual[i] - spec.expected[i];
  151. chi_square += (diff * diff) / spec.expected[i];
  152. }
  153. EXPECT_NEAR(chi_square,
  154. ChiSquare(std::begin(spec.actual), std::end(spec.actual),
  155. std::begin(spec.expected), std::end(spec.expected)),
  156. 1e-5);
  157. }
  158. }
  159. TEST(ChiSquareTest, CalcChiSquareInt64) {
  160. const int64_t data[3] = {910293487, 910292491, 910216780};
  161. // $ python -c "import scipy.stats
  162. // > print scipy.stats.chisquare([910293487, 910292491, 910216780])[0]"
  163. // 4.25410123524
  164. double sum = std::accumulate(std::begin(data), std::end(data), double{0});
  165. size_t n = std::distance(std::begin(data), std::end(data));
  166. double a = ChiSquareWithExpected(std::begin(data), std::end(data), sum / n);
  167. EXPECT_NEAR(4.254101, a, 1e-6);
  168. // ... Or with known values.
  169. double b =
  170. ChiSquareWithExpected(std::begin(data), std::end(data), 910267586.0);
  171. EXPECT_NEAR(4.254101, b, 1e-6);
  172. }
  173. TEST(ChiSquareTest, TableData) {
  174. // Test data from
  175. // http://www.itl.nist.gov/div898/handbook/eda/section3/eda3674.htm
  176. // 0.90 0.95 0.975 0.99 0.999
  177. const double data[100][5] = {
  178. /* 1*/ {2.706, 3.841, 5.024, 6.635, 10.828},
  179. /* 2*/ {4.605, 5.991, 7.378, 9.210, 13.816},
  180. /* 3*/ {6.251, 7.815, 9.348, 11.345, 16.266},
  181. /* 4*/ {7.779, 9.488, 11.143, 13.277, 18.467},
  182. /* 5*/ {9.236, 11.070, 12.833, 15.086, 20.515},
  183. /* 6*/ {10.645, 12.592, 14.449, 16.812, 22.458},
  184. /* 7*/ {12.017, 14.067, 16.013, 18.475, 24.322},
  185. /* 8*/ {13.362, 15.507, 17.535, 20.090, 26.125},
  186. /* 9*/ {14.684, 16.919, 19.023, 21.666, 27.877},
  187. /*10*/ {15.987, 18.307, 20.483, 23.209, 29.588},
  188. /*11*/ {17.275, 19.675, 21.920, 24.725, 31.264},
  189. /*12*/ {18.549, 21.026, 23.337, 26.217, 32.910},
  190. /*13*/ {19.812, 22.362, 24.736, 27.688, 34.528},
  191. /*14*/ {21.064, 23.685, 26.119, 29.141, 36.123},
  192. /*15*/ {22.307, 24.996, 27.488, 30.578, 37.697},
  193. /*16*/ {23.542, 26.296, 28.845, 32.000, 39.252},
  194. /*17*/ {24.769, 27.587, 30.191, 33.409, 40.790},
  195. /*18*/ {25.989, 28.869, 31.526, 34.805, 42.312},
  196. /*19*/ {27.204, 30.144, 32.852, 36.191, 43.820},
  197. /*20*/ {28.412, 31.410, 34.170, 37.566, 45.315},
  198. /*21*/ {29.615, 32.671, 35.479, 38.932, 46.797},
  199. /*22*/ {30.813, 33.924, 36.781, 40.289, 48.268},
  200. /*23*/ {32.007, 35.172, 38.076, 41.638, 49.728},
  201. /*24*/ {33.196, 36.415, 39.364, 42.980, 51.179},
  202. /*25*/ {34.382, 37.652, 40.646, 44.314, 52.620},
  203. /*26*/ {35.563, 38.885, 41.923, 45.642, 54.052},
  204. /*27*/ {36.741, 40.113, 43.195, 46.963, 55.476},
  205. /*28*/ {37.916, 41.337, 44.461, 48.278, 56.892},
  206. /*29*/ {39.087, 42.557, 45.722, 49.588, 58.301},
  207. /*30*/ {40.256, 43.773, 46.979, 50.892, 59.703},
  208. /*31*/ {41.422, 44.985, 48.232, 52.191, 61.098},
  209. /*32*/ {42.585, 46.194, 49.480, 53.486, 62.487},
  210. /*33*/ {43.745, 47.400, 50.725, 54.776, 63.870},
  211. /*34*/ {44.903, 48.602, 51.966, 56.061, 65.247},
  212. /*35*/ {46.059, 49.802, 53.203, 57.342, 66.619},
  213. /*36*/ {47.212, 50.998, 54.437, 58.619, 67.985},
  214. /*37*/ {48.363, 52.192, 55.668, 59.893, 69.347},
  215. /*38*/ {49.513, 53.384, 56.896, 61.162, 70.703},
  216. /*39*/ {50.660, 54.572, 58.120, 62.428, 72.055},
  217. /*40*/ {51.805, 55.758, 59.342, 63.691, 73.402},
  218. /*41*/ {52.949, 56.942, 60.561, 64.950, 74.745},
  219. /*42*/ {54.090, 58.124, 61.777, 66.206, 76.084},
  220. /*43*/ {55.230, 59.304, 62.990, 67.459, 77.419},
  221. /*44*/ {56.369, 60.481, 64.201, 68.710, 78.750},
  222. /*45*/ {57.505, 61.656, 65.410, 69.957, 80.077},
  223. /*46*/ {58.641, 62.830, 66.617, 71.201, 81.400},
  224. /*47*/ {59.774, 64.001, 67.821, 72.443, 82.720},
  225. /*48*/ {60.907, 65.171, 69.023, 73.683, 84.037},
  226. /*49*/ {62.038, 66.339, 70.222, 74.919, 85.351},
  227. /*50*/ {63.167, 67.505, 71.420, 76.154, 86.661},
  228. /*51*/ {64.295, 68.669, 72.616, 77.386, 87.968},
  229. /*52*/ {65.422, 69.832, 73.810, 78.616, 89.272},
  230. /*53*/ {66.548, 70.993, 75.002, 79.843, 90.573},
  231. /*54*/ {67.673, 72.153, 76.192, 81.069, 91.872},
  232. /*55*/ {68.796, 73.311, 77.380, 82.292, 93.168},
  233. /*56*/ {69.919, 74.468, 78.567, 83.513, 94.461},
  234. /*57*/ {71.040, 75.624, 79.752, 84.733, 95.751},
  235. /*58*/ {72.160, 76.778, 80.936, 85.950, 97.039},
  236. /*59*/ {73.279, 77.931, 82.117, 87.166, 98.324},
  237. /*60*/ {74.397, 79.082, 83.298, 88.379, 99.607},
  238. /*61*/ {75.514, 80.232, 84.476, 89.591, 100.888},
  239. /*62*/ {76.630, 81.381, 85.654, 90.802, 102.166},
  240. /*63*/ {77.745, 82.529, 86.830, 92.010, 103.442},
  241. /*64*/ {78.860, 83.675, 88.004, 93.217, 104.716},
  242. /*65*/ {79.973, 84.821, 89.177, 94.422, 105.988},
  243. /*66*/ {81.085, 85.965, 90.349, 95.626, 107.258},
  244. /*67*/ {82.197, 87.108, 91.519, 96.828, 108.526},
  245. /*68*/ {83.308, 88.250, 92.689, 98.028, 109.791},
  246. /*69*/ {84.418, 89.391, 93.856, 99.228, 111.055},
  247. /*70*/ {85.527, 90.531, 95.023, 100.425, 112.317},
  248. /*71*/ {86.635, 91.670, 96.189, 101.621, 113.577},
  249. /*72*/ {87.743, 92.808, 97.353, 102.816, 114.835},
  250. /*73*/ {88.850, 93.945, 98.516, 104.010, 116.092},
  251. /*74*/ {89.956, 95.081, 99.678, 105.202, 117.346},
  252. /*75*/ {91.061, 96.217, 100.839, 106.393, 118.599},
  253. /*76*/ {92.166, 97.351, 101.999, 107.583, 119.850},
  254. /*77*/ {93.270, 98.484, 103.158, 108.771, 121.100},
  255. /*78*/ {94.374, 99.617, 104.316, 109.958, 122.348},
  256. /*79*/ {95.476, 100.749, 105.473, 111.144, 123.594},
  257. /*80*/ {96.578, 101.879, 106.629, 112.329, 124.839},
  258. /*81*/ {97.680, 103.010, 107.783, 113.512, 126.083},
  259. /*82*/ {98.780, 104.139, 108.937, 114.695, 127.324},
  260. /*83*/ {99.880, 105.267, 110.090, 115.876, 128.565},
  261. /*84*/ {100.980, 106.395, 111.242, 117.057, 129.804},
  262. /*85*/ {102.079, 107.522, 112.393, 118.236, 131.041},
  263. /*86*/ {103.177, 108.648, 113.544, 119.414, 132.277},
  264. /*87*/ {104.275, 109.773, 114.693, 120.591, 133.512},
  265. /*88*/ {105.372, 110.898, 115.841, 121.767, 134.746},
  266. /*89*/ {106.469, 112.022, 116.989, 122.942, 135.978},
  267. /*90*/ {107.565, 113.145, 118.136, 124.116, 137.208},
  268. /*91*/ {108.661, 114.268, 119.282, 125.289, 138.438},
  269. /*92*/ {109.756, 115.390, 120.427, 126.462, 139.666},
  270. /*93*/ {110.850, 116.511, 121.571, 127.633, 140.893},
  271. /*94*/ {111.944, 117.632, 122.715, 128.803, 142.119},
  272. /*95*/ {113.038, 118.752, 123.858, 129.973, 143.344},
  273. /*96*/ {114.131, 119.871, 125.000, 131.141, 144.567},
  274. /*97*/ {115.223, 120.990, 126.141, 132.309, 145.789},
  275. /*98*/ {116.315, 122.108, 127.282, 133.476, 147.010},
  276. /*99*/ {117.407, 123.225, 128.422, 134.642, 148.230},
  277. /*100*/ {118.498, 124.342, 129.561, 135.807, 149.449}
  278. /**/};
  279. // 0.90 0.95 0.975 0.99 0.999
  280. for (int i = 0; i < ABSL_ARRAYSIZE(data); i++) {
  281. const double E = 0.0001;
  282. EXPECT_NEAR(ChiSquarePValue(data[i][0], i + 1), 0.10, E)
  283. << i << " " << data[i][0];
  284. EXPECT_NEAR(ChiSquarePValue(data[i][1], i + 1), 0.05, E)
  285. << i << " " << data[i][1];
  286. EXPECT_NEAR(ChiSquarePValue(data[i][2], i + 1), 0.025, E)
  287. << i << " " << data[i][2];
  288. EXPECT_NEAR(ChiSquarePValue(data[i][3], i + 1), 0.01, E)
  289. << i << " " << data[i][3];
  290. EXPECT_NEAR(ChiSquarePValue(data[i][4], i + 1), 0.001, E)
  291. << i << " " << data[i][4];
  292. const double F = 0.1;
  293. EXPECT_NEAR(ChiSquareValue(i + 1, 0.90), data[i][0], F) << i;
  294. EXPECT_NEAR(ChiSquareValue(i + 1, 0.95), data[i][1], F) << i;
  295. EXPECT_NEAR(ChiSquareValue(i + 1, 0.975), data[i][2], F) << i;
  296. EXPECT_NEAR(ChiSquareValue(i + 1, 0.99), data[i][3], F) << i;
  297. EXPECT_NEAR(ChiSquareValue(i + 1, 0.999), data[i][4], F) << i;
  298. }
  299. }
  300. TEST(ChiSquareTest, ChiSquareTwoIterator) {
  301. // Test data from http://www.stat.yale.edu/Courses/1997-98/101/chigf.htm
  302. // Null-hypothesis: This data is normally distributed.
  303. const int counts[10] = {6, 6, 18, 33, 38, 38, 28, 21, 9, 3};
  304. const double expected[10] = {4.6, 8.8, 18.4, 30.0, 38.2,
  305. 38.2, 30.0, 18.4, 8.8, 4.6};
  306. double chi_square = ChiSquare(std::begin(counts), std::end(counts),
  307. std::begin(expected), std::end(expected));
  308. EXPECT_NEAR(chi_square, 2.69, 0.001);
  309. // Degrees of freedom: 10 bins. two estimated parameters. = 10 - 2 - 1.
  310. const int dof = 7;
  311. // The critical value of 7, 95% => 14.067 (see above test)
  312. double p_value_05 = ChiSquarePValue(14.067, dof);
  313. EXPECT_NEAR(p_value_05, 0.05, 0.001); // 95%-ile p-value
  314. double p_actual = ChiSquarePValue(chi_square, dof);
  315. EXPECT_GT(p_actual, 0.05); // Accept the null hypothesis.
  316. }
  317. TEST(ChiSquareTest, DiceRolls) {
  318. // Assume we are testing 102 fair dice rolls.
  319. // Null-hypothesis: This data is fairly distributed.
  320. //
  321. // The dof value of 4, @95% = 9.488 (see above test)
  322. // The dof value of 5, @95% = 11.070
  323. const int rolls[6] = {22, 11, 17, 14, 20, 18};
  324. double sum = std::accumulate(std::begin(rolls), std::end(rolls), double{0});
  325. size_t n = std::distance(std::begin(rolls), std::end(rolls));
  326. double a = ChiSquareWithExpected(std::begin(rolls), std::end(rolls), sum / n);
  327. EXPECT_NEAR(a, 4.70588, 1e-5);
  328. EXPECT_LT(a, ChiSquareValue(4, 0.95));
  329. double p_a = ChiSquarePValue(a, 4);
  330. EXPECT_NEAR(p_a, 0.318828, 1e-5); // Accept the null hypothesis.
  331. double b = ChiSquareWithExpected(std::begin(rolls), std::end(rolls), 17.0);
  332. EXPECT_NEAR(b, 4.70588, 1e-5);
  333. EXPECT_LT(b, ChiSquareValue(5, 0.95));
  334. double p_b = ChiSquarePValue(b, 5);
  335. EXPECT_NEAR(p_b, 0.4528180, 1e-5); // Accept the null hypothesis.
  336. }
  337. } // namespace