---
title: "Modelagem de tópicos com LDA"
output:
  rmarkdown::html_vignette:
    toc: true
    toc_depth: 2
vignette: >
  %\VignetteIndexEntry{Modelagem de tópicos com LDA}
  %\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 = 6,
  fig.align  = "center",
  out.width  = "100%",
  dpi        = 140
)
library(acR)
has_topicmodels <- requireNamespace("topicmodels", quietly = TRUE)
```

```{r aviso-topicmodels, echo=FALSE, results="asis", eval=!has_topicmodels}
cat(
  "> **Nota:** o pacote `topicmodels` não está instalado neste ambiente.",
  "Os trechos a seguir que dependem dele foram omitidos; instale com",
  "`install.packages(\"topicmodels\")` para reproduzir a vignette completa.",
  sep = "\n"
)
```

## O que é LDA e para que serve

**Latent Dirichlet Allocation** (Blei, Ng & Jordan, 2003) é um modelo
probabilístico não-supervisionado que descobre *tópicos* em um corpus. Um
"tópico" é uma **distribuição de palavras**: por exemplo, um tópico sobre
saúde tende a ter alta probabilidade para "hospital", "médico", "SUS",
"paciente". Um documento é modelado como **mistura de tópicos** com pesos
diferentes.

Duas matrizes de saída são o coração do modelo:

* **beta (β)**: probabilidade de cada termo em cada tópico. Responde:
  *"que palavras definem este tópico?"*.
* **gamma (γ)**: probabilidade de cada tópico em cada documento. Responde:
  *"que temas dominam este documento?"*.

> **Quando usar LDA?** Quando você tem um corpus grande demais para ler
> tudo, quer identificar temas emergentes sem definir categorias a priori,
> e as unidades são documentos longos o suficiente para ter mistura
> temática (não frases isoladas). É comum em análise de discurso
> parlamentar, notícias, revisões de literatura, feedback de clientes.

### LDA vs. clustering vs. LLM

Três técnicas resolvem problemas parecidos e são confundidas. A distinção
importa mais que a sintaxe:

| Técnica                  | Saída                            | Você fornece            |
|--------------------------|----------------------------------|-------------------------|
| **LDA** (esta vignette)  | Mistura probabilística de tópicos| Só o corpus + `k`       |
| **Hard clustering** (`vignette("cluster")`) | Uma etiqueta por documento | Só o corpus + `k`     |
| **LLM** (`vignette("qualitativo-llm")`) | Categoria pré-definida | Codebook com categorias |

Regra prática: use **LDA** quando temas se **misturam** dentro do mesmo texto
(discurso parlamentar, notícia, artigo científico); **clustering** quando você
quer uma partição limpa para amostrar; **LLM** quando as categorias vêm da
teoria e não da distribuição empírica de palavras.

O `acR` empacota o pipeline em três chamadas: `ac_lda_tune()` para
escolher **K** (número de tópicos), `ac_lda()` para ajustar o modelo,
`ac_plot_lda_topics()` para visualizar. Todo o restante é `dplyr` puro.

## 1. Corpus: agenda legislativa

LDA precisa de **corpus suficiente**. Aqui usamos 32 documentos cobrindo
quatro temas: reforma tributária, IA e proteção de dados, habitação, e
educação. Cada tema tem 8 documentos com vocabulário parcialmente
sobreposto (para o modelo ter algo interessante para desemaranhar).

Cada documento é um parágrafo de três frases (cerca de 20 palavras
depois da limpeza), não uma frase solta. Isso importa: o LDA aprende
os tópicos a partir de palavras que **co-ocorrem no mesmo documento**,
e textos muito curtos dão poucas co-ocorrências. Hong & Davison (2010)
e Yan et al. (2013) documentam que o LDA padrão perde qualidade em
textos curtos como tweets; numa versão anterior desta vignette, com
frases de ~7 palavras, os tópicos recuperavam os temas pouco melhor
que o acaso. Se seu corpus é de textos curtos, agregue-os (por autor,
por sessão, por dia) ou use modelos próprios para isso.

```{r corpus}
textos <- c(
  # Tema 1: reforma tributária (8 documentos)
  "A reforma tributária simplifica o sistema de impostos e reduz a carga fiscal para empresas. O governo federal estima ganho de arrecadacao com menos sonegacao. Tributaristas pedem cautela na transicao entre os dois sistemas de impostos.",
  "O IVA dual substitui PIS, COFINS e ICMS no novo modelo de arrecadacao federal. Estados e municipios temem perder receita com o fim do ICMS. A transicao do imposto sobre consumo levara anos de ajuste fiscal.",
  "Deputados debatem aliquotas e excecoes tributárias para setores economicos. Cada setor pede tratamento diferenciado no novo imposto. O relator da reforma tributária resiste a ampliar as excecoes fiscais.",
  "A reforma tributária unifica impostos indiretos e simplifica obrigacoes das empresas. Contadores esperam menos custo para apurar tributos. A cobranca do imposto no destino muda a disputa fiscal entre estados.",
  "Empresários criticam a alta carga tributária brasileira sobre o consumo popular. O peso dos impostos indiretos recai sobre os mais pobres. A proposta de cashback devolve parte do tributo as familias de baixa renda.",
  "O relator propoe aliquotas diferenciadas para produtos essenciais no novo IVA. A cesta básica tera aliquota zero de imposto. Economistas calculam o efeito das excecoes sobre a aliquota padrao do tributo.",
  "A reforma tributária promete reduzir litigios fiscais e aumentar a arrecadacao. O contencioso tributário bilionario trava empresas e governo. Regras simples de imposto diminuem disputas judiciais sobre tributos.",
  "Congresso avalia impacto da reforma tributária sobre pequenas empresas e servicos. O Simples Nacional sera mantido com ajustes de aliquota. O setor de servicos teme aumento da carga de impostos sobre a folha.",
  # Tema 2: IA e proteção de dados (8 documentos)
  "O marco legal da inteligência artificial regulamenta o uso de algoritmos e dados. O projeto classifica sistemas de IA por nivel de risco. Algoritmos de alto risco exigirao avaliação de impacto antes do uso.",
  "Privacidade e seguranca de dados pessoais sao prioridades da nova lei de IA. Vazamentos de dados expõem milhoes de usuarios. A lei preve multas para empresas que tratarem dados pessoais sem consentimento.",
  "Algoritmos de inteligência artificial precisam de transparência e auditoria pública. Decisoes automatizadas devem ser explicaveis ao cidadao. Auditores independentes poderao inspecionar o codigo e os dados de treinamento.",
  "A LGPD estabelece regras para tratamento de dados pessoais por empresas e governo. O titular pode pedir acesso e exclusao dos seus dados. Empresas precisam nomear um encarregado de protecao de dados pessoais.",
  "Modelos de IA generativa levantam questoes eticas sobre direitos autorais e dados. Artistas reclamam do uso de obras no treinamento de algoritmos. O projeto de lei de IA preve remuneracao por dados protegidos.",
  "A ANPD fiscaliza o cumprimento das regras de protecao de dados no Brasil. A autoridade aplicou as primeiras sancoes por vazamento de dados pessoais. Plataformas digitais devem relatar incidentes de seguranca em prazo curto.",
  "Especialistas defendem regulamentacao de algoritmos preditivos usados por bancos. Sistemas de credito com inteligência artificial podem discriminar clientes. A transparência dos algoritmos permite contestar decisoes automatizadas.",
  "Inteligência artificial na saude requer protocolos de seguranca e privacidade. Algoritmos de diagnostico usam dados pessoais sensiveis de pacientes. Hospitais precisam de consentimento para treinar modelos de IA com esses dados.",
  # Tema 3: habitação e cidade (8 documentos)
  "O programa habitacional amplia recursos federais para moradia popular urbana. O governo pretende contratar milhares de novas unidades habitacionais. Prefeituras disputam terrenos urbanos bem localizados para as obras.",
  "O deficit habitacional afeta milhoes de familias de baixa renda nas cidades. O aluguel caro consome boa parte da renda das familias. Coabitacao e moradia precária concentram o deficit nas periferias urbanas.",
  "Urbanizacao de favelas e regularizacao fundiária avancam em municipios brasileiros. Moradores recebem titulos de propriedade e obras de saneamento. A urbanizacao das favelas leva infraestrutura e transporte aos bairros.",
  "Moradia digna e direito social garantido pela Constituicao federal de 1988. Movimentos de moradia ocupam imoveis vazios no centro das cidades. A funcao social da propriedade urbana orienta a politica habitacional.",
  "Assentamentos precários crescem nas periferias metropolitanas do pais. Familias constroem moradias em areas de risco sem infraestrutura urbana. Deslizamentos e enchentes atingem bairros sem planejamento habitacional.",
  "O programa Minha Casa Minha Vida entrega novas unidades habitacionais populares. Conjuntos habitacionais distantes do centro dificultam o acesso ao emprego. O programa retoma a faixa de moradia para familias de baixa renda.",
  "Especialistas em urbanismo debatem alugueis abusivos nas grandes cidades. O aluguel social atende familias despejadas de suas moradias. Imoveis ociosos no centro urbano poderiam virar habitacao popular.",
  "Politicas de habitacao popular incluem regularizacao fundiária e infraestrutura urbana. Saneamento, transporte e moradia precisam de planejamento conjunto nas cidades. Municipios recebem recursos para urbanizacao de bairros periféricos.",
  # Tema 4: educação (8 documentos)
  "A educacao básica recebe novos recursos do Fundeb ampliado pelo Congresso. Redes municipais de ensino ampliam vagas em creches e escolas. A complementacao federal prioriza municipios com menor investimento por aluno.",
  "Alfabetizacao na idade certa e meta central do novo Plano Nacional de Educacao. Avaliacoes mostram atraso na leitura dos alunos apos a pandemia. Escolas recebem material didático e formacao de professores alfabetizadores.",
  "Professores debatem remuneracao e condicoes de trabalho nas escolas públicas. Salas lotadas e falta de estrutura prejudicam o ensino. Sindicatos de professores cobram plano de carreira nas redes de educacao.",
  "O ensino médio integrado ao técnico expande vagas em institutos federais. Alunos combinam formacao geral e curso profissionalizante na escola. A reforma do ensino médio redefine o curriculo e a carga horaria.",
  "A avaliação Prova Brasil mede aprendizagem em português e matemática. Os resultados do Saeb orientam metas das escolas e redes de ensino. Professores usam os indicadores de aprendizagem para planejar as aulas.",
  "Politicas de permanência estudantil ampliam bolsas para universitários pobres. Alunos de baixa renda abandonam o ensino superior por falta de apoio. Universidades públicas ampliam moradia estudantil e restaurantes para estudantes.",
  "O piso salarial nacional dos professores e reajustado pelo governo federal. Municipios alegam falta de recursos para pagar o piso do magisterio. A valorizacao dos professores e meta do Plano Nacional de Educacao.",
  "A formacao continuada de professores e desafio permanente da educacao básica. Cursos de capacitacao ajudam docentes a usar tecnologia em sala de aula. Escolas com professores bem formados apresentam melhor aprendizagem dos alunos."
)

