reyalab-test / paper_type.py
ReyaLabColumbia's picture
Upload 3 files
592dbff verified
Raw
History Blame Contribute Delete
4 kB
"""Deterministic paper-type classification for review routing.
This module is intentionally lightweight. It does not claim to infer a formal
publication type; it produces a practical label that helps the pipeline and UI
separate likely functional experiments from reviews, expression/prognosis-only
papers, methods papers, and broad screens.
"""
from __future__ import annotations
import re
from typing import Any
REVIEW_TERMS = (
"review", "systematic review", "meta-analysis", "editorial", "comment",
"letter", "guideline",
)
METHODS_TERMS = (
"database", "dataset", "atlas", "protocol", "method", "pipeline",
"software", "benchmark", "resource", "web server",
)
CLINICAL_ASSOCIATION_TERMS = (
"prognostic", "prognosis", "survival analysis", "overall survival",
"hazard ratio", "kaplan-meier", "nomogram", "cohort",
)
EXPRESSION_ASSOCIATION_TERMS = (
"expression", "expressed", "upregulated", "downregulated",
"biomarker", "correlated with", "associated with",
"immunohistochemistry", "methylation",
)
PERTURBATION_TERMS = (
"knockdown", "knock-down", "knockout", "knock-out", "silencing",
"silenced", "depletion", "deleted", "deletion", "crispr", "cas9",
"sirna", "shrna", "rnai", "sgRNA".lower(), "loss-of-function",
)
PHENOTYPE_TERMS = (
"proliferation", "viability", "apoptosis", "migration", "invasion",
"tumor growth", "tumour growth", "tumor volume", "tumour volume",
"colony formation", "metastasis", "xenograft", "organoid",
)
SCREEN_TERMS = (
"screen", "screening", "genome-wide", "pooled crispr", "library",
"dropout",
)
def _split(value: Any) -> list[str]:
if isinstance(value, list):
return [str(x).strip() for x in value if str(x).strip()]
return [x.strip() for x in str(value or "").split("|") if x.strip()]
def _contains(text: str, terms: tuple[str, ...]) -> bool:
return any(term in text for term in terms)
def classify_paper_type(
title: str = "",
abstract: str = "",
publication_types: str | list[str] = "",
evidence: dict | None = None,
) -> str:
"""Return a practical paper-type label used for triage.
Labels are heuristic and meant to support human review:
- functional_experiment
- functional_screen
- review
- clinical_prognostic
- expression_association
- methods_or_dataset
- unknown
"""
evidence = evidence or {}
title_l = str(title or "").lower()
abstract_l = str(abstract or "").lower()
pub_types_l = " ".join(_split(publication_types)).lower()
evidence_l = " ".join(
s.lower()
for key in (
"evidence_perturbation",
"evidence_in_vitro",
"evidence_in_vivo",
"evidence_crispr_screen",
)
for s in _split(evidence.get(key))
)
text_l = f"{title_l}\n{abstract_l}\n{evidence_l}"
if _contains(pub_types_l, REVIEW_TERMS) or _contains(title_l, REVIEW_TERMS):
return "review"
if _contains(title_l, METHODS_TERMS) and not _contains(evidence_l, PERTURBATION_TERMS):
return "methods_or_dataset"
has_perturbation = _contains(evidence_l or text_l, PERTURBATION_TERMS)
has_phenotype = _contains(evidence_l or text_l, PHENOTYPE_TERMS)
has_screen = _contains(evidence_l or title_l, SCREEN_TERMS)
if has_perturbation and has_phenotype and has_screen:
return "functional_screen"
if has_perturbation and has_phenotype:
return "functional_experiment"
if _contains(text_l, CLINICAL_ASSOCIATION_TERMS):
return "clinical_prognostic"
if _contains(text_l, EXPRESSION_ASSOCIATION_TERMS):
return "expression_association"
if _contains(text_l, METHODS_TERMS):
return "methods_or_dataset"
return "unknown"
def paper_type_is_negative_evidence(paper_type: str) -> bool:
return str(paper_type or "") in {
"review",
"clinical_prognostic",
"expression_association",
"methods_or_dataset",
}