Sentiment Analysis
Sentiment analysis detects the author's emotions expressed in the text. Was the author:
- happy (I loved it.),
- neutral (We went to London.),
- unhappy (The lunch was not good at all.), or
- ambivalent (The lunch was good, but expensive.)
about their experience?
You can analyze sentiment in reviews, feedback, or customer service messages. Document-level sentiment analysis of news articles often doesn't make much sense (except for opinion pieces), as the goal isn't to determine whether the news is "good" or "bad," but rather to detect the author's opinion about the subject matter.
Our sentiment analysis is driven by practical needs—not academic purity.
Textual Sentiment
Since sentiment often cannot be expressed as a single number, we return three values (all in the range [-1.0, 1.0]):
mean– the average sentimentpos– the positive component (ignores negative parts)neg– the negative component (ignores positive parts)
{
"language": { "detected": "en" },
"docSentiment": {
"mean": 0.6,
"label": "positive",
"positive": 0.8,
"negative": -0.2
},
"usedChars": 100
}
For clearly positive or negative texts, only the (mean) matters:
| mean | label | pos | neg | text |
|---|---|---|---|---|
| +0.5 | positive | +0.5 | +0.0 | Staff was nice. |
| +0.7 | very positive | +0.7 | +0.0 | Staff was absolutely astounding. |
| -0.5 | negative | +0.0 | -0.5 | Food was overpriced. |
| -0.7 | very negative | +0.0 | -0.7 | Food was absolutely terrible. |
In theses cases, the pos and neg values don't add any extra information—they
are either zero or equal to the mean.
However, ambivalent reviews are common:
the author likes some things but dislikes others.
In such cases, the mean alone isn't enough—pos and neg provide critical nuance.
Consider the following examples. In both cases, the overall sentiment score is 0. However, the second text is more emotionally charged—both positively and negatively. This is reflected in the higher values of the positive and negative components.
| mean | label | pos | neg | text |
|---|---|---|---|---|
| +0.0 | ambivalent | +0.3 | -0.3 | Staff was nice. Food was overpriced. |
| +0.0 | ambivalent | +0.4 | -0.4 | Staff was absolutely astounding. Food was absolutely terrible. |
When calculating components, passages with opposite sentiment are treated as neutral. For example, both of the following texts result in the same negative component:
| mean | label | pos | neg | text |
|---|---|---|---|---|
| +0.0 | ambivalent | +0.4 | -0.4 | Staff was absolutely astounding. The food was absolutely terrible. |
| -0.4 | negative | +0.0 | -0.4 | Today is Tuesday. The food was absolutely terrible. |
Sentiment Labels
Sentiment labels are included for convenience. By default, they're defined as:
-
neutral: mean in interval [-0.1, 0.1] -
ambivalent:- both
posandnegare non-zero - neither component is significant (i.e.,
meanis between [-0.5, 0.5]) {mean: 0.0, pos: 0.4, neg: -0.4}→ ambivalent{mean: -0.1, pos: 0.4, neg: -0.6}→ ambivalent{mean: -0.6, pos: 0.2, neg: -1.0}→ negative (dominant negativity)
- both
A typical text contains many neutral passages, which may "dilute" overall sentiment.
Item Sentiment
In addition to overall sentiment, we also provide item-level sentiment—i.e., sentiment linked to specific entities or relations.
Currently, item sentiment is derived from the sentiment of the sentence in which the items appears. This means that sentiment may not always precisely reflect the item's evaluation.
For example, in the sentence:
Even the talented actor could not make up for the absolutely disastrous script.
... the actor may be judged negatively because the sentence as a whole is negative—even though the phrase about the actor is positive.
Sample Call
Try sentiment analysis with the following examples:
- cURL
- cURL (Windows)
- Python SDK
- plain Python
curl -X POST https://api.geneea.com/v3/analysis \
-H 'Authorization: user_key <YOUR USER KEY>' \
-H 'Content-Type: application/json' \
-d '{
"id": "1",
"text": "The trip to London was amazing. Only the food was weird. Especially the pizza was terrible.",
"analyses": ["sentiment", "entities", "relations"],
"returnItemSentiment": "true",
"domain": "voc-hospitality"
}'
curl -X POST https://api.geneea.com/v3/analysis \
-H "Authorization: user_key <YOUR USER KEY>" \
-H "Content-Type: application/json" \
-d "{
\"id\": \"1\",
\"text\": \"The trip to London was amazing. Only the food was weird. Especially the pizza was terrible.\",
\"analyses\": [\"sentiment\", \"entities\", \"relations\"],
\"returnItemSentiment\": \"true\",
\"domain\": \"voc-hospitality\"
}"
from geneeanlpclient import g3
requestBuilder = g3.Request.Builder(
analyses=[g3.AnalysisType.SENTIMENT, g3.AnalysisType.ENTITIES, g3.AnalysisType.RELATIONS],
domain='voc-hospitality',
returnItemSentiment=True
)
with g3.Client.create(userKey=<YOUR USER KEY>) as analyzer:
result = analyzer.analyze(requestBuilder.build(
id=str(1),
text='The trip to London was amazing. Only the food was weird. Especially the pizza was terrible.'
))
print(f'Doc sentiment: {result.docSentiment}')
print('Entities:')
for e in result.entities:
print(f' {e.stdForm}: {e.sentiment}')
print('Relations:')
for r in result.relations:
print(f' {r.textRepr}: {r.sentiment}')
import requests
def callGeneea(input):
url = 'https://api.geneea.com/v3/analysis'
headers = {
'content-type': 'application/json',
'Authorization': 'user_key <YOUR USER KEY>'
}
return requests.post(url, json=input, headers=headers).json()
responseObj = callGeneea({
'id': '1',
'text': 'The trip to London was amazing. Only the food was weird. Especially the pizza was terrible.',
'analyses': ['sentiment', 'entities', 'relations'],
'returnItemSentiment': True,
'domain': 'voc-hospitality'
})
print(responseObj)
Sample Response
- cURL
- cURL (Windows)
- Python SDK
- plain Python
{
"id": "1",
"language": {"detected": "en"},
"entities": [
{"id": "E0", "gkbId": "HSP-1091", "stdForm": "pizza", "type": "food"}
],
"relations": [
{"id": "R0", "name": "amazing", "textRepr": "amazing(trip)", "type": "ATTR", "args": [{"type": "SUBJECT", "name": "trip"}], "feats": {"negated": "false", "modality": ""}},
{"id": "R1", "name": "weird", "textRepr": "weird(food)", "type": "ATTR", "args": [{"type": "SUBJECT", "name": "food"}], "feats": {"negated": "false", "modality": ""}},
{"id": "R2", "name": "terrible", "textRepr": "terrible(pizza)", "type": "ATTR", "args": [{"type": "SUBJECT", "name": "pizza", "entityId": "E0"}], "feats": {"negated": "false", "modality": ""}}
],
"docSentiment": {"mean": -0.1, "label": "negative", "positive": 0.2, "negative": -0.3},
"itemSentiments": {
"E0": {"mean": -0.5, "label": "negative", "positive": 0.0, "negative": -0.5},
"R0": {"mean": 0.5, "label": "positive", "positive": 0.5, "negative": 0.0},
"R1": {"mean": -0.4, "label": "negative", "positive": 0.0, "negative": -0.4},
"R2": {"mean": -0.5, "label": "negative", "positive": 0.0, "negative": -0.5}
},
"usedChars": 100
}
{
"id": "1",
"language": {"detected": "en"},
"entities": [
{"id": "E0", "gkbId": "HSP-1091", "stdForm": "pizza", "type": "food"}
],
"relations": [
{"id": "R0", "name": "amazing", "textRepr": "amazing(trip)", "type": "ATTR", "args": [{"type": "SUBJECT", "name": "trip"}], "feats": {"negated": "false", "modality": ""}},
{"id": "R1", "name": "weird", "textRepr": "weird(food)", "type": "ATTR", "args": [{"type": "SUBJECT", "name": "food"}], "feats": {"negated": "false", "modality": ""}},
{"id": "R2", "name": "terrible", "textRepr": "terrible(pizza)", "type": "ATTR", "args": [{"type": "SUBJECT", "name": "pizza", "entityId": "E0"}], "feats": {"negated": "false", "modality": ""}}
],
"docSentiment": {"mean": -0.1, "label": "negative", "positive": 0.2, "negative": -0.3},
"itemSentiments": {
"E0": {"mean": -0.5, "label": "negative", "positive": 0.0, "negative": -0.5},
"R0": {"mean": 0.5, "label": "positive", "positive": 0.5, "negative": 0.0},
"R1": {"mean": -0.4, "label": "negative", "positive": 0.0, "negative": -0.4},
"R2": {"mean": -0.5, "label": "negative", "positive": 0.0, "negative": -0.5}
},
"usedChars": 100
}
Doc sentiment: Sentiment(mean=-0.1, label="ambivalent", positive=0.2, negative=-0.3)
Entities:
food: Sentiment(mean=-0.4, label="negative", positive=0.0, negative=-0.4)
pizza: Sentiment(mean=-0.5, label="negative", positive=0.0, negative=-0.5)
Relations:
amazing(trip): Sentiment(mean=0.5, label="positive", positive=0.5, negative=0.0)
weird(food): Sentiment(mean=-0.4, label="negative", positive=0.0, negative=-0.4)
terrible(pizza): Sentiment(mean=-0.5, label="negative", positive=0.0, negative=-0.5)
{
"id": "1",
"language": {"detected": "en"},
"entities": [
{"id": "E0", "gkbId": "HSP-1091", "stdForm": "pizza", "type": "food"}
],
"relations": [
{"id": "R0", "name": "amazing", "textRepr": "amazing(trip)", "type": "ATTR", "args": [{"type": "SUBJECT", "name": "trip"}], "feats": {"negated": "false", "modality": ""}},
{"id": "R1", "name": "weird", "textRepr": "weird(food)", "type": "ATTR", "args": [{"type": "SUBJECT", "name": "food"}], "feats": {"negated": "false", "modality": ""}},
{"id": "R2", "name": "terrible", "textRepr": "terrible(pizza)", "type": "ATTR", "args": [{"type": "SUBJECT", "name": "pizza", "entityId": "E0"}], "feats": {"negated": "false", "modality": ""}}
],
"docSentiment": {"mean": -0.1, "label": "negative", "positive": 0.2, "negative": -0.3},
"itemSentiments": {
"E0": {"mean": -0.5, "label": "negative", "positive": 0.0, "negative": -0.5},
"R0": {"mean": 0.5, "label": "positive", "positive": 0.5, "negative": 0.0},
"R1": {"mean": -0.4, "label": "negative", "positive": 0.0, "negative": -0.4},
"R2": {"mean": -0.5, "label": "negative", "positive": 0.0, "negative": -0.5}
},
"usedChars": 100
}
Interpretation
From the response:
- The document as a whole is slighly negative
→ (
"docSentiment": {"mean": -0.1, "label": "negative", ...}), - It contains positive sentiment (
"docSentiment": { ... "positive": 0.2 ... }) → ("docSentiment": { ... "negative": -0.3}) - ...and **negative sentiment
→ (
"E0": {"mean": -0.5, "label": "negative", "positive": 0.0, "negative": -0.5}) - Amazing trip is judged positively
→ (
"R0": {"mean": 0.5, "label": "positive", "positive": 0.5, "negative": 0.0}) - Weird food is judged negatively
→ (
"R1": {"mean": -0.4, "label": "negative", "positive": 0.0, "negative": -0.4}) - Terrible pizza is judged negatively
→ (
"R2": {"mean": -0.5, "label": "negative", "positive": 0.0, "negative": -0.5})
Customization
We can tailor sentiment analysis to meet your specific needs.