corpus <- ac_corpus(
  data.frame(
    text = textos,
    tema = rep(c("tributário","tecnologia","habitacao","educacao"), each = 8L),
    stringsAsFactors = FALSE
  )
)
corpus
```

## 2. Preparar: limpeza e remoção de *stopwords*

**Sem remover *stopwords*, LDA vai identificar tópicos dominados por "o",
"a", "de", "que", "para".** Isso mascara os padrões reais. Removemos as
*stopwords* padrão do português e adicionamos algumas específicas do
domínio legislativo (`brasil`, `pais`) que aparecem em todos os temas.

```{r limpeza}
corpus_limpo <- ac_clean(
  corpus,
  remove_stopwords = "pt",
  extra_stopwords  = c("brasil", "pais", "sobre", "novo", "nova")
)
corpus_limpo
```

## 3. Escolher K: quantos tópicos?

LDA exige que você especifique **K** (número de tópicos) *a priori*. A
escolha ideal é um balanço:

* **K muito baixo** → tópicos misturam temas distintos (subajuste).
* **K muito alto** → tópicos fragmentam um mesmo tema (sobreajuste,
  ruído).

A métrica clássica é a **perplexidade**: o quão "surpreso" o modelo fica
com os documentos (menor = prevê melhor as palavras). O detalhe que
decide tudo é **em quais documentos** ela é medida. Calculada nos mesmos
documentos usados no ajuste, ela cai quase sempre que K aumenta, porque
mais tópicos = mais parâmetros para memorizar o corpus. Por isso Blei,
Ng & Jordan (2003) e Wallach et al. (2009) a medem em documentos **não
vistos**, e Zhao et al. (2015) usam validação cruzada em 5 partes, que é
o que `ac_lda_tune()` faz: ajusta em 4/5 dos documentos, mede no 1/5
restante, e repete 5 vezes. A coluna `perplexity_train` mostra a versão
de treino, só para comparação.

### Antes de rodar: o *prior* α importa em textos curtos

O LDA tem um hiperparâmetro α que controla **quantos tópicos cada
documento costuma misturar**: α alto empurra todo documento para uma
mistura de vários tópicos; α baixo permite que cada documento se
concentre em poucos. O padrão do Gibbs no `topicmodels` é α = 50/K
(Griffiths & Steyvers, 2004), pensado para documentos longos; com K = 4
isso dá α = 12,5, uma mistura forçada que não combina com parágrafos de
três frases sobre um único tema. Wallach, Mimno & McCallum (2009)
mostram que a escolha do *prior* muda substancialmente a qualidade dos
tópicos. Usamos aqui Gibbs com α = 0,1, passado via `control`:

```{r controle-lda}
controle <- list(alpha = 0.1, iter = 2000, burnin = 500)
```

```{r tune, eval=has_topicmodels}
tune <- ac_lda_tune(
  corpus_limpo,
  k_range = 2:8,
  seed    = 42L,
  method  = "Gibbs",
  control = controle
)
tune
```

```{r plot-tune, eval=has_topicmodels}
ac_plot_lda_tune(tune) +
  ggplot2::labs(
    title    = "Perplexidade fora da amostra por número de tópicos (K)",
    subtitle = "Validação cruzada em 5 partes; menor = melhor previsão de documentos novos",
    caption  = "acR - ac_lda_tune()"
  )
