*! version 1.0 17may2026
help litdiscover

Title

litdiscover -- LDA topic modelling with deductive
construct extraction for systematic literature reviews (SLRs)

Syntax

litdiscover, abstract(varname)
[options]

Required
abstract(varname) string variable containing the abstract text

Identification and stratification
id(varname) study identifier; defaults to study_id if it exists, otherwise auto-generated

year(varname) publication year; enables year-stratified topic table

Construct fields (multi-valued, separator-split)
theory(varname) theoretical perspective(s)

dv(varname) dependent variable(s)

iv(varname) independent variable(s) / antecedent(s)

mod(varname) moderator(s)

med(varname) mediator(s)

decision(varname) decision(s)

context(varname) empirical context(s)

method(varname) method(s)

Construct fields (single-valued)
journal(varname) journal name; rejected if it contains the separator

TCCM framework options
tccmclass(name) field label for the TCCM characteristic axis;
one of dv, iv, mod, med, decision, journal;
default is dv when dv() is supplied

tccmminfreq(#) minimum cell frequency to retain in the TCCM table; default 1

Tokenisation and engine options
sep(string) separator for multi-valued cells; default ";"

topics(#) number of LDA topics; default 5

seeds(#) number of random seeds for stability assessment; default 1

coherence compute UMass topic coherence

seed(#) primary random seed; default 12345

minfreq(#) minimum document frequency for a term; default 1

maxdf(#) maximum document frequency, in (0, 1]; default 1.0

ngram(#) maximum n-gram length, in [1, 3]; default 1

File handling
script(string) path to litdiscover.py; if omitted, located via findfile

export(string) flat output directory; default "output"; mutually exclusive with outdir()

outdir(string) root output directory with the subdirectory layout ./root/tables/, ./root/figures/, ./root/interactive/; mutually exclusive with export()

keeptemp retain the intermediate corpus CSV

Visualisation
figures produce static figures (Stata-tier via heatplot/palettes; Python-tier via matplotlib/seaborn/wordcloud)

interactive produce three interactive HTML deliverables (pyLDAvis, pyvis network, plotly Sankey)

sankeytopfreq(#) truncate theory nodes in the Sankey diagram to the top # by total documents; default 15

vizscript(string) path to litdiscover_viz.py; if omitted, located via findfile

Network and exclusivity measures
netmeasures compute network-analytic measures (degree centrality, betweenness centrality, modularity, Louvain communities) over the within-field and cross-field construct co-occurrence tables; requires at least one construct field

frex add a frex column to litdiscover_topicterms.dta containing the FREX (FRequency-EXclusivity) score per Roberts, Stewart, and Tingley (2019)

netscript(string) path to litdiscover_net.py; if omitted, located via findfile

Interactive behaviour
noautoload suppress the default of loading litdiscover_topicterms.dta into memory at the end of the run; preserves the input dataset in memory instead

Description

litdiscover implements the analytic phase of a systematic literature
review. It combines an inductive component (latent Dirichlet allocation (LDA),
Blei et al. 2003) applied to abstract
text with a deductive component (researcher-coded construct fields) and
emits a set of crosswalk tables that align the two. The package also
produces summary tables aligned with the TCCM and ADO frameworks of
Paul et al. (2024).

The LDA engine is implemented in Python (via the python script
command) using scikit-learn. All construct extraction, tokenisation,
and crosswalk computation are performed in Stata. Multi-valued construct
cells are split on a user-specified separator (default ";");
single-valued cells (currently journal()) are validated to contain
no separator.

The deductive-inductive bridge is exposed primarily through
litdiscover_topic_by_field.dta, which gives the mean topic share of
each topic within each (field, value) cell, along with the number of
documents contributing to that cell. The TCCM and ADO tables provide
complementary framework-oriented views aligned with current best practice
in business-school literature reviews.

The figures and interactive flags produce publication-ready visualisations: Stata-native graphs (.gph and
.png) via heatplot (Jann 2018;
Jann 2023), Python-side static figures
(.png), and three interactive HTML files for online supplementary
materials and project websites
(Sievert and Shirley 2014;
Donthu et al. 2021).

Interactive behaviour (v1.0)

For convenience at the Stata prompt, litdiscover loads
litdiscover_topicterms.dta into memory at the end of the run by
default. The user can then immediately run list, tabulate,
graph, tabstat, and similar commands on the top-term
table without first issuing a use command. The original input
dataset is saved to _litdiscover_input_recovery.dta in the tables
directory so the user can return to it with a single use.

The end-of-run summary printed to the Results window lists every output
file produced, with copy-paste-ready use commands for the
doctopic, coherence, topic_by_field, and other tables.

For scripted workflows that need the input dataset to remain in memory,
the noautoload option suppresses the default loading.

Output layout

The package supports two output layouts.

Flat layout (default

When outdir() is omitted, all outputs (tables, figures, and HTML
files) are written to a single flat directory named by export().
export() defaults to "output".

Subdirectory layout

When outdir(root) is supplied, the package creates
./root/tables/, ./root/figures/, and
./root/interactive/, routing each kind of output to the
appropriate subdirectory. outdir() and export() are
mutually exclusive; supplying both raises an error.

Output files

Engine outputs are produced for every call. Construct, framework, and
stratification outputs are produced only when the corresponding options
are supplied. Figure and HTML outputs are produced only when the
corresponding flags are supplied.

Engine outputs (always produced)

litdiscover_doctopic.dta

Columns: study_id, topic_1, ..., topic_K,
dominant_topic, dominant_topic_share. One row per processed
document.

litdiscover_topicterms.dta

Columns: topic, rank, term, weight. Top terms per topic
under the primary seed. When frex is supplied, an additional column
frex contains the FREX score per
Roberts, Stewart, and Tingley (2019),
balancing within-topic frequency and across-topic exclusivity (omega = 0.5,
the stm default).

litdiscover_stability.dta (only when seeds() > 1)

Columns: seed_a, seed_b, mean_jaccard, min_jaccard,
n_topics. Pairwise Jaccard overlap of top-term sets across seeds,
matched via the Hungarian algorithm.

litdiscover_topic_stability.dta (only when seeds() > 1; v0.3.1)

Columns: topic, mean_best_match, min_best_match,
n_seed_pairs. For each topic in the primary-seed run, the mean and
minimum Jaccard similarity of its best-matched counterpart across the
other seeds. Use together with litdiscover_coherence.dta for a
combined per-topic quality assessment (high coherence and high
stability indicate a robust topic).

litdiscover_coherence.dta (only with coherence)

Columns: topic, umass, topn. UMass coherence score per
topic (Mimno et al. 2011).

Construct outputs (produced when any construct field is supplied)

litdiscover_construct_freq.dta

Columns: field, value, n_docs. Frequency of each unique
(field, value) pair across documents.

litdiscover_cooc_within.dta

Columns: field, value_a, value_b, n_both, n_a,
n_b, jaccard. Within-field co-occurrence of unordered value
pairs, with marginal counts and Jaccard similarity.

litdiscover_cooc_cross.dta

Columns: field_a, value_a, field_b, value_b,
n_both, n_a, n_b, jaccard. Cross-field co-occurrence
of value pairs with field_a < field_b lexically.

litdiscover_topic_by_field.dta

Columns: field, value, topic, mean_share, n_docs.
Mean LDA topic share within each (field, value) cell.

Stratification output

litdiscover_topic_by_year.dta (only with year())

Columns: year, topic, mean_share, n_docs.

Network-analytic outputs (only with netmeasures)

litdiscover_network_measures.dta

One row per (field, value). Columns: field, value, n_nodes,
n_edges, degree, strength, betweenness, community,
modularity. Per-field undirected weighted graphs with edge weight equal
to the Jaccard similarity from litdiscover_cooc_within.dta. Betweenness
uses 1 - jaccard as the distance form. Louvain community indices are
0-based within each network and are not comparable across networks.

litdiscover_network_measures_cross.dta

One row per (field_a, field_b, field, value). Columns: field_a,
field_b, field, value, n_nodes, n_edges,
degree, strength, betweenness, community,
modularity. Per-pair bipartite undirected weighted graphs from
litdiscover_cooc_cross.dta, with node identifier internally namespaced
as field::value to keep field domains disjoint.

Framework outputs

litdiscover_tccm.dta (produced when theory(), context(),
method(), and the TCCM characteristic field are all supplied)

Columns: theory, context, method,
tccm_class_field, n.

litdiscover_ado.dta (produced when any of iv(),
decision(), dv() is supplied)

Columns: study_id, ado_class, share_within_class.

Static figures (only with figures)

All figures are written at 300 DPI in PNG format. Stata-tier figures
are also saved as Stata-native .gph.

Stata-tier (via heatplot and palettes):

- litdiscover_fig_constructfreq_field. one per supplied field, top 15 values

- litdiscover_fig_topicyear. multi-line plot, when year() is set

- litdiscover_fig_ado. ADO class frequency, when ADO output exists

- litdiscover_fig_tccm. theory by characteristic heatmap, when TCCM output exists

- litdiscover_fig_topicheatmap_field. one per supplied field, top 15 values

Python-tier (via matplotlib, seaborn, wordcloud):

- litdiscover_fig_coherence.png per-topic UMass bar chart, when coherence was set

- litdiscover_fig_topicterms.png small multiples of top-10 terms per topic

- litdiscover_fig_wordcloud_tk.png one wordcloud per topic

Interactive HTML deliverables (only with interactive)

- litdiscover_topicvis.html pyLDAvis topic explorer
(Sievert and Shirley 2014)

- litdiscover_network.html pyvis force-directed graph of within-field
construct co-occurrences (edges with Jaccard >= 0.1; nodes coloured by
field, sized by log-document-count)

- litdiscover_sankey.html plotly Sankey diagram of the theory-to-topic
flow. Theory nodes are truncated to the top sankeytopfreq(#) by
total documents (default 15). Topic labels include the top three terms.

Returned scalars and macros

litdiscover is rclass. The following are returned in every call:

r(N_input) observations passed to the engine after if/in

r(N_docs) documents retained after Python text cleaning

r(k_topics) number of LDA topics fitted

r(n_seeds) number of seeds used for stability

r(coherence) 1 if UMass coherence was computed, 0 otherwise

r(fields_supplied) space-separated list of construct fields supplied

r(export) flat-mode export directory; empty under outdir()

r(outdir) outdir() root directory; empty in flat mode

r(tables_dir) directory where .dta outputs were written

r(figures_dir) directory where figure files were written

r(interactive_dir) directory where HTML files were written

r(figures_n) number of figure files produced; 0 if figures not set

r(interactive_n) number of HTML files produced; 0 if interactive not set

r(figures_list) space-separated basenames of figure files produced

r(interactive_list) space-separated basenames of HTML files produced

Conditional returns:

r(years_min) / r(years_max) only when year() is supplied

r(N_field) distinct values for each supplied construct field

r(tccm_cells) / r(tccm_minfreq) only when TCCM is emitted

r(tccm_class_field) field label used as the TCCM characteristic

r(ado_a) / r(ado_d) / r(ado_o) row counts per ADO class

r(net_networks_within) / r(net_networks_cross) counts of networks built; only with netmeasures

r(net_nodes_within) / r(net_nodes_cross) row counts of the two network-measures files

r(net_modularity_mean) / r(net_modularity_min) / r(net_modularity_max) summary statistics of within-field network modularity

r(net_louvain_seed) seed used for Louvain community detection

r(network_measures_file) / r(network_measures_cross_file) absolute paths to the two output files

r(topic_stability_file) absolute path to litdiscover_topic_stability.dta (only when seeds() > 1)

r(topicterms_file) absolute path to litdiscover_topicterms.dta (the table autoloaded into memory by default)

r(input_recovery_file) absolute path to the saved copy of the input dataset; use with use to restore the input data

r(frex_omega) / r(frex_epsilon) FREX parameters; only with frex

r(frex_topics) / r(frex_vocab_size) counts used in the FREX ECDFs

Examples

Minimal call (engine outputs only)

. litdiscover, abstract(abstract)

Single construct field

. litdiscover, abstract(abstract) theory(theory) topics(8)

Full call with all construct fields, year stratification, and the subdirectory layout

. litdiscover, abstract(abstract) id(study_id) year(year) theory(theory) dv(dv) iv(iv)

mod(mod) med(med) decision(decision) journal(journal) context(context) method(method)

topics(8) seeds(5) coherence outdir(review_2026)

With static figures and interactive HTMLs in the subdirectory layout

. litdiscover, abstract(abstract) theory(theory) dv(dv) iv(iv) context(context) method(method)

year(year) coherence outdir(review_2026) figures interactive

TCCM with a non-default characteristic axis

. litdiscover, abstract(abstract) theory(theory) context(context) method(method) iv(iv)

tccmclass(iv) tccmminfreq(2)

Network-analytic measures and FREX

. litdiscover, abstract(abstract) theory(theory) dv(dv) iv(iv) context(context) method(method)

topics(8) netmeasures frex

Requirements

Stata 19 or later, with Python 3 configured for use via python.

The base Python script litdiscover.py requires pandas,
numpy, scikit-learn, and scipy.

figures additionally requires SSC packages heatplot,
palettes, and colrspace:

. ssc install heatplot, replace

. ssc install palettes, replace

. ssc install colrspace, replace

figures and interactive together additionally require the
Python packages matplotlib, seaborn, wordcloud,
pyLDAvis, pyvis, plotly, and networkx:

. pip install matplotlib seaborn wordcloud pyldavis pyvis plotly networkx

netmeasures additionally requires the Python package
networkx (also pulled in by interactive):

. pip install networkx

References

Blei, D. M., Ng, A. Y., and Jordan, M. I. 2003. Latent Dirichlet
allocation. Journal of Machine Learning Research 3: 993-1022.

Donthu, N., Kumar, S., Mukherjee, D., Pandey, N., and Lim, W. M. 2021.
How to conduct a bibliometric analysis: An overview and guidelines.
Journal of Business Research 133: 285-296.
https://doi.org/10.1016/j.jbusres.2021.04.070

Greene, D., O'Callaghan, D., and Cunningham, P. 2014. How many topics?
Stability analysis for topic models. In Machine Learning and Knowledge Discovery in Databases (ECML PKDD 2014),
Lecture Notes in Computer Science 8724, 498-513. Springer.
https://doi.org/10.1007/978-3-662-44848-9_32

Jann, B. 2018. Color palettes for Stata graphics. The Stata Journal
18(4): 765-785. https://doi.org/10.1177/1536867X1801800402

Jann, B. 2023. Color palettes for Stata graphics: An update.
The Stata Journal 23(2): 336-385.
https://doi.org/10.1177/1536867X231175264

Mimno, D., Wallach, H. M., Talley, E., Leenders, M., and McCallum, A.
2011. Optimizing semantic coherence in topic models. In
Proceedings of the 2011 Conference on Empirical Methods in Natural Language Processing, 262-272.

Paul, J., Khatri, P. and Duggal, H. K. 2024. Frameworks for developing
impactful systematic literature reviews and theory building: What, Why
and How?
Journal of Decision Systems 33(4): 537–550.
https://doi.org/10.1080/12460125.2023.2197700

Roberts, M. E., Stewart, B. M., and Tingley, D. 2019. stm: An R package
for structural topic models. Journal of Statistical Software
91(2): 1-40. https://doi.org/10.18637/jss.v091.i02

Sievert, C., and Shirley, K. 2014. LDAvis: A method for visualizing and
interpreting topics. In Proceedings of the Workshop on Interactive
Language Learning, Visualization, and Interfaces, 63-70.
Association for Computational Linguistics.
https://aclanthology.org/W14-3110/

Aliases

litdi is provided as a short-form alias for litdiscover. It
forwards every argument and propagates returned scalars and macros.

Author

Nebojsa S. Davcik

EM Normandie Business School, Oxford, UK

ORCID: 0000-0003-1041-8788

https://orcid.org/0000-0003-1041-8788

Email: davcik@live.com

Citation

When citing litdiscover in academic work, please use:

Davcik, N. S. 2026. litdiscover: A Stata package for theory-aware literature review, analysis, and discovery.
Available at: https://github.com/Davcik/litdiscover

Licence

litdiscover is free software released under the
GNU General Public License version 3 or later (GPL-3.0-or-later).
You may redistribute and modify it under the terms of that licence;
modified versions and larger works that incorporate litdiscover
must also be released under GPL-3 or later. See the LICENCE file in
the repository root for the full licence text.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Also see

Short-form alias: litdi