Skip to main content

Drop-in TF-IDF + rhetorical-fingerprint + embedding analysis for any research corpus, with a static-HTML dashboard.

Project description

corpus-lens

Drop-in TF-IDF + rhetorical-fingerprint + embedding analysis for any research corpus, with a static-HTML dashboard.

You have a folder of papers, blog posts, working notes, lecture transcripts, or any other writing. You want to know:

  • What is each paper actually about? (keywords + bigrams)
  • What is its rhetorical posture? (hedging / assertion / empirical / theoretical / critical / quantitative density)
  • Which papers are similar to which? (cosine on TF-IDF, top-K)
  • Where does each paper sit in concept space? (UMAP / PaCMAP / PCA)
  • How does it compare to public baselines? (arxiv categories, specific researchers)
  • What clusters emerge? (force-directed similarity network)

corpus-lens gives you all of that from a single command, with a self-contained HTML dashboard you can open in any browser, host as a static site, or embed in your existing docs.

Zero hard dependencies for the core analysis (stdlib only). Embeddings need numpy + sklearn + optionally umap-learn / pacmap. No vector DB, no Docker, no LLM API calls, no telemetry.

Quickstart

pip install corpus-lens[all]

# Discover what's in your input directory + emit an editable manifest
corpus-lens prepare ./my-papers --output ./.lens

# Run the analysis (TF-IDF, fingerprint, similarity, keyword index)
corpus-lens analyze ./my-papers --output ./.lens

# Add 2D embeddings (UMAP / PaCMAP / PCA)
corpus-lens embed --output ./.lens

# Add external comparison baselines from arxiv
corpus-lens compare --output ./.lens \
    --against arxiv:cs.LG --against arxiv-author:Friston

# Build the dashboard and open it
corpus-lens dashboard --output ./.lens --title "My Lab" --serve

That's it. The dashboard at http://127.0.0.1:8765/dashboard.html will show:

  • Per-paper view: top-20 TF-IDF keywords, distinctive vocab, bigrams, rhetorical fingerprint, top-5 most-similar papers
  • Map: UMAP / PaCMAP / PCA scatter, coloured by programme / maturity / year
  • Network: force-directed similarity graph
  • Timeline: lane chart of papers over time, per programme
  • Heatmap: programme × rhetorical-dimension grid
  • Plots: configurable scatter, programme bars, length histogram
  • Compare: side-by-side with arxiv baselines
  • Search by term: full inverted index
  • Leaderboard: sort papers by any metric

Input formats

corpus-lens accepts your corpus in three forms.

1. Directory of files (most common)

Drop .md, .html, .htm, .txt, or .pdf files into a directory. corpus-lens prepare will discover them, extract titles / dates / metadata, and emit a manifest you can edit. PDFs require pip install corpus-lens[pdf].

2. Frontmatter-annotated files

Add YAML-ish frontmatter to your markdown / HTML to override extracted metadata:

---
title: My Paper Title
date: 2026-06-18
programme: Statistical Mechanics
maturity: preprint
tags: koopman, perturbation, spectral theory
---

# My Paper Title

The rest of the content...

3. JSON manifest

For non-file-based corpora (database dumps, API exports), write a manifest directly:

{
  "items": [
    {
      "id": "paper-001",
      "title": "Whatever",
      "date": "2026-06-18",
      "programme": "Some Programme",
      "maturity": "preprint",
      "tags": ["tag1", "tag2"],
      "text": "The full plain-text body of the paper..."
    }
  ]
}

Then corpus-lens analyze --manifest ./manifest.json --output ./.lens.

See docs/INPUT_FORMATS.md for full schema details.

What the rhetorical fingerprint measures

Each paper gets a six-axis fingerprint in markers per 1000 tokens:

Axis What it counts Why it matters
hedging "may", "might", "could", "perhaps", "suggests", ... Epistemic caution, openness
assertion "proves", "establishes", "demonstrates", "definitively", ... Confidence, finality
empirical "experiment", "dataset", "measure", "baseline", ... Data-driven posture
theoretical "theorem", "lemma", "proof", "axiom", ... Formal-derivation posture
critical "refutes", "fails", "mistaken", "naive", ... Argumentative / refutational tone
quantitative numbers with units (10ms, 95%, 3.2kHz) Numeric specificity

The lexicons are tunable; pass --lexicons to override or extend via the Python API. See docs/INTERPRETATION.md for guidance on reading the fingerprint.

