--- title: "Analise quantitativa de texto" output: rmarkdown::html_vignette: toc: true toc_depth: 2 vignette: > %\VignetteIndexEntry{Analise quantitativa de texto} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include=FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.width = 8.5, fig.height = 5.5, fig.align = "center", out.width = "100%", dpi = 140 ) ``` Esta vignette percorre o **módulo quantitativo** do `acR` em cima de um corpus de discursos parlamentares fabricado para o exemplo. Cada etapa é apresentada com uma pergunta metodológica — *por que faço isso?* — antes do código e do gráfico. O caminho é o clássico da análise de conteúdo estatística: **corpus → limpeza → tokenização → frequências → distintividade → visualização**. ## Qual métrica responde qual pergunta? O módulo quantitativo tem seis técnicas centrais. Aprender qual usar em cada situação vale mais que decorar sintaxe: | Pergunta | Técnica | Função | |----------------------------------------------------------------------|-----------------|--------------------------------------------| | Que palavras dominam o corpus como um todo? | Frequência bruta| `ac_count()` → `ac_top_terms()` | | Que palavras são **distintivas** de cada documento vs. o resto? | TF-IDF | `ac_tf_idf()` → `ac_plot_tf_idf()` | | Que palavras separam **um grupo** do outro estatisticamente? | *Keyness* | `ac_keyness()` → `ac_plot_keyness()` | | Que palavras **andam juntas** (associação sintagmática)? | Coocorrência | `ac_cooccurrence()` → `ac_plot_cooccurrence()` | | **Onde no texto** um termo aparece com mais frequência? | X-ray plot | `ac_plot_xray()` | | Panorama visual rápido para relatório ou apresentação | Nuvem de palavras | `ac_wordcloud()` (`ggwordcloud`) | **Não é redundância.** As três métricas de frequência respondem perguntas diferentes: - **Frequência bruta** conta ocorrências. É a primeira olhada, mas os termos no topo tendem a ser genéricos (mesmo depois de tirar *stopwords*). - **TF-IDF** penaliza termos que aparecem em muitos documentos: destaca o vocabulário **próprio** de cada texto. Sem grupo definido a priori. - **Keyness** compara **grupos** com um teste estatístico (χ² ou log-likelihood). Precisa de uma variável categórica no corpus. Para descobrir tipologias emergentes sem categoria a priori, veja `vignette("cluster")` e `vignette("lda")` (não supervisionado). Para rotular com categorias teóricas, veja `vignette("qualitativo-llm")` (assistido). ```{r pkg} library(acR) ``` ## 1. Construir o corpus O `ac_corpus()` padroniza qualquer `data.frame` em um objeto com colunas fixas `doc_id` e `text`, mantendo os metadados que você quiser preservar para comparações posteriores. ```{r corpus} textos <- c( "O governo anunciou nova politica fiscal para reduzir o deficit publico.", "A oposicao critica o aumento dos gastos e da divida publica no orcamento.", "Parlamentares debatem a reforma tributaria e o imposto de renda das familias.", "O presidente vetou o projeto que ampliava beneficios sociais para os pobres.", "O Senado aprovou o marco legal para investimentos em infraestrutura logistica.", "Deputados votam pela reducao da carga tributaria para pequenas empresas.", "A politica monetaria do Banco Central eleva a taxa de juros para conter inflacao.", "A inflacao acima da meta preocupa economistas e o mercado financeiro.", "O relator defendeu emendas ao texto do orcamento com foco em saude e educacao.", "A comissao aprovou o projeto que amplia beneficios sociais no interior.", "O ministro afirmou que a reforma tributaria simplifica o sistema para empresas.", "Lideres da oposicao pediram vistas ao projeto na comissao de constituicao." ) meta <- data.frame( text = textos, tema = c("fiscal","fiscal","tributario","social","infraestrutura", "tributario","monetario","monetario","fiscal","social", "tributario","fiscal"), stringsAsFactors = FALSE ) corpus <- ac_corpus(meta) corpus ``` ## 2. Limpeza: por que remover *stopwords*? Sem remoção de *stopwords*, os termos mais frequentes de qualquer corpus em português são artigos, preposições e conjunções (`o`, `a`, `de`, `que`, `para`...). Eles carregam pouquíssima informação temática e sufocam qualquer padrão real de vocabulário. A regra prática em análise de conteúdo (Sampaio & Lycarião, 2021) é sempre remover *stopwords* antes de contar frequências ou calcular distintividade. O `ac_clean()` aceita o parâmetro `remove_stopwords = "pt"` para aplicar uma lista de *stopwords* portuguesas curada. Você pode adicionar seu próprio glossário via `extra_stopwords`. ```{r limpeza} corpus_limpo <- ac_clean( corpus, remove_stopwords = "pt", extra_stopwords = c("pra", "pro") # opcional: coloquiais ) corpus_limpo ``` ## 3. Tokenização `ac_tokenize()` quebra cada documento em unidades de análise. Com `n = 1L` são unigramas (palavras); com `n = 2L`, bigramas ("reforma tributária"). Bigramas são úteis quando você quer capturar **expressões compostas** que perdem sentido tokenizadas separadamente. ```{r tokenizacao} tokens <- ac_tokenize(corpus_limpo, n = 1L) head(tokens, 12) ``` ## 4. Frequência Contagem de ocorrências por documento e depois agregação. `ac_count()` já retorna no formato *tidy* pronto para pipeline com `dplyr` e `ggplot2`. ```{r frequencia} contagem <- ac_count(corpus_limpo) contagem ``` ## 5. Top termos: o vocabulário do corpus O gráfico abaixo mostra as **15 palavras mais frequentes** — agora, com *stopwords* removidas, é possível ler os *temas* dominantes: reforma, projeto, tributária, orçamento, etc. ```{r top-termos, fig.height=6} top <- ac_top_terms(contagem, n = 15) ac_plot_top_terms(top) + ggplot2::labs( title = "Termos mais frequentes no corpus", subtitle = "Discursos legislativos - 12 documentos fabricados", caption = "acR - vignette Analise Quantitativa" ) ``` ## 6. Nuvem de palavras Uma nuvem funciona como **panorama visual** rápido, útil para relatórios e apresentações. Não substitui análise numérica: o tamanho da palavra é proporcional à frequência, mas a posição no plot é aleatória. Desde a versão 0.3.1 o `ac_wordcloud()` prefere automaticamente o motor `ggwordcloud` (retorna um `ggplot`, com tipografia mais legível e layout consistente com o tema do pacote); se ele não estiver instalado, cai para o `wordcloud` clássico. Você pode forçar via `backend = "ggwordcloud"` ou `backend = "wordcloud"`. ```{r wordcloud, fig.height=6} ac_wordcloud(contagem, max_words = 40, title = "Panorama do corpus") ``` ### 6.1 Nuvem comparativa entre grupos Quando o corpus tem uma variável de agrupamento (partido, período, tema, etc.), a nuvem comparativa mostra lado a lado os termos mais **distintivos** de cada grupo — pontuados por TF-IDF calculado tratando cada grupo como um "documento". Aceita 2, 3 ou mais grupos. ```{r wordcloud-comp, fig.height=6} # Nosso corpus tem 5 temas distintos — todos entram na comparativa: ac_plot_wordcloud_comparative( corpus, group = tema, max_words = 25, title = "Termos distintivos por tema" ) ``` O layout usa `ggwordcloud` com facets quando disponível (`backend = "auto"`), e cai em `geom_text` com jitter reproduzível quando não. A semente do posicionamento é controlada por `seed = 42L`. Cores vêm de `ac_palette()` por padrão — uma por grupo. ## 7. TF-IDF: o que é *distintivo* em cada documento? Frequência bruta favorece palavras que aparecem em quase todos os documentos (mesmo depois de *stopwords*). O **TF-IDF** (*term frequency × inverse document frequency*) penaliza termos comuns e destaca vocabulário próprio de cada grupo — útil quando o corpus é composto por textos com propostas ou temas distintos. Com **12 discursos curtos** e cada palavra aparecendo em 1–2 documentos, o TF-IDF *por documento* resulta em barras quase todas iguais (todo termo único vale o mesmo). O padrão fica mais legível **agregando por tema**: tratamos cada tema como um "documento" e o TF-IDF revela o vocabulário característico de cada eixo temático do corpus. ```{r tfidf, fig.height=6} freq_tema <- ac_count(corpus_limpo, by = "tema") tfidf_tema <- ac_tf_idf(freq_tema, by = "tema") ac_plot_tf_idf(tfidf_tema, by = "tema", n = 6) + ggplot2::labs( title = "Termos mais distintivos por tema (TF-IDF)", subtitle = "Cada eixo temático como um documento; alta pontuação = termo característico do tema" ) ``` ## 8. *Keyness*: o que é distintivo entre **grupos**? Enquanto TF-IDF compara documento vs. corpus, ***keyness*** compara **grupos** — e o critério que define o grupo é seu, não do pacote. Pode ser partido, período histórico, tema, região, autor, condição experimental, faixa etária, qualquer variável categórica no seu corpus. É a métrica clássica para a pergunta comparativa: *"o que um grupo diz que o outro não diz?"*. Aqui comparamos os temas **fiscal** e **tributário** — quais palavras marcam cada lado? ```{r keyness, fig.height=6} freq_tema <- ac_count(corpus_limpo, by = "tema") freq_2grupos <- freq_tema[freq_tema$tema %in% c("fiscal", "tributario"), ] kn <- ac_keyness(freq_2grupos, group = "tema", target = "fiscal") ac_plot_keyness(kn, n = 10) + ggplot2::labs( title = "Palavras distintivas: 'fiscal' vs. 'tributario'", subtitle = "Barras positivas = distintivas do tema 'fiscal'" ) ``` Interpretação: chi-quadrado alto indica **sobre-representação estatística**. Termos como *deficit* e *orçamento* marcam o eixo fiscal; *carga* e *renda* o tributário — resultado consistente com o corpus de exemplo. ## 9. Rede de co-ocorrência Palavras que aparecem próximas com frequência formam **agrupamentos semânticos**. A rede de coocorrência mostra essas vizinhanças com base numa **janela** de tokens (aqui, 5 palavras à esquerda e à direita). Útil para: identificar frames, mapear associações conceituais e encontrar combinações de palavras que definem sub-temas dentro do corpus. ```{r coocorrencia, fig.height=7} cooc <- ac_cooccurrence(corpus_limpo, window = 5L, min_count = 2L) if (requireNamespace("ggraph", quietly = TRUE)) { ac_plot_cooccurrence(cooc, top_n = 25) + ggplot2::labs( title = "Rede de coocorrencia (janela = 5, freq minima = 2)", subtitle = "Arestas conectam termos que co-ocorrem no mesmo contexto" ) } ``` ### 9.1 Trocando o peso: PMI em vez de contagem A rede padrão pondera arestas pela **contagem** de coocorrências — termos frequentes tendem a dominar. O **PMI** (*pointwise mutual information*) mede o quanto duas palavras aparecem juntas *acima do esperado por acaso*; revela pares surpreendentes, mesmo que raros. ```{r coocorrencia-pmi, fig.height=7} if (requireNamespace("ggraph", quietly = TRUE)) { ac_plot_cooccurrence(cooc, top_n = 25, weight = "pmi") + ggplot2::labs( title = "Rede de coocorrencia ponderada por PMI", subtitle = "Destaca pares associados alem do esperado por acaso" ) } ``` ### 9.2 Trocando o layout O layout controla como os nós são posicionados no plano. O padrão `"fr"` (Fruchterman-Reingold) minimiza cruzamentos; `"kk"` (Kamada-Kawai) respeita distâncias de grafo; `"circle"` alinha tudo num anel — útil para corpora pequenos onde a topologia importa menos que a legibilidade. ```{r coocorrencia-circle, fig.height=7} if (requireNamespace("ggraph", quietly = TRUE)) { ac_plot_cooccurrence(cooc, top_n = 25, layout = "circle") + ggplot2::labs( title = "Rede de coocorrencia — layout circular", subtitle = "Todas as palavras num anel; arestas mostram o grafo cru" ) } ``` ### 9.3 Coocorrência como matriz (heatmap) Quando a rede fica saturada de arestas, um **heatmap** das top-N palavras pode ser mais legível: linhas e colunas são termos, e a intensidade da célula é a coocorrência. Não requer `ggraph`. ```{r coocorrencia-heatmap, fig.height=6} top_tokens <- ac_top_terms(ac_count(corpus_limpo), n = 12)$token cooc_mat <- cooc[cooc$word1 %in% top_tokens & cooc$word2 %in% top_tokens, ] ggplot2::ggplot(cooc_mat, ggplot2::aes(word1, word2, fill = cooc)) + ggplot2::geom_tile(color = "white") + ggplot2::scale_fill_gradient(low = "#F1F5F9", high = ac_palette(1)) + ggplot2::labs( title = "Matriz de coocorrencia (top 12 termos)", subtitle = "Alternativa ao grafo quando a rede fica densa", x = NULL, y = NULL, fill = "cooc." ) + theme_ac() + ggplot2::theme( axis.text.x = ggplot2::element_text(angle = 45, hjust = 1) ) ``` ## Referências * Krippendorff, K. (2018). *Content Analysis: An Introduction to Its Methodology*. 4. ed. SAGE. * Sampaio, R. C.; Lycarião, D. (2021). *Análise de conteúdo categorial: manual de aplicação*. ENAP. * Silge, J.; Robinson, D. (2017). *Text Mining with R*. O'Reilly.