```

> **Como ler a tabela e o gráfico**
>
> 1. **Onde olhar.** Na tabela, cada linha é um K testado. Compare duas
>    colunas: `perplexity_train` (medida nos mesmos documentos do
>    ajuste) e `perplexity` (medida em documentos que o modelo não viu;
>    é a do gráfico). Menor = o modelo prevê melhor as palavras. No
>    gráfico, procure onde a curva **para de cair**. `perplexity_sd` diz
>    o quanto o valor variou entre as 5 partes da validação.
>
> 2. **O que este exemplo mostra.** A de treino cai sem parar, de ≈ 198
>    (K = 2) até ≈ 109 (K = 8). A de fora da amostra cai até K = 4 (≈
>    131) e depois fica parada entre ≈ 136 e ≈ 143. O `perplexity_sd`
>    fica entre 10 e 22.
>
> 3. **Em palavras simples.** Na versão de treino, mais tópicos sempre
>    parecem melhores, porque o modelo decora o corpus; escolher K por
>    ela levaria sempre ao maior K testado. Em documentos novos, tópicos
>    além do 4º já não ajudam a prever nada: o ganho acaba em K = 4, que
>    é o número de temas com que o corpus foi construído.
>
> 4. **Cuidado.** A curva delimita uma faixa, não um valor exato: K = 5,
>    6 e 7 ficam dentro da margem de variação (`perplexity_sd`) de K =
>    4. Perplexidade e interpretabilidade humana nem sempre concordam
>    (Chang et al., 2009); a decisão final combina a curva, a leitura
>    dos tópicos de alguns K candidatos e o conhecimento do corpus
>    (DiMaggio, Nag & Blei, 2013). Faça o teste: com o método padrão
>    (`"VEM"`), a curva fora da amostra **sobe** com K neste corpus,
>    sinal de que o ajuste precisa de atenção, não só o K.

## 4. Treinar o modelo LDA

Com K definido, ajustamos o modelo com os mesmos `method` e `control`
usados na escolha de K. `seed` é obrigatório para reprodutibilidade: LDA
é estocástico (Variational EM ou Gibbs sampling), e rodadas com sementes
diferentes retornam **tópicos com IDs diferentes** e pequenas variações
nas distribuições.

```{r lda, eval=has_topicmodels}
modelo <- ac_lda(
  corpus_limpo,
  k       = 4L,
  seed    = 42L,
  method  = "Gibbs",
  control = controle
)
modelo
```

## 5. Termos por tópico (matriz β)

Cada linha é uma combinação (tópico, termo), com `beta` = probabilidade do
termo dado o tópico. Filtramos os top termos para inspecionar o que cada
tópico "significa".

```{r beta, eval=has_topicmodels}
beta <- modelo$terms
head(beta, 10)
```

Visualização: os 10 termos com maior β por tópico:

```{r plot-topics, eval=has_topicmodels, fig.height=7}
ac_plot_lda_topics(modelo, top_n = 10L) +
  ggplot2::labs(
    title    = "Top 10 termos por tópico (LDA, K=4)",
    subtitle = "Beta = probabilidade do termo dado o tópico"
  )
