Skip to main content

pubmed-research-classifier

Classify PubMed articles as research or non-research using a trained MLP on top of EMBO/ModernBERT-neg-sampling-PubMed embeddings.

v0.2.0 ships the Workflow v2 production weights (amplified research definition, default decision threshold τ = 0.75). Model weights, StandardScaler, and publication-type vocabulary are bundled — no external model downloads are needed for the embedding-mode API.

Installation

# Embedding mode only (no sentence-transformers required)
pip install pubmed-research-classifier

# Text mode (package embeds internally)
pip install "pubmed-research-classifier[embed]"

Upgrade from 0.1.x (narrow v1 definition / older weights):

pip install -U "pubmed-research-classifier>=0.2.0"

Research definition (v2)

Labels follow the amplified Workflow v2 definition used in the Scientometrics pipeline:

  • Research — methodology-backed work with data/analysis; systematic reviews / meta-analyses; resources (datasets, software, code); methods / theoretical models; clinical and observational designs that report such findings.
  • Non-research — narrative reviews without new analysis; perspectives, primers, letters/editorials (opinion-only), errata / retractions / news-like items.

p_nr is P(non-research). Default threshold 0.75: label is non-research when p_nr >= 0.75, else research. Raise τ (e.g. 0.95) only when you need higher precision on non-research at the cost of overall agreement.

Training provenance: MLP_with_pt (ModernBERT title + abstract + 6 continuous scalars + 194 MeSH publication-type multi-hot), seed 42, frozen split from the paper repo (models/v2/).

Quick start

Text mode

from pubmed_research_classifier import classify

result = classify({
    "title": "Structural basis of CRISPR-Cas9 activity",
    "abstract": "We report crystal structures of Cas9 ...",
    "pub_types": ["Journal Article"],
    "n_authors": 8,
    "n_refs": 42,
})
# {"label": "research", "p_nr": 0.018}

Embedding mode

Pre-compute embeddings with EMBO/ModernBERT-neg-sampling-PubMed using normalize_embeddings=True, then pass them directly:

from pubmed_research_classifier import classify
import numpy as np

result = classify({
    "title_emb":     title_embedding,    # np.ndarray, shape (768,)
    "abstract_emb":  abstract_embedding, # np.ndarray, shape (768,); zeros if absent
    "has_abstract":  True,
    "length_title":  52,
    "length_abstract": 1240,
    "pub_types":     ["Journal Article"],
    "n_authors":     8,
    "n_refs":        42,
})

Batch — millions of records

results = classify(records, batch_size=128)
# Returns a list in the same order as the input.

Custom threshold

# Default τ=0.75 (recommended)
classify(record)

# Higher precision on non-research (fewer NR calls)
classify(record, threshold=0.95)

Input fields

Field Type Mode Notes
title str text
abstract str or None text empty/None → treated as absent
title_emb array (768,) embed L2-normalised
abstract_emb array (768,) embed L2-normalised; zeros if absent
has_abstract bool embed
length_title int embed auto-derived from title in text mode
length_abstract int embed auto-derived from abstract in text mode
pub_types list[str] or str both PubMed PT tags; comma-sep string accepted
n_authors int both
n_refs int both
has_funding bool both optional; inferred from "Research Support" PTs if omitted

Output

{"label": "research",     "p_nr": 0.018}
{"label": "non-research", "p_nr": 0.921}

p_nr is P(non-research) from the model. Default threshold: 0.75 (configurable via classify(..., threshold=0.75)).

Obtaining has_funding from PubMed XML

has_funding is True when the article's PubMed XML record contains at least one <Grant> element inside a <GrantList>. It is not the same as the "Research Support, …" publication type tags (those are a separate, coarser signal also used by the model via pub_types).

If you fetch articles via the NCBI E-utilities API (efetch, XML format), you can extract it like this:

import xml.etree.ElementTree as ET

