GraphTopic
Discover topics by partitioning a sparse semantic document graph.
GraphTopic encodes documents (or accepts your embeddings), retrieves approximate candidate neighbors, recomputes cosine weights, builds a union-max graph, partitions it with Leiden, and explains the resulting communities with class-based TF-IDF.
Version 0.1.1 is the official implementation of the current GraphTopic paper protocol.
The earlier 0.0.2 implementation remains available under the Git tag legacy-v0.0.2.
Installation
GraphTopic requires Python 3.11 or newer and is tested on Python 3.11–3.14.
Install the library from PyPI:
python -m pip install graphtopic
The core installation accepts precomputed embeddings. To also encode documents with Sentence Transformers:
python -m pip install "graphtopic[embedding]"
To install from a source checkout:
python -m venv .venv
# Activate the environment using your shell's activation command.
python -m pip install .
For development:
python -m pip install -e ".[dev]"
Quick start: precomputed embeddings, no model download
This complete offline example uses illustrative two-dimensional embeddings. For real data, pass one semantic embedding row per document in exactly the same order.
import numpy as np
from sklearn.feature_extraction.text import CountVectorizer
from graphtopic import CTFIDFRepresentation, ExactNeighborSearch, GraphTopic
documents = [
"apple fruit red",
"apple fruit green",
"football goal team",
"football goal match",
]
embeddings = np.array(
[
[1.0, 0.05],
[1.0, 0.10],
[0.05, 1.0],
[0.10, 1.0],
]
)
model = GraphTopic(
embedding_model=None,
neighbor_search=ExactNeighborSearch(n_candidates=3),
n_neighbors=1,
representation_model=CTFIDFRepresentation(CountVectorizer()),
)
topics = model.fit_transform(documents, embeddings=embeddings)
print(topics)
print(model.get_topic_info())
print(model.get_document_info())
print(model.graph_.diagnostics)
The small example explicitly changes search, degree and lexical thresholds.
Canonical defaults are NNDescent with 50 non-self candidates and 20 retained
neighbors. Leiden uses two optimization iterations, matching the frozen paper
experiments; pass a custom LeidenDetector(n_iterations=-1) to optimize until no
further improvement. Tiny corpora clamp neighborhood budgets with warnings. The default English
vectorizer uses min_df=5, max_df=.95 and at most 20,000 features; use the example's
vectorizer for tiny corpora or configure it for your language.
Encode documents with a model
After installing the embedding extra:
from graphtopic import GraphTopic
model = GraphTopic(
embedding_model="sentence-transformers/all-MiniLM-L6-v2",
resolution=1.0,
random_state=42,
)
# documents is your ordered sequence of text strings.
topics = model.fit_transform(documents)
The encoder loads only when needed and may download model files on first use.
You can pass any object with encode(documents), including a configured Sentence
Transformer. To control device and batch size, use SentenceTransformerEmbedding.
Supplying embeddings=... always bypasses the encoder, even if one is configured.
Save and reuse your own embedding arrays to avoid recomputation. Inputs are copied
for normalization; your array is not modified.
Inspect and reuse
words = model.get_topics() # topic -> [(word, score), ...]
one_topic = model.get_topic(0)
graph = model.get_graph() # defensive graph copy
adjacency = graph.adjacency # writable SciPy CSR copy
result = model.result_ # snapshot of this fit
path = model.resolution_path([0.1, 0.5, 1.0, 2.0])
print(path.get_summary())
The path reuses the graph and embeddings without running search again. It does not change the model's original result and is not a nested topic hierarchy. A higher resolution generally favors finer partitions, but no universal optimum or monotonic topic-count guarantee is supplied.
model.update_topics(
representation_model=CTFIDFRepresentation(
CountVectorizer(ngram_range=(1, 2), min_df=1),
top_n_words=5,
)
)
This changes only lexical descriptions. It does not rebuild or repartition the graph.
CTFIDFRepresentation also accepts a replaceable ctfidf_model. GraphTopic's
default ClassTfidfTransformer uses SciPy and scikit-learn sparse operations and
implements the formula in the paper without requiring BERTopic as a dependency.
Replace pipeline components
from graphtopic import LeidenDetector, NNDescentSearch, UnionMaxGraph
model = GraphTopic(
embedding_model=my_encoder,
neighbor_search=NNDescentSearch(n_candidates=100, random_state=7, n_jobs=1),
graph_model=UnionMaxGraph(n_neighbors=30),
community_model=LeidenDetector(resolution=0.3, random_state=7),
representation_model=my_representation,
)
Custom classes need only implement the methods in the component guide; inheritance is optional. Supplied components own their settings. Facade scalars configure only default components. Replacing a component can change the method's scientific behavior.
What the model returns
fit()returns the fitted model.fit_transform()returns a list of integer topic assignments.- Every document belongs to exactly one topic, including isolated vertices.
- Word scores are lexical weights, not membership probabilities.
- There is no noise topic, unseen-document
transform(), automatic resolution selection, or probabilistic topic mixture in this version.
The method follows the paper, but resolution=1.0 and seed=42 are software starting defaults, not promises to reproduce its result tables. See the API contract for numerical, graph, state and boundary rules.
Development and validation
python -m ruff check .
python -m ruff format --check .
python -m pytest --cov=graphtopic --cov-report=term-missing
python -m build
python -m twine check dist/*
The first ANN run may take longer due to Numba compilation. Tests use synthetic inputs without downloading embedding models; real ANN and Leiden integration tests run by default. The optional encoder adapter is tested with a lightweight fake model.
See CONTRIBUTING, release checks, validation evidence, and examples. The paper suite is self-bootstrapping and resumable: it downloads pinned public data and model revisions, creates and verifies local embeddings, and runs against a locked numerical environment. Previous uncontrolled partial outputs are not treated as reference results. See paper experiments for the exact setup, scope of reproducibility, and execution command.
License
MIT. See LICENSE.
Metadata
Release files for graphtopic 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| graphtopic-0.1.1.tar.gz | 83.9 kB | Without provenance | Details for graphtopic-0.1.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| graphtopic-0.1.1-py3-none-any.whl | Python 3 | none | any | Details for graphtopic-0.1.1-py3-none-any.whl |
Total release size: 106.2 kB
Release files / graphtopic-0.1.1.tar.gz
| Download URL | graphtopic-0.1.1.tar.gz |
|---|---|
| Size | 83.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1d39d282a0a39081d2537be04c01caa3c1d2400aad2316af91ae2f8507a3e2d5
|
|
BLAKE2b-256 checksum How to use checksums |
027cdcfc4031c145c43f4707db7581e2c954e582b40bc10d07d76607a6451ecf
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.6
|
Release files / graphtopic-0.1.1-py3-none-any.whl
| Download URL | graphtopic-0.1.1-py3-none-any.whl |
|---|---|
| Size | 22.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
222dd80be9ebc2c94c3e31b0c3646f3bb9b0c24e368818289253707e89991704
|
|
BLAKE2b-256 checksum How to use checksums |
df38b8e375bd2886f409bec30a03335597de5a810e562edf044ea5e8e0fd622c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.6
|
Security for graphtopic 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.