```

> **Como ler este gráfico**
>
> 1. **Onde olhar.** Um painel por tópico. Em cada painel, a barra mais
>    longa (no topo) é a palavra mais provável daquele tópico (β). Leia
>    as 3 ou 4 primeiras palavras de cada painel e pergunte: "sobre o
>    que fala um texto que usa essas palavras?".
>
> 2. **O que este exemplo mostra.** Tópico 1: *moradia*, *familias*,
>    *renda*, *urbana* (habitação). Tópico 2: *professores*, *ensino*,
>    *escolas*, *educacao* (educação). Tópico 3: *dados*, com a barra
>    mais longa do gráfico, seguido de *algoritmos*, *pessoais*, *ia*
>    (IA e proteção de dados). Tópico 4: *tributária*, *reforma*,
>    *impostos*, *imposto* (reforma tributária).
>
> 3. **Em palavras simples.** Cada tópico corresponde a uma das quatro
>    agendas do corpus. O modelo não sabe os nomes dos temas: quem dá o
>    nome é você, olhando as palavras.
>
> 4. **Cuidado.** Palavras genéricas aparecem no fim de alguns painéis
>    (*precisam*, *ampliam*, *empresas*). Se elas aparecerem nas
>    **primeiras** posições de todos os tópicos, faltou removê-las como
>    *stopwords* do domínio na etapa 2. E não confunda os números:
>    "Tópico 1" não é o primeiro tema do corpus; a numeração é
>    arbitrária e muda com a semente.

O gráfico permite **rotular** os tópicos. Nomeamos com base nos termos
dominantes: se um tópico é dominado por *reforma*, *tributária*, *iva*,
*aliquotas*, chamamos de "Reforma Tributária".

Visualização alternativa: uma *word cloud* ponderada por β dentro de cada
tópico, útil em apresentações onde o eixo numérico do gráfico de barras
pesa menos que o impacto visual imediato das palavras maiores:

```{r plot-topics-alt, eval=has_topicmodels && requireNamespace("ggwordcloud", quietly = TRUE), fig.height=6}
topico_escolhido <- 1L

