Source code for qca.engines.mvqca

"""Multi-value QCA engine."""

from __future__ import annotations

from collections.abc import Sequence

import pandas as pd

from qca.engines._helpers import (
    normalize_engine_condition_types,
    resolve_engine_conditions,
)
from qca.engines.base import QCAEngineBase


[docs] class MVQCA(QCAEngineBase): """First-class mvQCA engine for categorical multi-value conditions.""" qca_type = "mvQCA" def __init__( self, data: pd.DataFrame, outcome: str, conditions: Sequence[str] | None = None, case_id: str | None = None, condition_types: dict[str, str] | None = None, multivalue_conditions: Sequence[str] | None = None, ) -> None: resolved_conditions = resolve_engine_conditions( engine_name="MVQCA", conditions=conditions, legacy_conditions=multivalue_conditions, legacy_name="multivalue_conditions", ) if condition_types is None: super().__init__( data=data, case_id=case_id, set_conditions=[], multivalue_conditions=resolved_conditions, outcome=outcome, ) return normalized_types = normalize_engine_condition_types( engine_name="MVQCA", condition_types=condition_types, allowed_types={"multi"}, ) super().__init__( data=data, case_id=case_id, outcome=outcome, conditions=resolved_conditions, condition_types=normalized_types, ) def _validate(self) -> None: super()._validate() self._validate_multivalue_schema() self._validate_multivalue_domains() def _validate_multivalue_schema(self) -> None: non_multi = [spec.name for spec in self.condition_specs if not spec.is_multi] if non_multi: raise ValueError( f"MVQCA requires multi-value condition specs. Non-multi: {non_multi}" ) if self.set_conditions: raise ValueError( "MVQCA does not support crisp/fuzzy set conditions. " f"Got {self.set_conditions}." ) def _validate_multivalue_domains(self) -> None: degenerate: list[str] = [] for condition in self.multivalue_conditions: values = self.data[condition].dropna().unique() if len(values) < 2: degenerate.append(condition) if degenerate: raise ValueError( "MVQCA requires at least two observed values for each " f"multi-value condition. Degenerate: {degenerate}" ) def __repr__(self) -> str: return ( f"MVQCA(" f"n_cases={self.n_cases}, " f"conditions={self.conditions}, " f"outcome={self.outcome!r}" f")" )
__all__ = ["MVQCA"]