Skip to content

Commit f4c2218

Browse files
authored
Ensure composite correlator works with fewer observations than variables (#88)
1 parent d90fac6 commit f4c2218

1 file changed

Lines changed: 33 additions & 6 deletions

File tree

src/probabilit/correlation.py

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -216,9 +216,7 @@ class Cholesky(Correlator):
216216
Parameters
217217
----------
218218
correlation_matrix : ndarray
219-
Target correlation matrix of shape (K, K). The Iman-Conover will
220-
try to induce a correlation on the data set X so that corr(X) is
221-
as close to `correlation_matrix` as possible.
219+
Target correlation matrix of shape (K, K).
222220
223221
Examples
224222
--------
@@ -931,7 +929,29 @@ def update_column(self, col, i, j):
931929

932930
@dataclasses.dataclass(init=False, repr=True, eq=False)
933931
class Composite(Correlator):
934-
"""A composition where we first run ImanConover, then Permutation."""
932+
"""A composition where we first run ImanConover, then Permutation.
933+
934+
Examples
935+
--------
936+
937+
This example shows a data matrix X with more variables than observations.
938+
939+
>>> correlation_matrix = np.eye(4)
940+
>>> rng = np.random.default_rng(42)
941+
>>> X = rng.normal(size=(3, 4))
942+
>>> corr_X = np.corrcoef(X, rowvar=False)
943+
>>> corr_X[np.tril_indices_from(corr_X, k=-1)].round(2)
944+
array([0.85, 0.96, 0.96, 1. , 0.85, 0.96])
945+
946+
The composite correlator will be able to remove some correlations,
947+
but some remain high because there are few observations to permute:
948+
949+
>>> transform = Composite(random_state=42).set_target(correlation_matrix)
950+
>>> Xt = transform(X)
951+
>>> corr_Xt = np.corrcoef(Xt, rowvar=False)
952+
>>> corr_Xt[np.tril_indices_from(corr_Xt, k=-1)].round(2)
953+
array([-0.23, 1. , -0.25, -0.27, -0.88, -0.24])
954+
"""
935955

936956
def __init__(self, *args, **kwargs):
937957
self.iman_conover_correlator = ImanConover()
@@ -943,8 +963,15 @@ def set_target(self, correlation_matrix, *, weights=None):
943963
return self
944964

945965
def __call__(self, X):
946-
# First run ImanConover to get a good starting point
947-
X_ic = self.iman_conover_correlator(X)
966+
try:
967+
# First run ImanConover to get a good starting point
968+
X_ic = self.iman_conover_correlator(X)
969+
except ValueError as e:
970+
if "The matrix X must have rows > columns" in str(e):
971+
X_ic = X
972+
else:
973+
raise # re-raise if it's a different ValueError
974+
948975
# Then run Permutation
949976
return self.permutation_correlator(X_ic)
950977

0 commit comments

Comments
 (0)