Esta vignette percorre o pipeline completo do acR em um
exemplo pequeno mas realista (discursos parlamentares favoráveis e
contrários a uma reforma), mostrando o output efetivo de cada
etapa. Se você prefere um tour de 5 minutos com só o essencial, veja o
Quickstart.
ac_corpusTudo no acR gira em torno do ac_corpus, um
tibble com colunas padronizadas doc_id e
text que carrega quaisquer metadados adicionais. As demais
funções aceitam esse objeto diretamente.
df <- data.frame(
id = paste0("d", 1:8),
texto = c(
"Sou favoravel a reforma tributária: simplifica o sistema e reduz distorcoes.",
"Voto contra: essa reforma vai destruir o setor produtivo brasileiro.",
"Apoio a proposta com forte adesao, ha ganhos claros de eficiência arrecadatória.",
"Rejeito o texto: transfere renda das familias para grandes corporacoes.",
"Defendo a reforma que corrige as distorcoes historicas do nosso sistema.",
"Somos contrários: o projeto beneficia apenas os mais ricos do pais.",
"Voto sim, precisamos modernizar urgentemente a estrutura tributária.",
"Voto não, os pequenos empresários serao os grandes prejudicados."
),
partido = c("PT","PL","PT","PL","PT","PL","PT","PL"),
posicao = c("favor","contra","favor","contra","favor","contra","favor","contra"),
stringsAsFactors = FALSE
)
corpus <- ac_corpus(df, text = texto, docid = id, meta = c(partido, posicao))
corpus
#>
#> ── Corpus acR ──────────────────────────────────────────────────────────────────
#> • Documentos: 8
#> • Metadados: 2 colunas
#> • Idioma: "pt"
#>
#> # A tibble: 8 × 4
#> doc_id text partido posicao
#> <chr> <chr> <chr> <chr>
#> 1 d1 Sou favoravel a reforma tributária: simplifica o siste… PT favor
#> 2 d2 Voto contra: essa reforma vai destruir o setor produti… PL contra
#> 3 d3 Apoio a proposta com forte adesao, ha ganhos claros de… PT favor
#> 4 d4 Rejeito o texto: transfere renda das familias para gra… PL contra
#> 5 d5 Defendo a reforma que corrige as distorcoes historicas… PT favor
#> 6 d6 Somos contrários: o projeto beneficia apenas os mais r… PL contra
#> # ℹ 2 more rowsAntes de contar, precisamos remover stopwords. Sem
essa etapa, os termos mais frequentes seriam o,
a, de, que: carregam pouca
informação temática e mascaram os padrões reais do corpus.
corpus_limpo <- ac_clean(corpus, remove_stopwords = "pt")
# Frequência global
freq <- ac_count(corpus_limpo)
ac_top_terms(freq, n = 8)
#> # A tibble: 8 × 2
#> token n
#> <chr> <int>
#> 1 reforma 3
#> 2 voto 3
#> 3 distorcoes 2
#> 4 grandes 2
#> 5 sistema 2
#> 6 tributária 2
#> 7 adesao 1
#> 8 apenas 1Agora quebrando por lado (favor vs
contra):
freq_lado <- ac_count(corpus_limpo, by = "posicao")
ac_top_terms(freq_lado, n = 5, by = "posicao")
#> # A tibble: 10 × 3
#> posicao token n
#> <chr> <chr> <int>
#> 1 contra grandes 2
#> 2 contra voto 2
#> 3 contra apenas 1
#> 4 contra beneficia 1
#> 5 contra brasileiro 1
#> 6 favor distorcoes 2
#> 7 favor reforma 2
#> 8 favor sistema 2
#> 9 favor tributária 2
#> 10 favor adesao 1Quais palavras aparecem muito mais em um grupo do
que no outro? A métrica chi2 (χ²) mede essa
distintividade.
key <- ac_keyness(freq_lado, group = "posicao", target = "favor")
head(key, 6)
#> # A tibble: 6 × 10
#> token group target reference n_target n_reference total_target total_reference
#> <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl>
#> 1 dist… posi… favor contra 2 0 29 27
#> 2 sist… posi… favor contra 2 0 29 27
#> 3 trib… posi… favor contra 2 0 29 27
#> 4 ades… posi… favor contra 1 0 29 27
#> 5 apoio posi… favor contra 1 0 29 27
#> 6 arre… posi… favor contra 1 0 29 27
#> # ℹ 2 more variables: keyness <dbl>, direction <chr>Visualização compacta dos termos mais distintivos:
Como ler este gráfico
Onde olhar. Cada barra é uma palavra. Barra para a direita (verde-azulada) = mais usada nos discursos
favor; para a esquerda (vermelha) = mais usada noscontra. Quanto mais comprida, maior a diferença entre os lados. Confira os números na tabelakeylogo acima:n_target(vezes emfavor) en_reference(vezes emcontra).O que este exemplo mostra. Do lado favorável, as três barras mais longas são distorcoes, sistema e tributária (2 vezes em
favor, nenhuma emcontra). Do lado contrário, a barra mais longa de todo o gráfico é grandes (0 contra 2: “grandes corporacoes”, “grandes prejudicados”). As outras barras, mais curtas e todas iguais, são palavras que aparecem uma vez só.Em palavras simples. Quem defende a reforma fala do sistema tributário e de corrigir distorções; quem é contra fala de quem ganha e quem perde (os “grandes”).
Cuidado. Com 8 discursos curtos, uma diferença de 2 contra 0 é pouca evidência: leia o gráfico como pista para o que ler nos textos, não como resultado estatístico (Dunning, 1993). Quando várias palavras empatam (as barras curtas), o gráfico mostra as primeiras em ordem alfabética: a escolha entre elas não tem significado.
Escore agregado de polaridade de cada documento, usando o léxico OpLexicon (Souza e Vieira, 2012):
sent <- ac_sentiment(corpus)
sent
#> # A tibble: 8 × 6
#> doc_id n_pos n_neg n_neu score sentiment
#> <chr> <int> <int> <int> <int> <chr>
#> 1 d1 1 0 10 1 positivo
#> 2 d2 1 0 9 1 positivo
#> 3 d3 2 0 10 2 positivo
#> 4 d4 0 0 10 0 neutro
#> 5 d5 0 0 11 0 neutro
#> 6 d6 1 1 9 0 neutro
#> 7 d7 1 0 7 1 positivo
#> 8 d8 0 1 8 -1 negativoVisualizando por documento:
Como ler este gráfico
Onde olhar. Cada barra é um discurso; o comprimento é o escore (palavras positivas menos negativas do léxico OpLexicon). Para a direita (verde) = positivo; para a esquerda (laranja) = negativo; sem barra = zero. Compare com a posição declarada de cada discurso, que você conhece pela tabela do corpus.
O que este exemplo mostra.
d3tem o maior escore (+2: “forte” e “claros”). O caso que mais ensina éd2, um discurso contra (“Voto contra: essa reforma vai destruir o setor produtivo”) que sai positivo: o léxico conta “produtivo” como +1 e não tem nenhuma palavra negativa para “destruir”.d6(contra) sai zero porque “ricos” conta +1 e anula “contrários” (−1).Em palavras simples. O léxico não lê a frase, ele soma palavras soltas. Quando a opinião está num verbo (“destruir”) ou quando uma palavra positiva aparece num contexto negativo (“setor produtivo” sendo destruído), o escore erra a direção.
Cuidado. O escore é relativo ao léxico, não uma medida de “quão positivo é o texto” (Taboada et al., 2011). Neste corpus, até “tributária” conta como +1, um erro de domínio. Antes de usar o escore, compare-o com uma amostra lida por humanos; a
vignette("sentimento")mostra esses erros em detalhe.
Antes de codificar qualitativamente, o acR recomenda
modelos com base em custo, idioma e tipo de tarefa. Consulta 100 %
offline:
ac_qual_recommend_model(task = "coding", budget = "medium", lang = "pt", n = 3)
#>
#> ── Recomendacoes de modelo acR ─────────────────────────────────────────────────
#> ℹ Tarefa: "coding" | Budget: "medium" | Idioma: "pt"
#> ℹ Baseado em Gilardi et al. (2023, PNAS) e Tornberg (2023, PLOS ONE).
#> # A tibble: 3 × 12
#> rank provider model_id name tier context_k cost_input cost_output
#> <int> <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl>
#> 1 1 google google/gemini-2.0… Gemi… fast 1000 0.1 0.4
#> 2 2 google google/gemini-2.5… Gemi… fron… 1000 1.25 10
#> 3 3 openai openai/o4-mini o4-m… bala… 200 1.1 4.4
#> # ℹ 4 more variables: pt_support <chr>, score <dbl>, justificativa <chr>,
#> # acr_string <chr>Base: Gilardi, Alizadeh e Kubli (2023) e Törnberg (2023).
Esta etapa exige chave de API (ANTHROPIC_API_KEY,
GROQ_API_KEY, etc.) e por isso não roda na construção da
vignette. Estrutura mínima:
codebook <- ac_qual_codebook(
name = "posicionamento",
instructions = "Classifique o posicionamento sobre a reforma tributária.",
categories = list(
favor = list(
definition = "Manifestação favorável à reforma.",
examples_pos = "Sou favoravel a reforma, simplifica o sistema."
),
contra = list(
definition = "Manifestação contrária à reforma.",
examples_pos = "Voto contra, vai destruir o setor produtivo."
)
)
)
codificado <- ac_qual_code(
corpus = corpus,
codebook = codebook,
model = "anthropic/claude-sonnet-4-5"
)codificado é um tibble com doc_id,
categoria, confidence_score (via
self-consistency) e reasoning.
# Amostra aleatória (representativa): é ela que estima a confiabilidade
# do corpus. Amostrar só os casos incertos subestimaria a concordância.
amostra <- ac_qual_sample(codificado, n = 50, strategy = "random")
# Exporta planilha para revisão humana
ac_qual_export_for_review(amostra, path = "revisao.xlsx", corpus = corpus)
# Após preencher, reimporta e calcula IRR
humano <- ac_qual_import_human("revisao.xlsx")
ac_qual_reliability(llm = codificado, human = humano)Um exemplo do formato do output com dados sintéticos (equivalente ao que sai com codificação real):
llm_sim <- tibble::tibble(
doc_id = paste0("d", 1:8),
categoria = c("favor","contra","favor","contra","favor","contra","favor","contra")
)
humano_sim <- tibble::tibble(
doc_id = paste0("d", 1:8),
categoria = c("favor","contra","favor","contra","favor","favor","favor","contra")
# 1 discordância em 8 casos -> ~87.5% de concordância
)
ac_qual_reliability(llm = llm_sim, human = humano_sim, bootstrap = 50)
#> Calculando confiabilidade em 8 documentos comuns...
#> ℹ Interpretação: alfa por Krippendorff (2018); AC1 por Landis & Koch (1977) e
#> Gwet (2014); percentual por Neuendorf (2017); F1 sem rótulo.
#> ℹ IC 95% via bootstrap (n = 50).
#> # A tibble: 4 × 5
#> metric estimate ci_lower ci_upper interpretation
#> <chr> <dbl> <dbl> <dbl> <chr>
#> 1 percent_agreement 0.875 0.528 1 aceitável na maioria dos casos …
#> 2 krippendorff_alpha 0.762 0.286 1 só conclusões provisórias (>= 0…
#> 3 gwet_ac1 0.754 0.280 1 substancial (Landis & Koch, 197…
#> 4 f1_macro 0.873 0.564 1 sem faixa canônica (não corrigi…As métricas incluem percent agreement, alpha de Krippendorff, AC1 de Gwet e F1 macro, com IC via bootstrap e interpretação segundo Landis e Koch (1977) e Gwet (2014).
ellmer,
self-consistency, tradução, fusão.DUNNING, T. Accurate methods for the statistics of surprise and coincidence. Computational Linguistics, v. 19, n. 1, p. 61-74, 1993.
GILARDI, F.; ALIZADEH, M.; KUBLI, M. ChatGPT outperforms crowd workers for text-annotation tasks. PNAS, v. 120, n. 30, 2023.
GWET, K. L. Handbook of inter-rater reliability. 4. ed. Gaithersburg: Advanced Analytics, 2014.
KRIPPENDORFF, K. Content analysis: an introduction to its methodology. 4. ed. Thousand Oaks: SAGE, 2018.
LANDIS, J. R.; KOCH, G. G. The measurement of observer agreement for categorical data. Biometrics, v. 33, n. 1, p. 159-174, 1977.
SOUZA, M.; VIEIRA, R. Sentiment analysis on Twitter with Portuguese language. STIL/SBC, 2012.
TABOADA, M. et al. Lexicon-based methods for sentiment analysis. Computational Linguistics, v. 37, n. 2, p. 267-307, 2011.
TÖRNBERG, P. ChatGPT-4 outperforms experts and crowd workers in annotating political Twitter messages with zero-shot learning. PLOS ONE, v. 18, n. 4, 2023.