extendedmosaicperm.factor module

class ExtendMosaicFactorTest(*args, sign_flipping=False, seed=123, ridge_alphas=None, adaptive_tiling=False, **kwargs)[source]

Bases: MosaicFactorTest

Extended mosaic factor test.

Extends mosaicperm.factor.MosaicFactorTest by adding:

  • sign-flip inference instead of standard permutation,

  • RidgeCV-based residual estimation,

  • adaptive tiling of data based on the correlation structure.

Parameters:
  • *args – Positional arguments forwarded to the base mosaicperm.factor.MosaicFactorTest constructor.

  • sign_flipping (bool) – Whether to use sign-flip inference instead of permutations.

  • seed (int) – Seed for the internal random number generator used for sign flips.

  • ridge_alphas (Optional[ndarray]) – Sequence of alpha values for sklearn.linear_model.RidgeCV. If None, ordinary least squares (OLS) is used to estimate residuals.

  • adaptive_tiling (bool) – If True, tiles are built automatically from outcomes and exposures using extendedmosaicperm.tilings.build_adaptive_tiling().

  • **kwargs – Keyword arguments forwarded to the base mosaicperm.factor.MosaicFactorTest constructor.

sign_flipping

Boolean flag indicating whether sign-flip inference is used.

rng

NumPy random number generator used for sign flips.

ridge_alphas

Grid of RidgeCV penalization strengths, or None for OLS.

Examples

Basic workflow with fixed exposures (2D exposures) and OLS residuals:

>>> import numpy as np
>>> from mosaicperm import statistics
>>> from extendedmosaicperm.factor import ExtendMosaicFactorTest
>>> from extendedmosaicperm.tilings import build_adaptive_tiling
>>>
>>> T, N, K = 60, 12, 3
>>> rng = np.random.default_rng(0)
>>> F = rng.standard_normal((T, K))
>>> B = rng.standard_normal((K, N))
>>> Y = F @ B + rng.standard_normal((T, N))
>>>
>>> tiles = build_adaptive_tiling(Y, B.T)  # exposures as (N x K)
>>> mpt = ExtendMosaicFactorTest(
...     outcomes=Y,
...     exposures=B.T,
...     tiles=tiles,
...     test_stat=statistics.mean_maxcorr_stat,
...     sign_flipping=True,     # enable sign-flip inference
...     ridge_alphas=None,      # OLS for residuals
...     seed=0,
... )
>>> _ = mpt.compute_mosaic_residuals()  
>>> p = mpt._compute_p_value(nrand=20, verbose=False)
>>> 0.0 <= p <= 1.0
True

Ridge-regularized residuals:

>>> import numpy as np
>>> from mosaicperm import statistics
>>> from extendedmosaicperm.factor import ExtendMosaicFactorTest
>>> from extendedmosaicperm.tilings import build_adaptive_tiling
>>>
>>> T, N, K = 60, 12, 3
>>> rng = np.random.default_rng(1)
>>> F = rng.standard_normal((T, K))
>>> B = rng.standard_normal((K, N))
>>> Y = F @ B + rng.standard_normal((T, N))
>>>
>>> tiles = build_adaptive_tiling(Y, B.T)
>>> alphas = np.logspace(-3, 3, 5)
>>> mpt_ridge = ExtendMosaicFactorTest(
...     outcomes=Y,
...     exposures=B.T,
...     tiles=tiles,
...     test_stat=statistics.mean_maxcorr_stat,
...     ridge_alphas=alphas,    # use RidgeCV
...     sign_flipping=False,    # standard permutation
...     seed=1,
... )
>>> _ = mpt_ridge.compute_mosaic_residuals()  
>>> p_ridge = mpt_ridge._compute_p_value(nrand=20, verbose=False)
>>> 0.0 <= p_ridge <= 1.0
True

Adaptive tiling (no need to pass tiles explicitly):

>>> import numpy as np
>>> from mosaicperm import statistics
>>> from extendedmosaicperm.factor import ExtendMosaicFactorTest
>>>
>>> T, N, K = 60, 12, 3
>>> rng = np.random.default_rng(2)
>>> F = rng.standard_normal((T, K))
>>> B = rng.standard_normal((K, N))
>>> Y = F @ B + rng.standard_normal((T, N))
>>>
>>> mpt_adapt = ExtendMosaicFactorTest(
...     outcomes=Y,
...     exposures=B.T,
...     adaptive_tiling=True,
...     test_stat=statistics.mean_maxcorr_stat,
...     sign_flipping=True,
...     seed=2,
... )
>>> _ = mpt_adapt.compute_mosaic_residuals()  
>>> p_adapt = mpt_adapt._compute_p_value(nrand=20, verbose=False)
>>> 0.0 <= p_adapt <= 1.0
True
compute_mosaic_residuals()[source]

Compute mosaic residuals for all tiles.

For each (batch, group) tile, factor loadings are estimated and residuals are computed. If ridge_alphas is None, residuals are obtained via OLS; otherwise, a separate RidgeCV is fit for each observation in the tile.

Returns:

Residual matrix with the same shape as self.outcomes.

Return type:

np.ndarray

flip_sign_residuals()[source]

Apply random sign flips within each tile.

For each tile (batch × group), an i.i.d. Rademacher vector of length len(batch) is drawn and applied across all assets in the tile.

Return type:

None