---
title: "Analise de sentimento"
output:
  rmarkdown::html_vignette:
    toc: true
    toc_depth: 2
vignette: >
  %\VignetteIndexEntry{Analise de sentimento}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(
  echo       = TRUE,
  eval       = TRUE,
  collapse   = TRUE,
  comment    = "#>",
  fig.width  = 8.5,
  fig.height = 5.5,
  fig.align  = "center",
  out.width  = "100%",
  dpi        = 140
)
library(acR)
```

## O que é análise de sentimento com léxico

Análise de sentimento baseada em **léxico** classifica textos em positivo,
negativo ou neutro **contando palavras** de listas curadas: palavras que
carregam valência positiva (*excelente*, *aprovar*, *conquista*) contra
palavras de valência negativa (*péssimo*, *fracasso*, *retrocesso*).

O `acR` usa por padrão o **OpLexicon** (Souza & Vieira, 2012), o mais
usado para português brasileiro, com cerca de 30 mil palavras rotuladas.

### Quando usar léxico vs. LLM?

| Cenário | Léxico | LLM |
|---|---|---|
| Sinal grosso em milhares de docs | ✅ | ❌ caro |
| Ironia, sarcasmo, contexto | ❌ | ✅ |
| Sem chave de API, sem rede | ✅ | ❌ |
| Precisa reprodutibilidade exata | ✅ | ⚠️ estocástico |
| Documentos muito curtos (tweets) | ⚠️ | ✅ |

Regra prática: use léxico para **panorama rápido** e **filtragem de casos
extremos**, depois use codificação qualitativa (via `ac_qual_code()`) na
amostra que interessa.

```{r pkg}
library(acR)
library(dplyr)
```

## 1. Corpus: pronunciamentos sobre uma reforma polêmica

Corpus fabricado de 14 pronunciamentos, com metadados de partido e
posição declarada. Serve para exercitar visualizações; em corpora reais,
substitua pela coleta via `ac_fetch_camara()` ou `ac_fetch_senado()`.

```{r corpus}
textos <- c(
  # Favoráveis
  "Excelente proposta que moderniza o sistema e protege as geracoes futuras.",
  "Aprovamos com convicção esta reforma responsável e necessária ao país.",
  "Vitória histórica para a economia: reforma corrige distorções antigas.",
  "Reforma equilibrada garante sustentabilidade fiscal e futuro promissor.",
  "Conquista importante que beneficia trabalhadores e empresas do Brasil.",
  # Contrários
  "Esta reforma é um retrocesso vergonhoso que prejudica os trabalhadores.",
  "Voto contra: proposta desastrosa ataca os mais pobres e vulneráveis.",
  "Uma tragédia nacional: estão roubando direitos duramente conquistados.",
  "Rejeitamos com indignação essa proposta injusta e cruel com o povo.",
  "Reforma péssima que aprofunda desigualdades e destrói a proteção social.",
  # Neutros / técnicos
  "O texto substitutivo altera o artigo 201 da Constituição Federal.",
  "O relatório final incorporou emendas apresentadas em plenário.",
  "A comissão aprovou o parecer com dez votos a favor e oito contra.",
  "A proposta segue para votação na próxima sessão ordinária."
)

df <- data.frame(
  text     = textos,
  posicao  = c(rep("favor", 5), rep("contra", 5), rep("neutro", 4)),
  partido  = c("PT","PSD","PL","MDB","PSDB",
               "PSOL","PT","PDT","PSOL","PCdoB",
               "MDB","PSDB","PL","MDB"),
  stringsAsFactors = FALSE
)
corpus <- ac_corpus(df)
corpus
```

## 2. Sentimento por documento

`ac_sentiment()` retorna um tibble com contagens de palavras positivas
(`n_pos`), negativas (`n_neg`) e neutras (`n_neu`), o `score` (n_pos −
n_neg pelo método padrão `sum`) e o rótulo categórico `sentiment`.

```{r sentiment}
sent <- ac_sentiment(corpus)
sent
```

Cruzando com a posição declarada — o léxico "acerta"?

```{r validacao}
sent |>
  dplyr::left_join(df |> dplyr::mutate(doc_id = paste0("doc_", dplyr::row_number())),
                   by = "doc_id") |>
  dplyr::count(posicao, sentiment) |>
  tidyr::pivot_wider(names_from = sentiment, values_from = n, values_fill = 0L)
```

Léxico funciona bem para **posições explícitas** (favor/contra) com
vocabulário emotivo. Falha em textos técnicos-neutros, que
tipicamente misturam palavras positivas e negativas em proporção baixa.

## 3. Comparar métodos de agregação

Três métodos alteram como o `score` é calculado a partir das contagens:

* **`sum`** (padrão): `score = n_pos − n_neg`. Depende do tamanho do texto.
* **`mean`**: normaliza pelo número total de palavras. Comparável entre
  textos curtos e longos.
* **`ratio`**: `(n_pos − n_neg) / (n_pos + n_neg)`. Ignora palavras
  neutras, foca só na intensidade emotiva relativa.

```{r metodos}
sent_sum   <- ac_sentiment(corpus, method = "sum")
sent_mean  <- ac_sentiment(corpus, method = "mean")
sent_ratio <- ac_sentiment(corpus, method = "ratio")