beta |>
  dplyr::filter(topic == topico_escolhido) |>
  dplyr::slice_max(beta, n = 25L, with_ties = FALSE) |>
  ggplot2::ggplot(ggplot2::aes(label = term, size = beta, color = beta)) +
  ggwordcloud::geom_text_wordcloud() +
  ggplot2::scale_size_area(max_size = 14) +
  ggplot2::scale_color_gradient(low = ac_palette(1), high = ac_palette(2)) +
  ggplot2::labs(
    title = paste("Nuvem de termos do tópico", topico_escolhido)
  ) +
  theme_ac()
```

## 6. Distribuição de tópicos por documento (matriz γ)

Para cada documento, quais tópicos dominam? Um documento pode ser 90%
tópico 1 (tema puro) ou distribuído entre vários tópicos (tema misto).

```{r gamma, eval=has_topicmodels}
gamma <- modelo$documents
head(gamma, 12)
```

Sumário: qual o tópico dominante de cada documento?

```{r dominante, eval=has_topicmodels}
dominante <- gamma |>
  dplyr::group_by(doc_id) |>
  dplyr::slice_max(gamma, n = 1L, with_ties = FALSE) |>
  dplyr::ungroup() |>
  dplyr::rename(topico_dominante = topic, prob = gamma)

head(dominante, 12)
```

Comparando com o **tema real** de cada documento (o `meta$tema` que
guardamos no corpus), conseguimos avaliar **quão bem o LDA recuperou a
estrutura conhecida**:

```{r validacao, eval=has_topicmodels}
# Recuperar o metadado 'tema' e cruzar com o tópico dominante
tema_real <- corpus[, c("doc_id", "tema")]
comparacao <- merge(dominante, tema_real, by = "doc_id")

