Truth tables¶
The truth table is the analytical heart of QCA. It enumerates every logically possible configuration of the conditions — all \(2^k\) corners of the property space — and reports what the evidence says about each.
Construction¶
from setqca import build_truth_table
table = build_truth_table(
data,
outcome="Y",
conditions=["A", "B", "C"],
inclusion_cutoff=0.8,
exclusion_cutoff=0.5,
pri_cutoff=0.6,
frequency_cutoff=2,
case_id="country",
)
Corner assignment¶
Each case is assigned to the corner implied by whether each of its memberships
lies above or below the crossover. A case with A=0.9, B=0.2 belongs to corner
A=1, B=0.
Corner membership is then computed for every case in every corner using the
minimum t-norm, with absent conditions negated as 1 - x. This is what makes
consistency a fuzzy quantity rather than a simple count.
Row coding¶
Rows are coded in this order:
| Code | Condition | Meaning |
|---|---|---|
R |
n < frequency_cutoff |
Logical remainder — too little evidence to judge |
1 |
consistency >= inclusion_cutoff and PRI >= pri_cutoff |
Sufficient for the outcome |
C |
consistency >= exclusion_cutoff |
Contradictory — between the two cutoffs |
0 |
otherwise | Not sufficient |
The frequency test comes first: a row with insufficient cases is a remainder no matter how consistent the few cases it has happen to be.
The contradictory band¶
By default exclusion_cutoff equals inclusion_cutoff, which collapses the
C band to nothing — every observed row is either 1 or 0. Setting a lower
exclusion cutoff creates an explicit grey zone:
Rows coded C participate in neither the on-set nor the don't-care set. They
are excluded from minimisation, which is deliberately conservative: an
ambiguous row should not silently drive a solution.
Inspecting the table¶
The tidy frame carries the condition states, the minterm index, case count
n, consistency, PRI, the OUT code, and the case labels.
Minterm indices are big-endian over the condition order you supplied, so with
conditions ["A", "B", "C"] the configuration A=1, B=1, C=0 is minterm 6.
Set-valued accessors give direct access to each group:
table.positive_minterms # coded "1"
table.negative_minterms # coded "0"
table.contradictory_minterms # coded "C"
table.remainder_minterms # coded "R"
Nothing is thrown away¶
Rows excluded by a threshold are kept, with their classification and the reason recorded. A row's outcome code alone conflates situations that call for different responses:
table.positive_rows() # coded "1"
table.negative_rows() # coded "0"
table.contradictions() # coded "C"
table.remainders() # coded "R"
table.excluded_rows() # kept out by a *threshold*, not by the evidence
print(table.summary())
excluded_rows() is the interesting one. It returns rows the frequency or PRI
cutoff held back — the rows a different analytical choice would have admitted.
A row with genuinely low consistency is excluded by the data and is not
listed, because no threshold would rescue it.
Every row carries exclusion_reason in words:
frequency 1 below the cutoff of 2
consistency 0.643 below the inclusion cutoff of 0.8
PRI 0.412 below the cutoff of 0.7
Consistency and PRI fail differently
A row can clear the consistency cutoff and still be excluded by the PRI cutoff. Both are named separately, because the responses differ: low consistency means the configuration does not reliably produce the outcome, while low PRI means it is nearly as good at producing the outcome's negation.
A table is a reusable object¶
A truth table carries everything Boolean minimisation needs, so it can be stored and re-minimised without recalibrating or rebuilding:
text = table.to_json()
restored = TruthTable.from_json(text)
restored.minimize() # conservative
restored.minimize(include_remainders=True) # parsimonious
Both agree with the estimator exactly — there are tests asserting so. Only the
case-level parameters of fit need the original data, since those describe
cases rather than configurations; use FSQCA.fit for those.
Limited diversity¶
The gap between \(2^k\) logically possible configurations and the handful you actually observe is limited diversity, and it is the central practical problem in QCA. With 6 conditions there are 64 corners; a study of 25 cases can occupy at most 25 of them.
Remainders are exactly what the conservative and parsimonious solutions disagree about:
- the conservative solution uses no remainders, so it assumes nothing;
- the parsimonious solution treats every remainder as a don't-care, so it assumes each unobserved configuration behaves however is most convenient.
Neither is more correct in general. Report the number of remainders alongside your solutions — if most of your property space is unobserved, the parsimonious solution rests almost entirely on untested assumptions.
setqca.truth_table ¶
Truth-table construction for crisp-set and fuzzy-set QCA.
TruthCode
module-attribute
¶
Outcome code of a truth-table row.
"1" sufficient, "0" not sufficient, "C" contradictory,
"R" logical remainder.
TruthTableRow
dataclass
¶
TruthTableRow(
minterm: int,
configuration: tuple[int, ...],
frequency: int,
consistency: float,
pri: float,
outcome: TruthCode,
cases: tuple[str, ...],
exclusion_reason: str | None = None,
)
A single causal configuration and its empirical fit.
Attributes:
| Name | Type | Description |
|---|---|---|
exclusion_reason |
str | None
|
Why the row is not coded sufficient, in words. |
excluded_by_threshold
property
¶
Return whether a threshold, rather than the evidence, kept this row out.
True for rows held back by the frequency or PRI cutoffs. A row with genuinely low consistency is excluded by the data, not by a choice.
TruthTable
dataclass
¶
TruthTable(
conditions: tuple[str, ...],
outcome_name: str,
rows: tuple[TruthTableRow, ...],
inclusion_cutoff: float,
exclusion_cutoff: float,
pri_cutoff: float,
frequency_cutoff: int,
)
Immutable QCA truth table covering every logically possible corner.
positive_minterms
property
¶
Return minterms of rows coded sufficient for the outcome.
negative_minterms
property
¶
Return minterms of rows coded not sufficient for the outcome.
contradictory_minterms
property
¶
Return minterms of rows falling between the exclusion and inclusion cutoffs.
remainder_minterms
property
¶
Return minterms of logical remainders, i.e. rows below the frequency cutoff.
rows_with ¶
rows_with(code: TruthCode) -> tuple[TruthTableRow, ...]
Return the rows carrying one outcome code, in minterm order.
positive_rows ¶
positive_rows() -> tuple[TruthTableRow, ...]
negative_rows ¶
negative_rows() -> tuple[TruthTableRow, ...]
contradictions ¶
contradictions() -> tuple[TruthTableRow, ...]
remainders ¶
remainders() -> tuple[TruthTableRow, ...]
excluded_rows ¶
excluded_rows() -> tuple[TruthTableRow, ...]
Return rows a threshold kept out, rather than the evidence.
These are the rows whose exclusion is a consequence of an analytical choice — the frequency or PRI cutoff — and therefore the rows to revisit when judging how much the result depends on those choices. A row with genuinely low consistency is excluded by the data and is not listed here.
Source code in src/setqca/truth_table.py
summary ¶
Return a short account of how the table came out.
Source code in src/setqca/truth_table.py
to_frame ¶
Return a tidy pandas representation of the truth table.
Returns:
| Type | Description |
|---|---|
DataFrame
|
One row per configuration, with the condition states followed by
|
Source code in src/setqca/truth_table.py
minimize ¶
minimize(
*,
include_remainders: bool = False,
max_solutions: int = 256,
) -> tuple[BooleanSolution, ...]
Minimise directly from the table, without the original data.
A stored truth table carries everything Boolean minimisation needs, so a saved table can be re-minimised under different assumptions without recalibrating or rebuilding it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
include_remainders
|
bool
|
Treat logical remainders as don't-cares, giving the parsimonious solution rather than the conservative one. |
False
|
max_solutions
|
int
|
Upper bound on tied minimal covers. |
256
|
Returns:
| Type | Description |
|---|---|
tuple of BooleanSolution
|
Boolean covers only. Case-level parameters of fit need the original
data and are produced by :meth: |
Raises:
| Type | Description |
|---|---|
ValueError
|
If no row is coded sufficient. |
Source code in src/setqca/truth_table.py
to_dict ¶
Return a JSON-compatible dictionary describing the whole table.
Source code in src/setqca/truth_table.py
from_dict
classmethod
¶
from_dict(payload: dict[str, Any]) -> TruthTable
Rebuild a table from :meth:to_dict output.
Raises:
| Type | Description |
|---|---|
KeyError
|
If a required key is missing. |
Source code in src/setqca/truth_table.py
to_json ¶
build_truth_table ¶
build_truth_table(
data: DataFrame,
*,
outcome: str,
conditions: list[str] | tuple[str, ...],
inclusion_cutoff: float = 0.8,
exclusion_cutoff: float | None = None,
pri_cutoff: float = 0.0,
frequency_cutoff: int = 1,
case_id: str | None = None,
allow_crossover_cases: bool = False,
) -> TruthTable
Construct a complete binary truth table from calibrated data.
Fuzzy cases are assigned to the crisp truth-table corner implied by scores above/below 0.5. Cases exactly at the crossover are rejected by default because their corner assignment is ambiguous.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
DataFrame
|
Calibrated condition and outcome memberships in |
required |
outcome
|
str
|
Name of the outcome column. |
required |
conditions
|
list of str or tuple of str
|
Names of the condition columns, in the order used for minterm coding. |
required |
inclusion_cutoff
|
float
|
Minimum sufficiency consistency for a row to be coded |
0.8
|
exclusion_cutoff
|
float
|
Consistency below which a row is coded |
None
|
pri_cutoff
|
float
|
Minimum PRI for a row to be coded |
0.0
|
frequency_cutoff
|
int
|
Minimum number of cases for a row to count as observed. |
1
|
case_id
|
str
|
Column holding case labels. Defaults to the frame index. |
None
|
allow_crossover_cases
|
bool
|
Permit membership scores of exactly 0.5. |
False
|
Returns:
| Type | Description |
|---|---|
TruthTable
|
Complete table with one row per corner of the property space. |
Raises:
| Type | Description |
|---|---|
TypeError
|
If |
ValueError
|
If any cutoff is out of range, memberships fall outside |
Source code in src/setqca/truth_table.py
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 | |