A Python library for entity-level sentiment analysis. SANE identifies named entities in text and classifies the sentiment expressed toward each one — rather than scoring the document as a whole.
Example:
"Apple announced great results, but Microsoft disappointed investors."
| Entity | Type | Sentiment | Confidence | Polarity |
|---|---|---|---|---|
| Apple | ORG | positive | 0.8529 | +0.8409 |
| Microsoft | ORG | negative | 0.9873 | −0.9873 |
SANE is a custom spaCy pipeline component that chains two models:
- NER — spaCy extracts named entities from the text
- ABSA — a HuggingFace aspect-based sentiment model classifies sentiment for each entity using the full sentence as context
uv syncimport spacy
import sane # registers the "sane" pipeline component
nlp = spacy.load("en_core_web_md")
nlp.add_pipe("sane", after="ner")
doc = nlp("Apple announced great results, but Microsoft disappointed investors.")
for es in doc._.entity_sentiments:
print(es.text, es.sentiment, es.polarity)
# Apple positive 0.8409
# Microsoft negative -0.9753for es in doc._.entity_sentiments:
print(es.text, es.sentiment_probs)
# Apple {'positive': 0.8529, 'neutral': 0.1351, 'negative': 0.012}
# Microsoft {'positive': 0.0089, 'neutral': 0.0158, 'negative': 0.9753}Only process specific entity types (e.g. organisations and people):
nlp = spacy.load("en_core_web_md")
nlp.add_pipe("sane", after="ner", config={"entity_types": ["ORG", "PERSON"]})
doc = nlp("Elon Musk praised Tesla, but critics blamed Washington for the slowdown.")
for es in doc._.entity_sentiments:
print(f"{es.text} ({es.entity_type}): {es.sentiment}")
# Elon Musk (PERSON): positive
# Tesla (ORG): positive
# Washington is skipped — it is a GPE, not ORG or PERSON# Explicit device
nlp.add_pipe("sane", after="ner", config={"device": "cuda"})
# Auto-select: prefers CUDA → MPS → CPU
nlp.add_pipe("sane", after="ner", config={"device": "auto"})nlp.add_pipe("sane", after="ner", config={
"model": "my-org/my-absa-model",
"device": "auto",
"precision": 6,
})Each item in doc._.entity_sentiments is an EntitySentiment dataclass:
| Field | Type | Description |
|---|---|---|
text |
str |
Entity surface form |
entity_type |
str |
spaCy NER label (e.g. "ORG", "PERSON") |
start_idx |
int |
Character offset (start) |
end_idx |
int |
Character offset (end) |
sentiment |
"positive" | "neutral" | "negative" |
Predicted label |
confidence |
float |
Model confidence for the predicted label |
polarity |
float |
P(positive) − P(negative) in [−1, 1] |
sentiment_probs |
dict[str, float] |
Full probability distribution over all labels |
# Run tests
pytest
# Run tests with coverage
pytest --cov=src/sane --cov-report=term-missing
# Lint
ruff check src tests
sane/
├── src/sane/ # Package
├── tests/ # Test suite
├── notebooks/ # Research & prototyping
├── docs/ # Planning & design docs
├── data/ # Datasets
└── models/ # Trained models
SANE is released under the MIT License.
SANE uses the yangheng/deberta-v3-base-absa-v1.1 model from the
PyABSA project by Yang Heng, also
distributed under the MIT License. If you use SANE in academic work, please
cite PyABSA:
@article{yang-etal-2023-pyabsa,
title = {PyABSA: A Modular and Reproducible Framework for Aspect-Based Sentiment Analysis},
author = {Yang, Heng and Li, Ke},
journal = {arXiv preprint arXiv:2208.01368},
year = {2023},
url = {https://arxiv.org/abs/2208.01368}
}