Combinational Regularity Analysis (CORA) in an R environment.
The package is named CORAtool because CRAN already carries a package
called cora; the method it implements is CORA.
Combinational Regularity Analysis is a configurational comparative method. It
searches data for INUS structures - cause-effect relations marked by
conjunctivity (a and not b and c) and disjunctivity (d
or e or f) - using Boolean minimisation algorithms borrowed from
switching circuit analysis. Unlike related methods, CORA analyses structures
with simple as well as complex effects, and it handles multi-value conditions.
This package is an R port of the Python packages CORA and LOGIGRAM by Zuzana Sebechlebská, Lusine Mkrtchyan and Alrik Thiem. It computes in plain R and requires no Python installation.
It is an independent implementation and is not endorsed by the authors of the original packages. Anything it gets wrong by departing from the Python implementation is this package's responsibility, not theirs.
# install.packages("remotes")
# remotes::install_github("youngchanresearcher/CORAtool")
install.packages("CORAtool", repos = c('https://youngchanresearcher.r-universe.dev', 'https://cloud.r-project.org'))
library(CORAtool)
library(CORAtool)
df <- data.frame(A = c(1, 0, 1, 0),
B = c(1, 0, 0, 1),
C = c(0, 1, 1, 0),
OUT = c(1, 1, 0, 1))
ctx <- cora_context(df, output_labels = "OUT")
cora_truth_table(ctx) # the configurations the analysis works on
cora_prime_implicants(ctx) # #A{0}, C{0}, B{1}
cora_irredundant_sums(ctx) # M1: #A{0} + C{0} ; M2: #A{0} + B{1}
Every literal is printed as CONDITION{value}, so a term says outright which
value of a condition it stands for: B{2}*D{0} is B at 2 and D at 0. An
essential prime implicant is prefixed with #. The package does not use the
upper/lower case convention of the Python implementation, which marks a
negated literal by the presence of 0 in its value set and therefore says
nothing when a condition happens to be coded without a zero.
cora_context() aggregates the cases into configurations and decides which
of them count as positive:
| argument | meaning |
|---|---|
n_cut |
minimum number of cases below which a configuration is a don't care |
inc_score1 |
minimum inclusion score for an output function value of 1 |
inc_score2, U
|
the second inclusion cut-off and which value it applies to |
case_col |
column holding case identifiers |
algorithm |
"ON-DC" (Quine-McCluskey over positive and don't care terms) or "ON-OFF" (McCluskey's modified algorithm over positive and negative terms) |
Conditions must be coded from zero upwards, with no gaps: 0, 1, 2, ...
The package refuses data that is coded otherwise and names the columns to
fix, as the other configurational packages in R do. as.integer() on a
factor numbers the levels from one, so this is easy to run into:
df <- cora_recode(df, c("A", "B")) # or cora_recode(df) to find them itself
Outcome values that count as positive are declared in curly brackets. With more than one outcome column the analysis returns irredundant systems rather than sums:
ctx <- cora_context(bergschlosser, c("AUTH{1}", "DEM{1}"),
input_labels = c("PS", "RQ", "LRC"),
case_col = "Case", inc_score1 = 0.6,
algorithm = "ON-OFF")
cora_irredundant_systems(ctx)
cora_data_mining() scores every n-tuple of conditions, a configurational
version of Occam's razor:
cora_data_mining(mccluskey, c("F1", "F2"), len_of_tuple = 2)
cora_logigram() draws a solution, or any expression in disjunctive normal
form, as a two-level logic diagram:
cora_logigram("A{1}*B{1}+C{0}<=>F")
cora_logigram(cora_irredundant_sums(ctx)[[1]])
A solution writes itself above its own diagram: the implicants, with the #
that marks an essential one, and the coverage and inclusion scores. Pass
show_terms = TRUE to label each gate with the conjunction it forms, title
and subtitle to write your own header, or NA to either for no header at
all.
The diagram reader still accepts the upper/lower case notation on input
("A*B+c*A+b<=>F"), so expressions written by hand or taken from the Python
implementation can be drawn as they are.
| function | purpose |
|---|---|
cora_context() |
data and analytical choices |
cora_truth_table() |
configurations after aggregation and cut-offs |
cora_prime_implicants() |
Boolean minimisation |
cora_pi_chart() |
prime implicant chart |
cora_irredundant_sums() |
solutions, one outcome |
cora_irredundant_systems() |
solutions, several outcomes |
cora_petrick() |
Petrick's method on a coverage list |
cora_recode() |
map conditions onto 0, 1, 2, ...
|
cora_pi_details(), cora_system_details(), cora_solutions()
|
summary tables |
cora_coverage_score(), cora_inclusion_score()
|
sufficiency statistics |
cora_describe(), cora_dnf()
|
textual renderings of a solution |
cora_logigram() |
two-level logic diagram |
cora_data_mining() |
configurational data mining |
cora_compare_python() |
optional cross-check against the Python package |
vignette("cora", package = "CORAtool")
walks through an analysis end to end in English: what the method looks for, the five stages, how to read coverage and inclusion, multi-value conditions and complex effects, the diagrams, choosing between the algorithms, and what to do when there are more solutions than anyone can report.
A fuller manual goes further: the theory, every stage of the pipeline, how to read a diagram, the six defects found in the Python implementation with their source locations, and how QCA, QCApro and cna handle the same problems. It ships in both English and Traditional Chinese:
file.show(system.file("docs", "manual_en.md", package = "CORAtool"))
file.show(system.file("docs", "manual_zh-TW.md", package = "CORAtool"))
inst/examples/getting-started.R is a runnable script over the same
material.
tools/acceptance.R exercises every exported function against an installed
copy, confirms the behaviours this version introduces, and checks that the
inputs which should be refused are refused:
Rscript tools/acceptance.R
tools/check.R builds the tarball and runs R CMD check --as-cran over it,
then prints only the checks that did not return OK and says which of those
come from the machine rather than from the package:
Rscript tools/check.R
swiss_minaret, gross_carvin, mccluskey and bergschlosser, all taken
from the examples of the Python CORA package.
The R results were checked configuration by configuration against the Python package on its own test and example data: truth tables, prime implicants, coverage sets and solution sets agree. Five differences are worth knowing, and each of them is this package's own judgement rather than the original authors':
0, 1, 2, ... is refused, with cora_recode() offered as the fix. The
Python implementation accepts it and computes: its "ON-OFF" algorithm
restores a free literal as {0, ..., levels - 1} and drops every row
outside that set, so coverage sets come out short or empty, and the check
deciding which outcomes a prime implicant refers to then succeeds
vacuously on the empty set and assigns it every outcome. (This package
also restores free literals from the values a condition actually takes,
so the two algorithms agree once the coding is right.)CONDITION{value}. The Python
implementation prints a binary literal in upper or lower case depending on
whether 0 is in its value set, which distinguishes nothing when a condition
is coded without a zero: A then stands for A's lower value and B for
B's upper value, indistinguishably. Nothing in the computation changes;
#a + B here reads #A{0} + B{1}. The diagram reader still accepts the
case notation on input.M1 in R need not be M1 in
Python. The sets of solutions are the same."ON-OFF" with several
outcomes. The Python implementation evaluates such a prime implicant
against every outcome column rather than against the outcomes the prime
implicant refers to, which makes its score disagree with the one the same
prime implicant receives under "ON-DC". This package uses the prime
implicant's own outcomes in both algorithms, so the two agree.1 is reported by cora_data_mining() with
zero solutions and zero scores. The Python implementation means to do
the same, but its check never fires, so such a tuple is scored as
though it explained the outcome.cora_compare_python() runs a context through both implementations and
reports whether they agree; it needs reticulate and the Python package, and
nothing else in the package does.
Cite this package, the method it implements, and the packages it was
adapted from. citation("CORAtool") prints all three entries:
Chan, Y. (2026). CORAtool: Combinational Regularity Analysis. R package version 0.1.2.
Thiem, A., Mkrtchyan, L., & Sebechlebská, Z. (2022). Combinational Regularity Analysis (CORA) - a new method for uncovering complex causation in medical and health research. BMC Medical Research Methodology, 22(1), 333.
Sebechlebská, Z., Mkrtchyan, L., & Thiem, A. (2023). CORA and LOGIGRAM: A duo of Python packages for Combinational Regularity Analysis (CORA). Journal of Open Source Software, 8(85), 5019.
GPL (>= 3), as required by the original implementation from which this
package is derived. See inst/NOTICE for attribution details.