comparacao <- data.frame(
  doc_id = sent_sum$doc_id,
  sum    = round(sent_sum$score,   2),
  mean   = round(sent_mean$score,  2),
  ratio  = round(sent_ratio$score, 2)
)
head(comparacao, 10)
```

## 4. Visualização: barras por documento

```{r plot-bar, fig.height=6}
ac_plot_sentiment(sent, type = "bar") +
  ggplot2::labs(
    title    = "Sentimento por documento",
    subtitle = "Contagem de palavras positivas vs. negativas (OpLexicon)",
    caption  = "acR - ac_plot_sentiment(type = \"bar\")"
  )
```

Padrão claro: os 5 primeiros documentos (favoráveis) verdes/positivos,
os 5 seguintes (contrários) vermelhos/negativos, os 4 últimos
(neutros/técnicos) cinza.

## 5. Visualização: distribuição (densidade)

Útil para ver **a forma da distribuição** — sentimento é bimodal (dois
picos, favor e contra) ou tem uma cauda longa?

```{r plot-density, fig.height=5}
ac_plot_sentiment(sent, type = "density") +
  ggplot2::labs(
    title    = "Distribuição de scores de sentimento",
    subtitle = "Bimodalidade sugere polarização; unimodal sugere consenso"
  )
```

## 6. Visualização: série ordenada

Se seus documentos têm ordem temporal (discursos ao longo do ano), a
série mostra tendência.

```{r plot-line, fig.height=5}
ac_plot_sentiment(sent, type = "line") +
  ggplot2::labs(
    title    = "Sentimento ao longo dos documentos (ordem de entrada)",
    subtitle = "Substitua a ordem por data em corpora reais"
  )
```

## 7. Agregação por grupo (partido)

A análise mais útil raramente é doc-a-doc: é comparar **grupos**. Vamos
agregar por partido:

```{r por-partido}
por_partido <- sent |>
  dplyr::left_join(df |> dplyr::mutate(doc_id = paste0("doc_", dplyr::row_number())),
                   by = "doc_id") |>
  dplyr::group_by(partido) |>
  dplyr::summarise(
    n_docs     = dplyr::n(),
    score_med  = round(mean(score), 2),
    score_dp   = round(stats::sd(score, na.rm = TRUE), 2),
    n_positivo = sum(sentiment == "positivo"),
    n_negativo = sum(sentiment == "negativo"),
    n_neutro   = sum(sentiment == "neutro"),
    .groups    = "drop"
  ) |>
  dplyr::arrange(dplyr::desc(score_med))

por_partido
```

Visualização (ranqueando partidos pelo score médio):

```{r plot-partidos, fig.height=6}
if (requireNamespace("ggplot2", quietly = TRUE)) {
  ggplot2::ggplot(
    por_partido,
    ggplot2::aes(x = stats::reorder(partido, score_med), y = score_med,
                 fill = score_med > 0)
  ) +
    ggplot2::geom_col(alpha = 0.85, show.legend = FALSE) +
    ggplot2::geom_hline(yintercept = 0, linetype = 2, color = "grey40") +
    ggplot2::coord_flip() +
    ggplot2::scale_fill_manual(values = c("TRUE" = "#166534", "FALSE" = "#991B1B")) +
    ggplot2::labs(
      title    = "Sentimento médio por partido",
      subtitle = "Score médio agregando pronunciamentos por sigla partidária",
      x        = NULL,
      y        = "Score médio (positivo = verde, negativo = vermelho)",
      caption  = "acR"
    ) +
    ggplot2::theme_minimal(base_size = 12) +
    ggplot2::theme(
      plot.title    = ggplot2::element_text(face = "bold"),
      plot.subtitle = ggplot2::element_text(color = "grey40", size = 10),
      panel.grid.major.y = ggplot2::element_blank()
    )
}
```

## 8. Termos-chave dentro do texto (X-ray)

`ac_plot_xray()` mostra **onde** termos-alvo aparecem dentro de cada
documento. Útil para ver se palavras negativas se concentram no começo,
meio ou fim — sinal de estrutura retórica.

```{r plot-xray, fig.height=5}
ac_plot_xray(corpus, terms = c("reforma", "trabalhadores", "aprovar", "vergonha")) +
  ggplot2::labs(
    title    = "Onde os termos-chave aparecem no corpus",
    subtitle = "Cada ponto marca uma ocorrência do termo (posição relativa no texto)"
  )
```

## 9. Exportar para artigo/relatório

```{r exportar, eval=FALSE}
# Tabela por partido pronta para colar no LaTeX
ac_export(por_partido, arquivo = "sentimento_por_partido.tex", formato = "latex")

# CSV para Excel/planilha
ac_export(sent, arquivo = "sentimento.csv", formato = "csv")

# Se quiser passar para a etapa qualitativa (LLM):
# amostrar documentos ambíguos (perto de score = 0) para revisão humana
casos_limitrofes <- sent |> dplyr::filter(abs(score) <= 1)
```

## Referências

* Souza, M.; Vieira, R. (2012). Sentiment Analysis on Twitter with
  Portuguese Language. *4th Workshop on Computational Approaches to
  Subjectivity, Sentiment and Social Media Analysis*, PUCRS.
* Pang, B.; Lee, L. (2008). Opinion Mining and Sentiment Analysis.
  *Foundations and Trends in Information Retrieval*, 2(1-2), 1-135.
* Silge, J.; Robinson, D. (2017). *Text Mining with R*. O'Reilly.
* Sampaio, R. C.; Lycarião, D. (2021). *Análise de conteúdo categorial:
  manual de aplicação*. ENAP.