def has_funding_from_xml(article_xml: str) -> bool:
    """Return True if the PubMed XML contains at least one <Grant> entry."""
    root = ET.fromstring(article_xml)
    return len(root.findall(".//Grant")) > 0

Or, if you are working with a parsed xml.etree.ElementTree.Element object (e.g. the <PubmedArticle> node returned by your ETL pipeline):

has_funding = len(article_element.findall(".//Grant")) > 0

If you do not have access to the raw XML and only have the metadata fields, omit has_funding entirely — the package will fall back to checking whether any of the pub_types start with "Research Support", which is a reasonable proxy and is already captured separately in the model's publication-type features.

Changelog

0.2.0

  • Bundle Workflow v2 MLP_with_pt weights (models/v2/ from the paper repo).
  • Labels follow the amplified research / non-research definition (see above).
  • Default decision threshold remains τ = 0.75.

0.1.0

  • Initial release with v1 (narrow) research definition weights.

Publishing a new version to PyPI

The built artifacts live in pubmed-research-classifier/dist/.

Workflow for every new release

  1. Update the model weights — copy new mlp_best.pt, scaler.joblib, and/or mlp_config.json into src/pubmed_research_classifier/_data/ and overwrite the old files.

  2. Bump the version in two places:

    # pyproject.toml
    version = "0.2.0"
    
    # src/pubmed_research_classifier/__init__.py
    __version__ = "0.2.0"
    
  3. Update regression expectations in tests/test_classify.py for the new weights, then run pytest.

  4. Rebuild the wheel:

    cd pubmed-research-classifier
    pip install build          # first time only
    python -m build
    # produces dist/pubmed_research_classifier-0.2.0-py3-none-any.whl
    #          and dist/pubmed_research_classifier-0.2.0.tar.gz
    
  5. Upload to PyPI:

    pip install twine           # first time only
    twine upload dist/pubmed_research_classifier-0.2.0*
    # Username: __token__
    # Password: <your PyPI API token>
    

    PyPI API tokens are managed at https://pypi.org/manage/account/token/. Use a project-scoped token (not account-wide) for safety.

  6. Verify the release:

    pip install "pubmed-research-classifier==0.2.0" --force-reinstall
    python -c "from pubmed_research_classifier import classify; print('ok')"
    

First-time PyPI setup

If the package does not yet exist on PyPI, the first upload creates it automatically. You will need a PyPI account and a project-scoped (or account-scoped) API token. Test releases can go to https://test.pypi.org first:

twine upload --repository testpypi dist/pubmed_research_classifier-0.2.0*
pip install --index-url https://test.pypi.org/simple/ pubmed-research-classifier

Download files

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

Source Distribution

pubmed_research_classifier-0.2.0.tar.gz (562.2 kB view details)

Uploaded Source

Built Distribution

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

pubmed_research_classifier-0.2.0-py3-none-any.whl (561.8 kB view details)

Uploaded Python 3

File details

Details for the file pubmed_research_classifier-0.2.0.tar.gz.

File metadata

File hashes

Hashes for pubmed_research_classifier-0.2.0.tar.gz
Algorithm Hash digest
SHA256 af1f3775b62ba65c6cf4042f6fb53616a972b4efff735baa7ada1bbda78cf6ae
MD5 24e7829926923a2940138f0fb30b34be
BLAKE2b-256 2cf139cfc35d6b3466674719d5fb8d091df7216215365be313b18e5a5104ad17

See more details on using hashes here.

File details

Details for the file pubmed_research_classifier-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for pubmed_research_classifier-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a4e21dd9c57c206c00f2c0d7808348ba91ec69218d476a0620278d1c35b9b714
MD5 06293dae4cc14f1eced4114dff3c49dd
BLAKE2b-256 15ce795283fcedf29ff77e6d492ec9ac4106e9b3e02396f471714a4d164cc8af

See more details on using hashes here.

Release history Release notifications | RSS feed

0.3.0

2 files

This release

0.2.0 This release

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page