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")
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 itselfOutcome 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.Rtools/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.Rswiss_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.