# Tabela de contingência: tópico dominante x tema real
table(comparacao$topico_dominante, comparacao$tema)
```

> **Como ler esta tabela**
>
> 1. **Onde olhar.** Linhas = tópico dominante de cada documento (o de
>    maior γ); colunas = tema real, que o modelo não viu. Cada número é
>    quantos documentos caem naquela combinação. O ideal é **uma célula
>    grande por linha** (cada tópico ligado a um tema só). Como a
>    numeração dos tópicos é arbitrária, a "diagonal" pode aparecer
>    embaralhada.
>
> 2. **O que este exemplo mostra.** O Tópico 1 tem os 8 documentos de
>    `habitacao`, o Tópico 2 os 8 de `educacao`, o Tópico 3 os 8 de
>    `tecnologia` e o Tópico 4 7 dos 8 de `tributário`. O único fora do
>    lugar é um documento tributário no Tópico 1 (habitação).
>
> 3. **Em palavras simples.** O modelo recuperou os quatro temas sem ver
>    nenhum rótulo, com 31 acertos em 32 documentos.
>
> 4. **Cuidado.** O "erro" é instrutivo: é `doc_5`, um texto tributário
>    sobre *cashback* para "famílias de baixa renda". Ele divide o peso
>    quase ao meio entre os tópicos tributário e de habitação (γ ≈ 0,47
>    e ≈ 0,52) porque compartilha vocabulário com os textos de moradia.
>    É um documento genuinamente ambíguo, e a seção 8 mostra como
>    reconhecê-lo. Em corpora reais, sem tema conhecido, essa validação
>    vem da leitura humana dos tópicos e de alguns documentos de cada
>    um.

Se a diagonal for dominante (cada tópico se alinha bem com um tema real),
o modelo capturou a estrutura. Em corpora reais sem "rótulo verdadeiro",
essa validação vem da **interpretação humana** dos tópicos e da
concordância entre codificadores.

## 7. Interpretação e nomeação dos tópicos

A prática consolidada em análise de conteúdo com LDA (DiMaggio et al.,
2013): apresente os top-N termos por tópico, proponha um **rótulo
substantivo** e valide com pelo menos um segundo pesquisador. O `acR`
facilita gerar essa tabela para incluir no artigo:

```{r rotulos, eval=has_topicmodels}
top_terms_por_topico <- beta |>
  dplyr::group_by(topic) |>
  dplyr::slice_max(beta, n = 7L, with_ties = FALSE) |>
  dplyr::summarise(
    top_termos = paste(term, collapse = ", "),
    .groups    = "drop"
  )

top_terms_por_topico
```

## 8. Visualizando as misturas: γ como heatmap

Uma das forças didáticas do LDA é que γ **não é** uma etiqueta: é uma
proporção. Documentos que "quase pertencem a um tema" aparecem como um
gradiente, não como uma célula preenchida. Este heatmap mostra os
documentos como linhas, os tópicos como colunas, e a cor como
`gamma` (0 = nada; 1 = totalidade da massa).

```{r gamma-heatmap, eval=has_topicmodels, fig.height=6}
gamma_ord <- gamma |>
  # ordem numérica (doc_1 no topo), para os 4 temas aparecerem em blocos
  dplyr::mutate(doc_id = factor(doc_id, levels = paste0("doc_", 32:1)))

ggplot2::ggplot(
  gamma_ord,
  ggplot2::aes(x = factor(topic), y = doc_id, fill = gamma)
) +
  ggplot2::geom_tile(color = "white") +
  ggplot2::scale_fill_gradient(
    low  = "#F1F5F9",
    high = ac_palette(1),
    name = "gamma"
  ) +
  ggplot2::labs(
    title    = "Composicao tematica de cada documento (matriz gamma)",
    subtitle = "Cores mais fortes = maior peso do tópico no documento",
    x        = "Tópico",
    y        = "Documento",
    caption  = "acR - LDA soft assignment"
  ) +
  theme_ac() +
  ggplot2::theme(
    axis.text.y = ggplot2::element_text(size = 7)
  )
