mixle.stats.directional.projected_normal module¶
Projected normal distribution – a flexible circular law from projecting a 2-D Gaussian.
The angle theta = atan2(Z2, Z1) of a bivariate normal Z ~ N(mu, I_2) follows the (isotropic)
projected normal PN(mu) on the circle. With mu = (mu_x, mu_y) and a(theta) = mu_x cos theta
+ mu_y sin theta = mu . u(theta), its density is
f(theta; mu) = (1 / 2 pi) exp(-||mu||^2 / 2) [1 + a Phi(a) / phi(a)],
where phi/Phi are the standard normal pdf/cdf. It is uniform at mu = 0 and concentrates in
the direction of mu as ||mu|| grows; unlike the von Mises it can be asymmetric/peaked depending
on mu. It samples exactly (draw N(mu, I_2), take the angle). Parameters are fit by EM with the
latent radius (Nunez-Antonio & Gutierrez-Pena 2005): given the current mu, E[r | theta] has a
closed form and the M-step is mu = mean(E[r | theta] u(theta)).
The ratio a Phi(a) / phi(a) is evaluated stably for all a via the scaled complementary error
function: Phi(a)/phi(a) = sqrt(pi/2) erfcx(-a/sqrt2), so M(a) := sqrt(pi/2) erfcx(-a/sqrt2) and
a Phi(a)/phi(a) = a M(a).
References
Mardia & Jupp, Directional Statistics (2000), sec. 3.5.6 (projected/offset normal).
Nunez-Antonio & Gutierrez-Pena, “A Bayesian analysis of directional data using the projected normal distribution”, J. Applied Statistics (2005).
- class ProjectedNormalDistribution(mu_x, mu_y, name=None, keys=None)[source]
Bases:
SequenceEncodableProbabilityDistributionIsotropic projected normal
PN(mu)on the circle,mu = (mu_x, mu_y).- density(x)[source]
Return the probability density at a single angle (radians).
- log_density(x)[source]
Return the log-density at a single angle (radians).
- seq_log_density(x)[source]
Return vectorized log-density for sequence-encoded
(cos, sin)observations.
- classmethod compute_capabilities()[source]
- classmethod compute_declaration()[source]
- classmethod backend_legacy_sufficient_statistics(x, params, engine)[source]
Per-row E-step stats
(E[r] cos, E[r] sin, 1)under the CURRENT estimate’s parameters.
- classmethod backend_log_density_from_params(cos_t, sin_t, mu_x, mu_y, engine)[source]
Engine-neutral projected-normal log-density from
(cos, sin)and the mean vector.
- backend_seq_log_density(x, engine)[source]
Engine-neutral vectorized log-density for encoded
(cos, sin)data.
- classmethod backend_stacked_params(dists, engine)[source]
Stacked projected-normal mean vectors for a homogeneous mixture kernel.
- classmethod backend_stacked_log_density(x, params, engine)[source]
Return an
(n, k)matrix of projected-normal log densities.
- classmethod backend_stacked_sufficient_statistics(x, weights, params, engine)[source]
Stacked E-step resultants
(sum r cos, sum r sin, count)under per-component parameters.
- sampler(seed=None)[source]
Return a sampler that draws
N(mu, I_2)and returns the angle.- Parameters:
seed (int | None)
- Return type:
ProjectedNormalSampler
- estimator(pseudo_count=None)[source]
Return an EM (latent-radius) estimator for
mu.- Parameters:
pseudo_count (float | None)
- Return type:
ProjectedNormalEstimator
- dist_to_encoder()[source]
Return the data encoder used by this distribution (cos/sin of the angle).
- Return type:
ProjectedNormalDataEncoder
- class ProjectedNormalSampler(dist, seed=None)[source]
Bases:
DistributionSamplerDraw angles as
atan2(Z2, Z1)forZ ~ N(mu, I_2).- Parameters:
dist (ProjectedNormalDistribution)
seed (int | None)
- sample(size=None)[source]
Draw observations.
Combinator samplers (mixture/sequence/…) accept
batched. Withbatched=True(the default) each child stream is drawn in one vectorized call instead of a per-draw Python loop – far faster. Because every child sampler owns an independentRandomState, batching consumes each stream in the same order as the loop, so the draws are identical to the legacy path.batched=Falseforces that legacy per-draw loop as a guaranteed- stable reference. Leaf samplers are already vectorized and ignore the flag.
- class ProjectedNormalAccumulator(name=None, keys=None)[source]
Bases:
SequenceEncodableStatisticAccumulatorAccumulate the
E[r|theta]-weighted resultant(sum r*cos, sum r*sin, count)(EM E-step).- update(x, weight, estimate)[source]
- initialize(x, weight, rng)[source]
- Parameters:
x (float)
weight (float)
rng (RandomState | None)
- Return type:
None
- seq_update(x, weights, estimate)[source]
- seq_initialize(x, weights, rng)[source]
- Parameters:
weights (ndarray)
rng (RandomState | None)
- Return type:
None
- combine(suff_stat)[source]
- from_value(x)[source]
- key_merge(stats_dict)[source]
Pool this accumulator’s statistics into
stats_dictunder its merge key.The structural default implements the common single-key pattern: store the accumulator under
self.keysthe first time the key is seen, elsecombineinto the one already there. Accumulators with several named keys (e.g. an HMM’s init/trans/state keys) or a non-accumulator stats payload override this. AkeysofNone(the default) is a no-op.
- key_replace(stats_dict)[source]
Replace this accumulator’s statistics from the pooled
stats_dictentry (see key_merge).
- acc_to_encoder()[source]
- Return type:
ProjectedNormalDataEncoder
- class ProjectedNormalAccumulatorFactory(name=None, keys=None)[source]
Bases:
StatisticAccumulatorFactoryFactory for ProjectedNormalAccumulator.
- make()[source]
- Return type:
ProjectedNormalAccumulator