| Title: | Systematic Theory Development |
|---|---|
| Description: | Provides a rigorous, reproducible workflow for building, developing and testing scientific theories represented as versioned, machine-checkable objects. Functions read and validate theory objects, score them against a versioned rigour checklist, screen constructs for lexical redundancy, and emit byte-identical diagram intermediate representations. The package is the feature-parity twin of a 'Python' package of the same name, with behaviour pinned by a shared specification so the two implementations produce identical verdicts. |
| Authors: | Pablo Bernabeu [aut, cre] (ORCID: <https://orcid.org/0000-0003-1083-2460>) |
| Maintainer: | Pablo Bernabeu <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.6.0 |
| Built: | 2026-10-10 18:00:14 UTC |
| Source: | https://github.com/cran/theoryforge |
Add an alternative theory (BUILDING mode)
tf_add_alternative(theory, id, label, key_constructs = NULL)tf_add_alternative(theory, id, label, key_constructs = NULL)
theory |
A theory object (named list). |
id, label
|
Alternative fields. |
key_constructs |
Optional character vector. |
The (mutated) theory object.
tf_theory("demo-1", "A demonstration theory") |> tf_add_alternative("alt1", "Cognitive appraisal account", key_constructs = c("c_threat"))tf_theory("demo-1", "A demonstration theory") |> tf_add_alternative("alt1", "Cognitive appraisal account", key_constructs = c("c_threat"))
Add an auxiliary assumption (BUILDING mode)
tf_add_assumption(theory, id, statement, added_for = NULL, protects = NULL)tf_add_assumption(theory, id, statement, added_for = NULL, protects = NULL)
theory |
A theory object (named list). |
id, statement
|
Assumption fields. |
added_for |
Optional reason the assumption was added. |
protects |
Optional character vector of prediction ids it protects. |
The (mutated) theory object.
tf_theory("demo-1", "A demonstration theory") |> tf_add_assumption("a1", "Measurement error is negligible.")tf_theory("demo-1", "A demonstration theory") |> tf_add_assumption("a1", "Measurement error is negligible.")
Appends a construct and a provenance entry, returning the mutated theory.
tf_add_construct( theory, id, label, definition, measurement = NULL, boundary_conditions = NULL )tf_add_construct( theory, id, label, definition, measurement = NULL, boundary_conditions = NULL )
theory |
A theory object (named list). |
id, label, definition
|
Construct fields. |
measurement, boundary_conditions
|
Optional character vectors. |
The (mutated) theory object.
tf_theory("demo-1", "A demonstration theory") |> tf_add_construct("c_arousal", "Physiological arousal", "Bodily activation in response to a stressor.")tf_theory("demo-1", "A demonstration theory") |> tf_add_construct("c_arousal", "Physiological arousal", "Bodily activation in response to a stressor.")
Add a prediction to a theory (BUILDING mode)
tf_add_prediction( theory, id, statement, type, derives_from = NULL, diagnostic_vs = NULL )tf_add_prediction( theory, id, statement, type, derives_from = NULL, diagnostic_vs = NULL )
theory |
A theory object (named list). |
id, statement, type
|
Prediction fields. |
derives_from, diagnostic_vs
|
Optional character vectors. |
The (mutated) theory object.
tf_theory("demo-1", "A demonstration theory") |> tf_add_prediction("h1", "Arousal precedes threat appraisal.", "directional")tf_theory("demo-1", "A demonstration theory") |> tf_add_prediction("h1", "Arousal precedes threat appraisal.", "directional")
Add a proposition to a theory (BUILDING mode)
tf_add_proposition(theory, id, from, to, relation, mechanism = NULL)tf_add_proposition(theory, id, from, to, relation, mechanism = NULL)
theory |
A theory object (named list). |
id, from, to, relation
|
Proposition fields. |
mechanism |
Optional mechanism string. |
The (mutated) theory object.
tf_theory("demo-1", "A demonstration theory") |> tf_add_construct("c_arousal", "Arousal", "Bodily activation.") |> tf_add_construct("c_threat", "Perceived threat", "Appraised danger.") |> tf_add_proposition("p1", "c_arousal", "c_threat", "increases")tf_theory("demo-1", "A demonstration theory") |> tf_add_construct("c_arousal", "Arousal", "Bodily activation.") |> tf_add_construct("c_threat", "Perceived threat", "Appraised danger.") |> tf_add_proposition("p1", "c_arousal", "c_threat", "increases")
Compares an amended theory new against its prior version and
returns a Lakatosian verdict.
tf_appraise_amendment(new, prior)tf_appraise_amendment(new, prior)
new |
The amended theory object (named list). |
prior |
The prior theory object (named list). |
A named list with verdict (one of "progressive",
"degenerating", "neutral") and the ascending-sorted
character vectors new_predictions, corroborated_new,
ad_hoc_assumptions.
Lakatos, I. (1970). Falsification and the methodology of scientific research programmes. In Criticism and the growth of knowledge (pp. 91-196). Cambridge University Press. doi:10.1017/cbo9781139171434.009
Meehl, P. E. (1990). Appraising and amending theories. Psychological Inquiry, 1(2), 108-141. doi:10.1207/s15327965pli0102_1
prior <- tf_theory("demo-1", "A demonstration theory") |> tf_add_prediction("h1", "Effect is positive.", "directional") new <- prior |> tf_add_prediction("h2", "Effect is exactly 0.30.", "point") new$test_outcomes <- list(list(prediction_id = "h2", passed = TRUE)) tf_appraise_amendment(new, prior)prior <- tf_theory("demo-1", "A demonstration theory") |> tf_add_prediction("h1", "Effect is positive.", "directional") new <- prior |> tf_add_prediction("h2", "Effect is exactly 0.30.", "point") new$test_outcomes <- list(list(prediction_id = "h2", passed = TRUE)) tf_appraise_amendment(new, prior)
Runs the full rigour checklist (12 items) over a theory object and returns a report, with the items in checklist order.
tf_check(theory)tf_check(theory)
theory |
A theory object (named list), e.g. from |
A named list with elements theory_id, schema_version
(the theory's), checklist_version (the rigour checklist's, which is
what the weights and thresholds came from), maturity,
aggregate_score, gate, n_blockers_failed, and
items (a list of per-item lists).
theory <- tf_theory("demo-1", "A demonstration theory") |> tf_add_construct("c_arousal", "Arousal", "Bodily activation.") |> tf_add_construct("c_threat", "Perceived threat", "Appraised danger.") |> tf_add_proposition("p1", "c_arousal", "c_threat", "causes", mechanism = "Activation raises salience of threat cues.") |> tf_add_prediction("h1", "Arousal precedes threat appraisal.", "point") report <- tf_check(theory) report$aggregate_score report$gatetheory <- tf_theory("demo-1", "A demonstration theory") |> tf_add_construct("c_arousal", "Arousal", "Bodily activation.") |> tf_add_construct("c_threat", "Perceived threat", "Appraised danger.") |> tf_add_proposition("p1", "c_arousal", "c_threat", "causes", mechanism = "Activation raises salience of threat cues.") |> tf_add_prediction("h1", "Arousal precedes threat appraisal.", "point") report <- tf_check(theory) report$aggregate_score report$gate
Compiles a theory's constructs and propositions into a lavaan model
string: constructs with measurement indicators become a latent measurement
model (=~), and propositions become structural paths (~),
covariances (~~), or moderation comment lines. The output is
deterministic.
tf_compile_sem(theory)tf_compile_sem(theory)
theory |
A theory object (named list), e.g. from |
The lavaan model syntax as a single string (LF line endings, single trailing newline).
theory <- tf_theory("demo-1", "A demonstration theory") |> tf_add_construct("c_arousal", "Arousal", "Bodily activation.", measurement = c("heart-rate variability", "self-reported arousal")) |> tf_add_construct("c_threat", "Perceived threat", "Appraised danger.", measurement = c("threat appraisal questionnaire")) |> tf_add_proposition("p1", "c_arousal", "c_threat", "increases") cat(tf_compile_sem(theory))theory <- tf_theory("demo-1", "A demonstration theory") |> tf_add_construct("c_arousal", "Arousal", "Bodily activation.", measurement = c("heart-rate variability", "self-reported arousal")) |> tf_add_construct("c_threat", "Perceived threat", "Appraised danger.", measurement = c("threat appraisal questionnaire")) |> tf_add_proposition("p1", "c_arousal", "c_threat", "increases") cat(tf_compile_sem(theory))
Produces a deterministic diagram IR string for the requested type. The
engine argument is accepted but has no effect, because the IR is
engine independent (Graphviz DOT for the two digraphs, dagitty syntax for
the causal DAG).
tf_diagram(theory, type = "nomological_net", engine = "graphviz")tf_diagram(theory, type = "nomological_net", engine = "graphviz")
theory |
A theory object (named list). |
type |
One of |
engine |
Rendering engine label, accepted but unused (default
|
A single string ending in a newline. Graphviz DOT for the digraphs, dagitty syntax for the causal DAG, and SVG for the Venn.
theory <- tf_theory("demo-1", "A demonstration theory") |> tf_add_construct("c_arousal", "Arousal", "Bodily activation.") |> tf_add_construct("c_threat", "Perceived threat", "Appraised danger.") |> tf_add_proposition("p1", "c_arousal", "c_threat", "causes") cat(tf_diagram(theory, "nomological_net")) cat(tf_diagram(theory, "causal_dag"))theory <- tf_theory("demo-1", "A demonstration theory") |> tf_add_construct("c_arousal", "Arousal", "Bodily activation.") |> tf_add_construct("c_threat", "Perceived threat", "Appraised danger.") |> tf_add_proposition("p1", "c_arousal", "c_threat", "causes") cat(tf_diagram(theory, "nomological_net")) cat(tf_diagram(theory, "causal_dag"))
Assembles a reviewer-facing audit bundle: the header, the rigour-checklist table, the severity list, the provenance list, and the appended preregistration document. The output is deterministic, so the same theory always yields the same dossier.
tf_dossier(theory)tf_dossier(theory)
theory |
A theory object (named list), e.g. from |
The dossier Markdown as a single string (LF line endings, single trailing newline).
theory <- tf_theory("demo-1", "A demonstration theory") |> tf_add_construct("c_arousal", "Arousal", "Bodily activation.") |> tf_add_construct("c_threat", "Perceived threat", "Appraised danger.") |> tf_add_proposition("p1", "c_arousal", "c_threat", "causes", mechanism = "Activation raises salience of threat cues.") |> tf_add_prediction("h1", "Effect is exactly 0.30.", "point") cat(tf_dossier(theory))theory <- tf_theory("demo-1", "A demonstration theory") |> tf_add_construct("c_arousal", "Arousal", "Bodily activation.") |> tf_add_construct("c_threat", "Perceived threat", "Appraised danger.") |> tf_add_proposition("p1", "c_arousal", "c_threat", "causes", mechanism = "Activation raises salience of threat cues.") |> tf_add_prediction("h1", "Effect is exactly 0.30.", "point") cat(tf_dossier(theory))
For every unordered pair of constructs, embeds each definition with the
supplied embedder and computes the cosine similarity (rounded to 6
decimals). The vectors of every compared pair must be of equal, nonzero
length, or the pair is refused. Returns a data frame sorted by descending
cosine then (a, b), flagging pairs at or above threshold for
review. This assistive screen complements the deterministic lexical
tf_redundancy_check(), and its results are only as reproducible as the
supplied embedder.
tf_embedding_redundancy(theory, embedder, threshold = NULL)tf_embedding_redundancy(theory, embedder, threshold = NULL)
theory |
A theory object (named list), e.g. from |
embedder |
A function mapping a definition string to a numeric vector. |
threshold |
Cosine threshold for the |
A data frame with columns a, b, cosine,
flag.
theory <- tf_theory("demo-1", "A demonstration theory") |> tf_add_construct("c_arousal", "Arousal", "bodily activation") |> tf_add_construct("c_threat", "Threat", "appraised danger") # A toy deterministic embedder: bag-of-words counts over a fixed vocabulary. vocab <- c("bodily", "activation", "appraised", "danger") embedder <- function(def) { words <- strsplit(tolower(def), "\\s+")[[1]] vapply(vocab, function(w) sum(words == w), numeric(1)) } tf_embedding_redundancy(theory, embedder)theory <- tf_theory("demo-1", "A demonstration theory") |> tf_add_construct("c_arousal", "Arousal", "bodily activation") |> tf_add_construct("c_threat", "Threat", "appraised danger") # A toy deterministic embedder: bag-of-words counts over a fixed vocabulary. vocab <- c("bodily", "activation", "appraised", "danger") embedder <- function(def) { words <- strsplit(tolower(def), "\\s+")[[1]] vapply(vocab, function(w) sum(words == w), numeric(1)) } tf_embedding_redundancy(theory, embedder)
The bundled set is four example theories and one literature corpus,
panic-corpus.yaml, which tf_read_corpus() reads.
tf_example_names()tf_example_names()
A sorted character vector of the .yaml file names. The Python
twin's example_names() applies the same extension filter, so the two
list the same set.
tf_example_names()tf_example_names()
Path to a packaged example file
tf_example_path(name)tf_example_path(name)
name |
File name, e.g. |
The path to the installed file.
tf_read(tf_example_path("panic-network.theory.yaml"))$idtf_read(tf_example_path("panic-network.theory.yaml"))$id
Assistive helper that builds a corpus by querying the OpenAlex works API
(https://api.openalex.org/works?search=...). This is a network call:
it depends on a live external service whose results change over time, so it
sits outside the package's deterministic core. Each work is mapped to
{id, title, year, keywords, references} (keywords falls back to the top
concepts when no keywords are present).
tf_fetch_corpus(query, per_page = 25, mailto = NULL)tf_fetch_corpus(query, per_page = 25, mailto = NULL)
query |
Free-text search query. |
per_page |
Number of works to request (default |
mailto |
Optional contact email for the OpenAlex "polite pool". |
A corpus object (named list) with schema_version, id,
and records.
## Not run: corpus <- tf_fetch_corpus("panic disorder interoception", mailto = "[email protected]") ## End(Not run)## Not run: corpus <- tf_fetch_corpus("panic disorder interoception", mailto = "[email protected]") ## End(Not run)
Reads the propositions whose relation is causal ("causes",
"increases", "decreases") as directed edges over the constructs
they connect, checks that the resulting graph is acyclic, and returns its
basis set: for every pair of non-adjacent constructs, the claim that the two
are independent given the parents of both. A basis set implies every other
conditional independence the graph entails, so it is the shortest complete
statement of what the theory forbids in data, and each entry is something a
study could find and refute.
tf_implications(theory)tf_implications(theory)
theory |
A theory object (named list), e.g. from |
Constructs that no causal proposition connects are left out, because silence about a construct is not a claim that it is independent of anything. A theory with no causal propositions therefore comes back with an empty basis set and no error.
A named list
list(theory_id, acyclic, constructs, n_edges, implications,
n_implications). constructs holds, in file order, the constructs a
causal proposition connects. acyclic is always TRUE in a
returned record, since a cyclic graph is refused; it is carried so that a
serialised record states the verdict rather than leaving a reader to infer
that the check ran. Each entry of implications is a list
list(a, b, given, statement), where statement renders the
claim as a _||_ b | z1, z2. Pairs come in construct file order, as do
the members of given.
The function stops in three cases. Two constructs sharing an id would give one
node two sets of parents, and a causal proposition naming an undeclared
construct would shrink the graph and so imply independencies the theory never
claimed. A cyclic causal graph has no basis set at all, and the message names
a cycle that was found. The Python twin raises ValueError on the same
three, with the same message text.
Pearl, J. (1988). Probabilistic reasoning in intelligent systems: Networks of plausible inference. Morgan Kaufmann.
Shipley, B. (2000). A new inferential test for path models based on directed acyclic graphs. Structural Equation Modeling, 7(2), 206-218. doi:10.1207/S15328007SEM0702_4
tf_diagram() with type = "causal_dag", which exports the
same subgraph as dagitty syntax without reading it, and the methodological
foundations article for the literature behind the causal-testability
criterion.
# A mediated chain commits the theory to one thing it does not state # directly: arousal and avoidance are independent once threat is held fixed. theory <- tf_theory("mediation", "A mediated chain") |> tf_add_construct("c_arousal", "Arousal", "Bodily activation.") |> tf_add_construct("c_threat", "Perceived threat", "Appraised danger.") |> tf_add_construct("c_avoidance", "Avoidance", "Withdrawal from the trigger.") |> tf_add_proposition("p1", "c_arousal", "c_threat", "increases") |> tf_add_proposition("p2", "c_threat", "c_avoidance", "increases") implied <- tf_implications(theory) implied$n_implications implied$implications[[1]]$statement# A mediated chain commits the theory to one thing it does not state # directly: arousal and avoidance are independent once threat is held fixed. theory <- tf_theory("mediation", "A mediated chain") |> tf_add_construct("c_arousal", "Arousal", "Bodily activation.") |> tf_add_construct("c_threat", "Perceived threat", "Appraised danger.") |> tf_add_construct("c_avoidance", "Avoidance", "Withdrawal from the trigger.") |> tf_add_proposition("p1", "c_arousal", "c_threat", "increases") |> tf_add_proposition("p2", "c_threat", "c_avoidance", "increases") implied <- tf_implications(theory) implied$n_implications implied$implications[[1]]$statement
Returns 0.0 if both sets are empty, otherwise the size of the intersection divided by the size of the union, rounded to 3 decimals.
tf_jaccard(a, b)tf_jaccard(a, b)
a, b
|
Character vectors of tokens (treated as sets). |
A numeric similarity in [0, 1].
tf_jaccard(tf_tokens("arousal threat response"), tf_tokens("threat appraisal response"))tf_jaccard(tf_tokens("arousal threat response"), tf_tokens("threat appraisal response"))
Maps a theory's focal constructs and its registered alternatives onto the
thematic structure of a corpus (computed by tf_litmap()). Each theme is
tagged "under_theorised", "covered", or "crowded".
tf_landscape(theory, corpus, min_link = 2)tf_landscape(theory, corpus, min_link = 2)
theory |
A theory object (named list), e.g. from |
corpus |
A corpus object (named list), e.g. from |
min_link |
Minimum co-occurrence count passed to |
A named list with elements theory_id, themes (each
{id, keywords, alternatives, focal, status}),
under_theorised_fronts, and redundancy_risk.
theory <- tf_theory("demo-1", "Arousal and threat") |> tf_add_construct("c_arousal", "Arousal", "Bodily activation.") corpus <- list( schema_version = "1.0", id = "demo-corpus", records = list( list(id = "w1", keywords = list("arousal", "threat")), list(id = "w2", keywords = list("arousal", "threat")) ) ) tf_landscape(theory, corpus)theory <- tf_theory("demo-1", "Arousal and threat") |> tf_add_construct("c_arousal", "Arousal", "Bodily activation.") corpus <- list( schema_version = "1.0", id = "demo-corpus", records = list( list(id = "w1", keywords = list("arousal", "threat")), list(id = "w2", keywords = list("arousal", "threat")) ) ) tf_landscape(theory, corpus)
Produces a deterministic DOT string for the literature layer.
tf_lit_diagram(obj, type = "keyword_cooccurrence")tf_lit_diagram(obj, type = "keyword_cooccurrence")
obj |
A |
type |
One of |
A single string ending in a newline.
corpus <- list( schema_version = "1.0", id = "demo-corpus", records = list( list(id = "w1", keywords = list("arousal", "threat")), list(id = "w2", keywords = list("arousal", "threat")) ) ) cat(tf_lit_diagram(tf_litmap(corpus), "keyword_cooccurrence"))corpus <- list( schema_version = "1.0", id = "demo-corpus", records = list( list(id = "w1", keywords = list("arousal", "threat")), list(id = "w2", keywords = list("arousal", "threat")) ) ) cat(tf_lit_diagram(tf_litmap(corpus), "keyword_cooccurrence"))
Computes keyword co-occurrence, thematic components, and reference co-citation for a corpus. Records iterate in file order.
tf_litmap(corpus, min_link = 2)tf_litmap(corpus, min_link = 2)
corpus |
A corpus object (named list), e.g. from |
min_link |
Minimum co-occurrence count for an edge to be kept
(default |
A named list with elements n_records, keywords,
keyword_cooccurrence, themes, and co_citation.
corpus <- list( schema_version = "1.0", id = "demo-corpus", records = list( list(id = "w1", keywords = list("arousal", "threat")), list(id = "w2", keywords = list("arousal", "threat")) ) ) tf_litmap(corpus)corpus <- list( schema_version = "1.0", id = "demo-corpus", records = list( list(id = "w1", keywords = list("arousal", "threat")), list(id = "w2", keywords = list("arousal", "threat")) ) ) tf_litmap(corpus)
Compares each DOI in candidate_dois against the theory's
evidence[].source_doi and alternatives[].source_doi fields, by
normalised form (lowercased, with any doi.org/dx.doi.org URL prefix
stripped), so a fresh literature search, for example via OpenAlex, Scopus, or
any other source, can be checked against what the theory already engages
with. Returns the qualifying DOIs in their original form, deduplicated and
sorted by normalised form. Deterministic and takes no network dependency:
the search itself is left to whichever literature tool the caller prefers.
tf_new_evidence_dois(theory, candidate_dois)tf_new_evidence_dois(theory, candidate_dois)
theory |
A theory object (named list), e.g. from |
candidate_dois |
Character vector of DOIs to check. |
A character vector of the candidate DOIs not already cited, deduplicated and sorted.
# The bundled panic theory cites one DOI as evidence and one for each of its # two registered alternatives. All three count as already cited. theory <- tf_read(system.file("fixtures", "panic-network.theory.yaml", package = "theoryforge")) tf_new_evidence_dois(theory, c( "10.1016/j.brat.2015.10.002", # cited as evidence "https://doi.org/10.1016/0005-7967(86)90011-2", # an alternative, in URL form "https://doi.org/10.1037/0033-2909.99.1.20" # not yet cited ))# The bundled panic theory cites one DOI as evidence and one for each of its # two registered alternatives. All three count as already cited. theory <- tf_read(system.file("fixtures", "panic-network.theory.yaml", package = "theoryforge")) tf_new_evidence_dois(theory, c( "10.1016/j.brat.2015.10.002", # cited as evidence "https://doi.org/10.1016/0005-7967(86)90011-2", # an alternative, in URL form "https://doi.org/10.1037/0033-2909.99.1.20" # not yet cited ))
Builds (and optionally sends) a request to upload tf_dossier(theory) to
OSF storage. With dry_run = TRUE (the default) the planned request is
returned and nothing is sent. A live upload (dry_run = FALSE) requires
both token and node (the OSF project id) and performs an
authenticated PUT. The live path is network- and credential-dependent.
tf_osf_push( theory, token = NULL, node = NULL, filename = NULL, dry_run = TRUE, base_url = .tf_OSF_BASE )tf_osf_push( theory, token = NULL, node = NULL, filename = NULL, dry_run = TRUE, base_url = .tf_OSF_BASE )
theory |
A theory object (named list), e.g. from |
token |
OSF personal access token (required when |
node |
OSF project (node) id; used to build the upload URL and required
when |
filename |
Destination filename; defaults to |
dry_run |
When |
base_url |
OSF storage base URL; override to target a non-default host. |
When dry_run = TRUE, a list list(dry_run = TRUE,
request = list(method, url, filename, content_bytes), note). When
dry_run = FALSE, a list describing the completed upload.
theory <- tf_theory("demo-1", "A demonstration theory") tf_osf_push(theory) tf_osf_push(theory, node = "abc12")$request$urltheory <- tf_theory("demo-1", "A demonstration theory") tf_osf_push(theory) tf_osf_push(theory, node = "abc12")$request$url
Produces a deterministic preregistration markdown string for a theory and,
if path is given, writes it (LF, single trailing newline).
tf_preregister(theory, path = NULL)tf_preregister(theory, path = NULL)
theory |
A theory object (named list), e.g. from |
path |
Optional destination path; when given, the markdown is written with LF line endings. |
The preregistration markdown as a single string.
theory <- tf_theory("demo-1", "A demonstration theory") |> tf_add_prediction("h1", "Effect is exactly 0.30.", "point") cat(tf_preregister(theory))theory <- tf_theory("demo-1", "A demonstration theory") |> tf_add_prediction("h1", "Effect is exactly 0.30.", "point") cat(tf_preregister(theory))
Reads a theory object authored as YAML (or JSON, chosen by file extension) into a named list.
tf_read(path)tf_read(path)
path |
Path to a |
A named list holding the parsed theory object.
# Round-trip a theory through a temporary file. theory <- tf_theory("demo-1", "A demonstration theory") path <- tempfile(fileext = ".yaml") tf_write(theory, path) tf_read(path)# Round-trip a theory through a temporary file. theory <- tf_theory("demo-1", "A demonstration theory") path <- tempfile(fileext = ".yaml") tf_write(theory, path) tf_read(path)
Reads a corpus object ({schema_version, id, records}) into a named
list. The format is chosen by the file extension (.json -> JSON,
otherwise YAML).
tf_read_corpus(path)tf_read_corpus(path)
path |
Path to a |
A named list holding the parsed corpus object.
corpus <- list( schema_version = "1.0", id = "demo-corpus", records = list( list(id = "w1", keywords = list("arousal", "threat")), list(id = "w2", keywords = list("arousal", "threat")) ) ) path <- tempfile(fileext = ".json") jsonlite::write_json(corpus, path, auto_unbox = TRUE) tf_read_corpus(path)corpus <- list( schema_version = "1.0", id = "demo-corpus", records = list( list(id = "w1", keywords = list("arousal", "threat")), list(id = "w2", keywords = list("arousal", "threat")) ) ) path <- tempfile(fileext = ".json") jsonlite::write_json(corpus, path, auto_unbox = TRUE) tf_read_corpus(path)
Computes Jaccard similarity for every unordered pair of construct
definitions. Returns a data frame with one row per pair, sorted by
descending similarity then (a, b) ascending. The flag column
is "review" when similarity meets or exceeds the configured
redundancy_similarity_max threshold, otherwise "ok".
tf_redundancy_check(theory)tf_redundancy_check(theory)
theory |
A theory object (named list). |
A data frame with columns a, b, similarity,
flag.
Le, H., Schmidt, F. L., Harter, J. K., & Lauver, K. J. (2010). The problem of empirical redundancy of constructs. Organizational Behavior and Human Decision Processes, 112(2), 112-125. doi:10.1016/j.obhdp.2010.02.003
Lawson, K. M., & Robins, R. W. (2021). Sibling constructs. Personality and Social Psychology Review, 25(4), 344-366. doi:10.1177/10888683211047101
theory <- tf_theory("demo-1", "A demonstration theory") |> tf_add_construct("c_arousal", "Arousal", "Bodily activation in response to a stressor.") |> tf_add_construct("c_threat", "Perceived threat", "Appraised danger in response to a stressor.") tf_redundancy_check(theory)theory <- tf_theory("demo-1", "A demonstration theory") |> tf_add_construct("c_arousal", "Arousal", "Bodily activation in response to a stressor.") |> tf_add_construct("c_threat", "Perceived threat", "Appraised danger in response to a stressor.") tf_redundancy_check(theory)
Renders a digraph view of a theory without leaving R. Where tf_diagram()
returns the deterministic Graphviz DOT string, tf_render_diagram() passes
that string to the DiagrammeR engine and returns either an interactive
widget, which displays in the RStudio viewer and in R Markdown documents, or
a standalone SVG string, ready to embed in a page or save to a file.
tf_render_diagram(x, type = "nomological_net", as = c("widget", "svg"))tf_render_diagram(x, type = "nomological_net", as = c("widget", "svg"))
x |
A theory object (named list), or a diagram IR string from
|
type |
The diagram type, as in |
as |
Either |
The three chart views (venn, rigour and severity) are already SVG, so
they are returned as-is under as = "svg" and wrapped for display under
as = "widget". The causal_dag view emits dagitty syntax rather than DOT,
so it is not rendered here; paste it into a dagitty tool or the dagitty R
package instead.
An htmlwidget when as = "widget"; a single SVG string when
as = "svg".
tf_diagram() for the intermediate representation itself, which
needs no optional packages and stays byte-identical across the R and
Python implementations.
theory <- tf_theory("demo-1", "A demonstration theory") |> tf_add_construct("c_arousal", "Arousal", "Bodily activation.") |> tf_add_construct("c_threat", "Perceived threat", "Appraised danger.") |> tf_add_proposition("p1", "c_arousal", "c_threat", "causes") tf_render_diagram(theory, "nomological_net")theory <- tf_theory("demo-1", "A demonstration theory") |> tf_add_construct("c_arousal", "Arousal", "Bodily activation.") |> tf_add_construct("c_threat", "Perceived threat", "Appraised danger.") |> tf_add_proposition("p1", "c_arousal", "c_threat", "causes") tf_render_diagram(theory, "nomological_net")
Writes a standalone Quarto report to path (forced to a .qmd
suffix): a YAML header (title, format) followed by the
deterministic tf_dossier(theory) body. Returns the written path. When
render = TRUE, invokes quarto render, which requires a Quarto
installation, and stops if that render fails.
tf_render_report(theory, path, title = NULL, render = FALSE, to = "html")tf_render_report(theory, path, title = NULL, render = FALSE, to = "html")
theory |
A theory object (named list), e.g. from |
path |
Destination path; any extension is replaced with |
title |
Optional report title; defaults to
|
render |
When |
to |
Quarto output format (default |
The path of the written .qmd file.
theory <- tf_theory("demo-1", "A demonstration theory") |> tf_add_construct("c_arousal", "Arousal", "Bodily activation.") path <- tempfile(fileext = ".qmd") tf_render_report(theory, path)theory <- tf_theory("demo-1", "A demonstration theory") |> tf_add_construct("c_arousal", "Arousal", "Bodily activation.") path <- tempfile(fileext = ".qmd") tf_render_report(theory, path)
Renders the result of tf_check() as a string. format = "json"
returns valid, pretty-printed JSON; format = "html" returns an HTML
fragment.
tf_report(theory, format = "json")tf_report(theory, format = "json")
theory |
A theory object (named list). |
format |
One of |
A single string.
theory <- tf_theory("demo-1", "A demonstration theory") |> tf_add_construct("c_arousal", "Arousal", "Bodily activation.") |> tf_add_prediction("h1", "Arousal precedes threat appraisal.", "point") cat(tf_report(theory, format = "json"))theory <- tf_theory("demo-1", "A demonstration theory") |> tf_add_construct("c_arousal", "Arousal", "Bodily activation.") |> tf_add_prediction("h1", "Arousal precedes threat appraisal.", "point") cat(tf_report(theory, format = "json"))
Set the formal model (BUILDING mode)
tf_set_formal_model(theory, type, spec_ref = NULL)tf_set_formal_model(theory, type, spec_ref = NULL)
theory |
A theory object (named list). |
type |
Formal-model type (e.g. |
spec_ref |
Optional reference to the model specification. |
The (mutated) theory object.
tf_theory("demo-1", "A demonstration theory") |> tf_set_formal_model("ode", spec_ref = "models/panic.ode")tf_theory("demo-1", "A demonstration theory") |> tf_set_formal_model("ode", spec_ref = "models/panic.ode")
Computes, for each prediction (in file order), the riskiness of the claim form and the discounted/bonus-adjusted severity.
tf_severity(theory)tf_severity(theory)
theory |
A theory object (named list), e.g. from |
A data.frame with columns prediction_id, type,
risk_score, computed_severity, one row per prediction in
file order.
Mayo, D. G. (2018). Statistical inference as severe testing. Cambridge University Press. doi:10.1017/9781107286184
Meehl, P. E. (1990). Why summaries of research on psychological theories are often uninterpretable. Psychological Reports, 66, 195-244. doi:10.2466/pr0.1990.66.1.195
theory <- tf_theory("demo-1", "A demonstration theory") |> tf_add_prediction("h1", "Effect is exactly 0.30.", "point") |> tf_add_prediction("h2", "Effect is positive.", "directional") tf_severity(theory)theory <- tf_theory("demo-1", "A demonstration theory") |> tf_add_prediction("h1", "Effect is exactly 0.30.", "point") |> tf_add_prediction("h2", "Effect is positive.", "directional") tf_severity(theory)
Treats each construct (in file order) as a state variable and each directed
proposition as a signed linear coupling term, then integrates
dX/dt = A X - damping * X with fixed-step (Euler) updates. The result
is fully deterministic. Construct ids must be unique; duplicates are refused
rather than resolved to an arbitrary state slot.
tf_simulate(theory, steps = 10, dt = 0.1, k = 1, damping = 0.5, init = 1)tf_simulate(theory, steps = 10, dt = 0.1, k = 1, damping = 0.5, init = 1)
theory |
A theory object (named list), e.g. from |
steps |
Number of Euler steps (default |
dt |
Integration step size (default |
k |
Coupling gain applied to each signed edge (default |
damping |
Per-state linear decay (default |
init |
Initial value for every state (default |
A named list
list(states, dt, steps, k, damping, init, trajectory), where
states are the construct ids in file order and trajectory is a
list of steps + 1 numeric vectors (row 0 = initial state), every
value rounded to 6 decimals. All five knobs are echoed back, because the
trajectory cannot be reproduced without them.
theory <- tf_theory("demo-1", "A demonstration theory") |> tf_add_construct("c_arousal", "Arousal", "Bodily activation.") |> tf_add_construct("c_threat", "Perceived threat", "Appraised danger.") |> tf_add_proposition("p1", "c_arousal", "c_threat", "increases") sim <- tf_simulate(theory, steps = 5) sim$states sim$trajectory[[1]] # the common initial state sim$trajectory[[length(sim$trajectory)]] # after five Euler stepstheory <- tf_theory("demo-1", "A demonstration theory") |> tf_add_construct("c_arousal", "Arousal", "Bodily activation.") |> tf_add_construct("c_threat", "Perceived threat", "Appraised danger.") |> tf_add_proposition("p1", "c_arousal", "c_threat", "increases") sim <- tf_simulate(theory, steps = 5) sim$states sim$trajectory[[1]] # the common initial state sim$trajectory[[length(sim$trajectory)]] # after five Euler steps
Seeds schema_version = "1.0" and a first provenance entry
{step:"1", action:"tf_theory", detail:<id>}.
tf_theory(id, title, maturity = "building", theory_form = "network")tf_theory(id, title, maturity = "building", theory_form = "network")
id |
Theory id. |
title |
Human-readable title. |
maturity |
Maturity stage (default |
theory_form |
Theory form (default |
A theory object (named list).
tf_theory("demo-1", "A demonstration theory")tf_theory("demo-1", "A demonstration theory")
Lowercases, replaces every run of non-[a-z0-9] characters with a
single space, splits, drops tokens shorter than 3 characters and the
canonical stopwords, then returns the unique set.
tf_tokens(s)tf_tokens(s)
s |
A single string (or |
A character vector of unique tokens (possibly empty).
tf_tokens("The physiological arousal response to a threat")tf_tokens("The physiological arousal response to a threat")
Built-in validation. The default
(full = FALSE) checks required fields and enum membership. With
full = TRUE it additionally checks referential integrity: that every id
is unique within its collection and that every cross-reference (proposition
endpoints, prediction derivations and diagnostics, and assumption, evidence and
test-outcome targets) points to a declared id, and that every prediction
severity is a number within [0, 1]. The full checks are
deterministic.
tf_validate(theory, full = FALSE)tf_validate(theory, full = FALSE)
theory |
A theory object (named list), e.g. from |
full |
When |
TRUE (invisibly) on success; otherwise stops with a message
listing every problem found.
theory <- tf_read(system.file("fixtures", "panic-network.theory.yaml", package = "theoryforge")) isTRUE(tf_validate(theory)) # required fields and enums isTRUE(tf_validate(theory, full = TRUE)) # also ids and cross-references # The failure path is the more informative one. Point a prediction at a # proposition that was never declared. broken <- theory broken$predictions[[1]]$derives_from <- "p_missing" tryCatch(tf_validate(broken, full = TRUE), error = conditionMessage)theory <- tf_read(system.file("fixtures", "panic-network.theory.yaml", package = "theoryforge")) isTRUE(tf_validate(theory)) # required fields and enums isTRUE(tf_validate(theory, full = TRUE)) # also ids and cross-references # The failure path is the more informative one. Point a prediction at a # proposition that was never declared. broken <- theory broken$predictions[[1]]$derives_from <- "p_missing" tryCatch(tf_validate(broken, full = TRUE), error = conditionMessage)
Serialises a theory object to disk. The format is chosen by the file
extension (.json -> JSON, otherwise YAML). Files are written with LF
line endings.
tf_write(theory, path)tf_write(theory, path)
theory |
A theory object (named list). |
path |
Destination path. |
The path (invisibly).
theory <- tf_theory("demo-1", "A demonstration theory") tf_write(theory, tempfile(fileext = ".yaml"))theory <- tf_theory("demo-1", "A demonstration theory") tf_write(theory, tempfile(fileext = ".yaml"))