Python API

The CLI is a thin wrapper. The full API is small and importable:

from corpuslens import extract, analyze, embed, compare, dashboard

items = extract.from_directory("./my-papers")
result = analyze.run(items)
embeddings = embed.run(result, methods=["pca", "umap", "pacmap"])
baselines = compare.run(["arxiv:cs.LG", "arxiv-author:Friston"], our_analysis=result)
dashboard.build("./.lens", title="My Lab")

Each function takes plain dicts/lists and returns plain dicts/lists. No frameworks, no inheritance, no DI.

Preparation guide

To get the most out of corpus-lens, spend ten minutes on preparation. See docs/PREPARATION.md for the full guide. The short version:

  1. One file per paper. Don't pre-concatenate. The unit of analysis is the paper.
  2. Use frontmatter. Title and date in YAML at the top of every file, even if the extractor would auto-detect them.
  3. Pick a programme label. A 1-3 word category for each paper. Loose groupings are fine; this is just colour-coding in the dashboard.
  4. Strip boilerplate. Page headers, footers, navigation, references sections, and author bios skew the keyword analysis. The HTML extractor drops these automatically; for markdown, put boilerplate in fenced blocks (\```) so it's excluded.
  5. Decide on maturity tags. draft, preprint, published, working are useful axes for the dashboard's colour-by-maturity view.

Comparison baselines

corpus-lens compare pulls abstracts from arxiv and applies the same fingerprint so you can position your corpus against published baselines.

corpus-lens compare --against arxiv:cs.LG          # ML
corpus-lens compare --against arxiv:math.PR        # probability
corpus-lens compare --against arxiv:q-bio.NC       # neuroscience
corpus-lens compare --against arxiv:cond-mat.stat-mech  # stat mech
corpus-lens compare --against arxiv-author:Friston
corpus-lens compare --against arxiv-author:Bengio
corpus-lens compare --against arxiv-query:cat:math.DS+AND+abs:Koopman

Each baseline becomes a row in the dashboard's Compare tab. Hover for the per-1k-token density of each rhetorical axis.

Output files

After analyze + embed + compare, your .lens/ directory contains:

.lens/
  manifest.json             # editable input description
  corpus_analysis.json      # per-paper meta + keywords + fingerprint
  corpus_keyword_index.json # inverted index: term -> [{paper, tf}]
  corpus_similarity.json    # paper -> top-K most-similar
  corpus_embedding.json     # 2D coordinates per paper, per method
  comparison_corpora.json   # external baselines
  dashboard.html            # the dashboard (loads the JSON siblings)

All JSON. All plain. Open in any browser, host as a static site (GitHub Pages, Netlify), or pipe into another tool.

Hosting the dashboard

The dashboard is a single HTML file plus its JSON siblings. Drop the whole .lens/ directory anywhere that serves static files:

# GitHub Pages
gh repo create my-corpus --public
cp -r .lens/* docs/
gh-pages -d docs

# Netlify drop: drag .lens/ onto netlify.app/drop
# Vercel: vercel deploy ./.lens
# nginx / Apache: copy .lens/ to your webroot

No build step. No server-side code.

License

MIT.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

corpus_lens-0.1.0.tar.gz (33.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

corpus_lens-0.1.0-py3-none-any.whl (35.7 kB view details)

Uploaded Python 3

File details

Details for the file corpus_lens-0.1.0.tar.gz.

File metadata

  • Download URL: corpus_lens-0.1.0.tar.gz
  • Upload date:
  • Size: 33.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for corpus_lens-0.1.0.tar.gz
Algorithm Hash digest
SHA256 2a5d4d2bfaab50754d21311ee97ba7788e65aca2c6c0e966be854bd5d6415e83
MD5 7d2582bcb6868f72341dd46a35c3a369
BLAKE2b-256 72a67ac2962b04e13faa0f171ba90eb2e567f90ba37e42fb32f841fd2e635fb3

See more details on using hashes here.

File details

Details for the file corpus_lens-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: corpus_lens-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 35.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for corpus_lens-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 53b569a6f3feec12cf2b331d3e30d7b6ec75665c8fdd9f15c617d7228c38253c
MD5 e15dc02416f146c60e3580540bd80725
BLAKE2b-256 b9eb4467c587018f6cc899e3d08996a22be9f8f85767de3b39f96cc319a58f08

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page