Text

Sentiment

How positive or negative English text is, by VADER: scores from -1 to 1 that understand not good, VERY good!!! and emoji, for reviews, tickets and feedback.

Version 1.0.0 · 3 functions · 1 crate

#text#reviews#support#feedback

Functions

What use sentiment.qwer gives a program, with the types Quillo sees. To take only some, or under other names: use sentiment_scores, sentiment_label from sentiment (Quillo 0.6).

sentiment_scores(text: str) → anyor an error

The sentiment of text: { label, compound, positive, negative, neutral }. compound runs from -1 (most negative) to 1 (most positive); label is "positive" from 0.05, "negative" from -0.05, else "neutral"; the other three are the shares of the text that are each, adding up to 1. Best on short texts such as a review or a tweet; English only. Up to 100 KB.

sentiment_label(text: str) → stror an error

Only the label: "positive", "negative" or "neutral".

sentiment_sentences(text: str) → listor an error

Each sentence of text with its scores, [{ sentence, label, compound }], for long feedback that praises one thing and complains of another.

Get it

$ quillo install sentiment               # sentiment.qwer into this folder$ quillo install sentiment --example     # and its example, sentiment.qwe

Or with curl:

$ curl -O https://quillolang.org/plugins/files/sentiment.qwer$ curl -O https://quillolang.org/plugins/files/sentiment.qwe

Then, in your program, all of it or only what you call:

main.qwe
use sentiment.qweruse sentiment_scores, sentiment_label from sentiment      // Quillo 0.6: only these

It uses this crate, which cargo fetches the first time the plugin is built:

[dependencies]
vader_sentiment = "0.1"

Example

sentiment.qwe
// Product reviews sorted by how unhappy they are, so support answers the worst first.////   quillo run sentiment.qwe      then open http://localhost:8080/reviewsuse "sentiment.qwer"let reviews = [    "Absolutely love it!!! Best purchase this year 😍",    "It is not good. The strap broke after two days and nobody answered my emails.",    "Arrived on Tuesday.",    "The screen is great, but the battery is terrible.",]route GET /reviews = reviews | map((r) => { review: r, mood: sentiment_scores(r)? }) | sort_by((r) => r.mood.compound)route GET /mood ? { text: str } = sentiment_scores(text)?route GET /sentences = sentiment_sentences("The screen is great. But the battery is terrible!!! Shipping was fine.")?
$ quillo run sentiment.qwe$ curl 'localhost:8080/reviews'$ curl 'localhost:8080/sentences'

Source

sentiment.qwer
//! How positive or negative English text is, for reviews, support tickets and feedback: VADER scores that understand "not good", "VERY good!!!" and emoji.//! version: 1.0.0//!//! [dependencies]//! vader_sentiment = "0.1"use serde_json::{json, Value};use std::sync::OnceLock;use vader_sentiment::SentimentIntensityAnalyzer;fn analyzer() -> &'static SentimentIntensityAnalyzer<'static> {    static A: OnceLock<SentimentIntensityAnalyzer<'static>> = OnceLock::new();    A.get_or_init(SentimentIntensityAnalyzer::new)}fn round(x: f64) -> f64 {    (x * 1000.0).round() / 1000.0}fn label(compound: f64) -> &'static str {    if compound >= 0.05 { "positive" } else if compound <= -0.05 { "negative" } else { "neutral" }}fn scores(text: &str) -> Result<Value, String> {    if text.len() > 100_000 {        return Err("text up to 100 KB".into());    }    let t = text.to_string();    // The analyzer is an old port; a panic in it becomes an error, not a failed request.    let s = std::panic::catch_unwind(move || {        let m = analyzer().polarity_scores(&t);        (m.get("pos").copied().unwrap_or(0.0), m.get("neg").copied().unwrap_or(0.0), m.get("neu").copied().unwrap_or(0.0), m.get("compound").copied().unwrap_or(0.0))    })    .map_err(|_| "could not score this text".to_string())?;    Ok(json!({ "label": label(s.3), "compound": round(s.3), "positive": round(s.0), "negative": round(s.1), "neutral": round(s.2) }))}/// The sentiment of `text`: `{ label, compound, positive, negative, neutral }`. `compound` runs from -1 (most negative) to 1 (most positive); `label` is "positive" from 0.05, "negative" from -0.05, else "neutral"; the other three are the shares of the text that are each, adding up to 1. Best on short texts such as a review or a tweet; English only. Up to 100 KB.pub fn sentiment_scores(text: &str) -> Result<Value, String> {    scores(text)}/// Only the label: "positive", "negative" or "neutral".pub fn sentiment_label(text: &str) -> Result<String, String> {    Ok(scores(text)?["label"].as_str().unwrap_or("neutral").to_string())}/// Each sentence of `text` with its scores, `[{ sentence, label, compound }]`, for long feedback that praises one thing and complains of another.pub fn sentiment_sentences(text: &str) -> Result<Vec<Value>, String> {    if text.len() > 100_000 {        return Err("text up to 100 KB".into());    }    let mut out = Vec::new();    let mut start = 0;    let chars: Vec<(usize, char)> = text.char_indices().collect();    for (i, &(at, c)) in chars.iter().enumerate() {        let next_space = chars.get(i + 1).map_or(true, |&(_, n)| n.is_whitespace());        if (c == '.' || c == '!' || c == '?' || c == '\n') && next_space {            let end = at + c.len_utf8();            // Keep "!!!" and "?!" with their sentence.            if chars.get(i + 1).map_or(false, |&(_, n)| n == '!' || n == '?') {                continue;            }            push(&mut out, &text[start..end])?;            start = end;        }    }    push(&mut out, &text[start..])?;    Ok(out)}fn push(out: &mut Vec<Value>, s: &str) -> Result<(), String> {    let s = s.trim();    if !s.is_empty() {        let sc = scores(s)?;        out.push(json!({ "sentence": s, "label": sc["label"], "compound": sc["compound"] }));    }    Ok(())}
Thread
79Useful? Like it, or add to its thread below.

Thread 0 posts

On Sentiment: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.