@@ -361,14 +361,16 @@ def omnibus_test(
361
361
verbose : bool = False ,
362
362
) -> tuple [float , float ]:
363
363
"""
364
- A simple alternative to the Rayleigh test, aka Hodges-Ajne test,
365
- which does not assume sampling from a specific distribution. This
366
- is called an "omnibus test" because it works well for unimodal,
367
- bimodal, and multimodal distributions (for ungrouped data).
364
+ Hodges–Ajne omnibus test for circular uniformity.
368
365
369
366
- H0: The population is uniformly distributed around the circle
370
367
- H1: The population is not uniformly distributed.
371
368
369
+ This test is distribution-free and handles uni-, bi-, and multimodal
370
+ alternatives. The classical p-value involves factorials and
371
+ overflows for large *n*. We therefore compute it in log-space
372
+ (``math.lgamma``) and exponentiate at the very end.
373
+
372
374
Parameters
373
375
----------
374
376
alpha: np.array or None
@@ -403,12 +405,41 @@ def omnibus_test(
403
405
lines_rotated > 0.0 , lines_rotated < np .round (np .pi , 5 )
404
406
).sum (1 )
405
407
m = int (np .min (right ))
406
- pval = (
407
- (n - 2 * m )
408
- * math .factorial (n )
409
- / (math .factorial (m ) * math .factorial (n - m ))
410
- / 2 ** (n - 1 )
408
+
409
+ # ------------------------------------------------------------------
410
+ # 2. p-value ——— analytical formula and its log form
411
+ # ------------------------------------------------------------------
412
+ # Classical (Zar 2010, eq. 27-4):
413
+ #
414
+ # p = (n − 2m) · n! / [ m! · (n − m)! · 2^(n−1) ] …(1)
415
+ # # pval = (
416
+ # # (n - 2 * m)
417
+ # # * math.factorial(n)
418
+ # # / (math.factorial(m) * math.factorial(n - m))
419
+ # # / 2 ** (n - 1)
420
+ # # ) # eq(27.7)
421
+
422
+ # Taking natural logs and using Γ(k+1) = k! with log Γ = lgamma:
423
+ #
424
+ # ln p = ln(n − 2m)
425
+ # + lgamma(n + 1)
426
+ # − lgamma(m + 1)
427
+ # − lgamma(n − m + 1)
428
+ # − (n − 1)·ln 2 …(2)
429
+ #
430
+ # Eq. (2) is numerically safe for very large n; we exponentiate at
431
+ # the end, knowing the result may under-flow to 0.0 in double precision.
432
+ # ------------------------------------------------------------------
433
+
434
+ logp = (
435
+ math .log (n - 2 * m )
436
+ + math .lgamma (n + 1 )
437
+ - math .lgamma (m + 1 )
438
+ - math .lgamma (n - m + 1 )
439
+ - (n - 1 )* math .log (2.0 )
411
440
)
441
+ pval = np .exp (logp )
442
+
412
443
A = np .pi * np .sqrt (n ) / (2 * (n - 2 * m ))
413
444
414
445
if verbose :
0 commit comments