```

> **Como ler este gráfico**
>
> 1. **Onde olhar.** Uma linha por documento (`doc_1` no topo), uma
>    coluna por tópico. Cor escura = o tópico ocupa quase todo o
>    documento; cinza médio = o documento está dividido. Como cada linha
>    soma 1, procure linhas com **duas células cinzas** em vez de uma
>    escura: são os documentos mistos.
>
> 2. **O que este exemplo mostra.** Os 32 documentos formam 4 blocos
>    escuros, um por tema: `doc_1` a `doc_8` no Tópico 4, `doc_9` a
>    `doc_16` no 3, `doc_17` a `doc_24` no 1 e `doc_25` a `doc_32` no 2.
>    Destoam poucas linhas: `doc_5` (dividido entre os Tópicos 1 e 4),
>    `doc_17` (entre 1 e 2) e, em menor grau, `doc_6`, `doc_23` e
>    `doc_30`.
>
> 3. **Em palavras simples.** Quase todo documento fala de um assunto
>    só. Os poucos divididos são textos que usam palavras de dois temas
>    (`doc_17`, sobre programa habitacional, fala de "recursos
>    federais", vocabulário que também aparece nos textos de educação).
>
> 4. **Cuidado.** Não há um limiar universal para dizer que um documento
>    "pertence" a um tópico. A prática usual é reportar o tópico de
>    maior γ e marcar como ambíguos os documentos em que o segundo
>    tópico chega perto do primeiro. A proporção de documentos puros
>    também depende do α escolhido na seção 3: α baixo favorece
>    documentos concentrados (Wallach, Mimno & McCallum, 2009).

Neste corpus, a maioria dos documentos é quase "pura": a γ do tópico
principal tem mediana ≈ 0,98. Só 5 dos 32 documentos dão mais de 0,20 a
um segundo tópico, e em dois deles (`doc_5` e `doc_17`) os dois tópicos
principais estão quase empatados (≈ 0,52 × 0,47 e ≈ 0,54 × 0,45): são
esses que mereceriam a etiqueta de "ambíguos". Isso é coerente com o α
baixo escolhido na seção 3, que favorece documentos concentrados em
poucos tópicos, e com textos escritos, cada um, sobre um único tema. Em
discursos longos reais, espere mais linhas com massa espalhada.

Compare com o heatmap "hard clustering" da `vignette("cluster")` §4.6:
lá, cada linha tem **uma** célula acesa (pertence ao grupo A **ou** ao
B), mesmo para documentos como `doc_5`, cuja ambiguidade a γ revela e a
etiqueta única esconde. Tomar o tópico de maior γ de cada documento é
justamente transformar a descrição contínua numa partição.

## 9. Exportar

Salvamos as duas matrizes principais para reuso: em outros scripts, no
Excel, ou para colar em tabelas de artigo:

```{r exportar, eval=FALSE}
ac_export(beta,  arquivo = "lda_beta.csv",  formato = "csv")
ac_export(gamma, arquivo = "lda_gamma.csv", formato = "csv")

# Tabela pronta para LaTeX
ac_export(top_terms_por_topico, arquivo = "lda_topicos.tex", formato = "latex")

# Objeto R serializado (recarregavel via readRDS)
saveRDS(modelo, "lda_modelo.rds")
```

## Referências

* Blei, D. M.; Ng, A. Y.; Jordan, M. I. (2003). Latent Dirichlet
  Allocation. *Journal of Machine Learning Research*, 3, 993-1022.
* DiMaggio, P.; Nag, M.; Blei, D. (2013). Exploiting affinities between
  topic modeling and the sociological perspective on culture: Application
  to newspaper coverage of U.S. government arts funding. *Poetics*, 41(6),
  570-606.
* Hong, L.; Davison, B. D. (2010). Empirical study of topic modeling in
  Twitter. *Proceedings of the First Workshop on Social Media
  Analytics*, 80-88.
* Yan, X.; Guo, J.; Lan, Y.; Cheng, X. (2013). A biterm topic model for
  short texts. *Proceedings of the 22nd International Conference on
  World Wide Web*, 1445-1456.
* Griffiths, T. L.; Steyvers, M. (2004). Finding scientific topics.
  *Proceedings of the National Academy of Sciences*, 101(suppl. 1),
  5228-5235.
* Wallach, H. M.; Mimno, D.; McCallum, A. (2009). Rethinking LDA: Why
  priors matter. *Advances in Neural Information Processing Systems*, 22,
  1973-1981.
* Chang, J.; Gerrish, S.; Wang, C.; Boyd-Graber, J.; Blei, D. M. (2009).
  Reading tea leaves: How humans interpret topic models. *Advances in
  Neural Information Processing Systems*, 22, 288-296.
* Wallach, H. M.; Murray, I.; Salakhutdinov, R.; Mimno, D. (2009).
  Evaluation methods for topic models. *Proceedings of the 26th
  International Conference on Machine Learning*, 1105-1112.
* Zhao, W. et al. (2015). A heuristic approach to determine an appropriate
  number of topics in topic modeling. *BMC Bioinformatics*, 16(S13).
* Silge, J.; Robinson, D. (2017). *Text Mining with R*. O'Reilly.
