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: SequenceEncodableProbabilityDistribution

Isotropic projected normal PN(mu) on the circle, mu = (mu_x, mu_y).

Parameters:
density(x)[source]

Return the probability density at a single angle (radians).

Parameters:

x (float)

Return type:

float

log_density(x)[source]

Return the log-density at a single angle (radians).

Parameters:

x (float)

Return type:

float

seq_log_density(x)[source]

Return vectorized log-density for sequence-encoded (cos, sin) observations.

Parameters:

x (tuple[ndarray, ndarray])

Return type:

ndarray

classmethod compute_capabilities()[source]

Describe backend support for generated projected-normal kernels.

classmethod compute_declaration()[source]

Return the structured compute declaration for projected normal distributions.

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.

Parameters:
Return type:

tuple[Any, …]

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.

Parameters:
Return type:

Any

backend_seq_log_density(x, engine)[source]

Engine-neutral vectorized log-density for encoded (cos, sin) data.

Parameters:
Return type:

Any

classmethod backend_stacked_params(dists, engine)[source]

Stacked projected-normal mean vectors for a homogeneous mixture kernel.

Parameters:
  • dists (Sequence[ProjectedNormalDistribution])

  • engine (Any)

Return type:

dict[str, Any]

classmethod backend_stacked_log_density(x, params, engine)[source]

Return an (n, k) matrix of projected-normal log densities.

Parameters:
Return type:

Any

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.

Parameters:
Return type:

tuple[Any, Any, Any]

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: DistributionSampler

Draw angles as atan2(Z2, Z1) for Z ~ N(mu, I_2).

Parameters:
  • dist (ProjectedNormalDistribution)

  • seed (int | None)

sample(size=None)[source]

Draw one angle or an array of iid angles.

Parameters:

size (int | None)

Return type:

float | ndarray

class ProjectedNormalAccumulator(name=None, keys=None)[source]

Bases: SequenceEncodableStatisticAccumulator

Accumulate the E[r|theta]-weighted resultant (sum r*cos, sum r*sin, count) (EM E-step).

Parameters:
  • name (str | None)

  • keys (str | None)

update(x, weight, estimate)[source]

Accumulate one EM radius-weighted directional contribution.

Parameters:
  • x (float)

  • weight (float)

  • estimate (ProjectedNormalDistribution | None)

Return type:

None

initialize(x, weight, rng)[source]

Initialize statistics from one angle.

Parameters:
Return type:

None

seq_update(x, weights, estimate)[source]

Accumulate EM radius-weighted direction statistics from encoded angles.

Parameters:
Return type:

None

seq_initialize(x, weights, rng)[source]

Initialize statistics from encoded angles.

Parameters:
Return type:

None

combine(suff_stat)[source]

Merge another projected-normal sufficient-statistic tuple.

Parameters:

suff_stat (tuple[float, float, float])

Return type:

ProjectedNormalAccumulator

value()[source]

Return radius-weighted x/y sums and total weight.

Return type:

tuple[float, float, float]

from_value(x)[source]

Replace accumulator contents from radius-weighted statistics.

Parameters:

x (tuple[float, float, float])

Return type:

ProjectedNormalAccumulator

key_merge(stats_dict)[source]

Merge keyed statistics into stats_dict when keys are configured.

Parameters:

stats_dict (dict[str, Any])

Return type:

None

key_replace(stats_dict)[source]

Replace this accumulator from keyed statistics when available.

Parameters:

stats_dict (dict[str, Any])

Return type:

None

acc_to_encoder()[source]

Return the encoder used by this accumulator.

Return type:

ProjectedNormalDataEncoder

class ProjectedNormalAccumulatorFactory(name=None, keys=None)[source]

Bases: StatisticAccumulatorFactory

Factory for ProjectedNormalAccumulator.

Parameters:
  • name (str | None)

  • keys (str | None)

make()[source]

Create a fresh projected-normal accumulator.

Return type:

ProjectedNormalAccumulator

class ProjectedNormalEstimator(name=None, keys=None)[source]

Bases: ParameterEstimator

EM estimator: mu = mean(E[r|theta] u(theta)) (one M-step per accumulated E-step).

Parameters:
  • name (str | None)

  • keys (str | None)

accumulator_factory()[source]

Return an accumulator factory for projected-normal EM statistics.

Return type:

ProjectedNormalAccumulatorFactory

estimate(nobs, suff_stat)[source]

Estimate the projected-normal mean vector from EM sufficient statistics.

Parameters:
Return type:

ProjectedNormalDistribution

class ProjectedNormalDataEncoder[source]

Bases: DataSequenceEncoder

Encode angles as their cosine and sine.

seq_encode(x)[source]

Encode angles as cosine and sine arrays.

Parameters:

x (Sequence[float])

Return type:

tuple[ndarray, ndarray]