Introdução

As conferências de processamento de linguagem natural dependem de revisões por pares para selecionar artigos sólidos, relevantes e com impacto potencial para a comunidade. A coleção PeerRead disponibiliza essas avaliações com o objetivo de fomentar pesquisas sobre transparência, vieses e qualidade do processo de revisão. Nesta análise, utilizamos os dados da trilha CoNLL 2016, localizados em data/conll_2016, para entender padrões de recomendação, consistência entre revisores e características textuais dos pareceres.

O objetivo é responder perguntas como: “Quais notas de recomendação são mais frequentes em cada partição (train/dev/test)?”, “Existe relação entre a confiança do revisor e a nota atribuída?”, “Como as rubricas analíticas (clareza, originalidade, impacto) se relacionam com a recomendação final?” e “Há diferenças léxicas entre comentários favoráveis e desfavoráveis?”.

Além de contextualizar o processo, também apresentamos exemplos visuais (trechos de pareceres e histogramas) para apoiar discussões sobre transparência e feedback para autores. O foco recai em métricas numéricas publicadas nos JSONs de revisão e na exploração textual dos comentários.

Pacotes Requeridos

Pacote Propósito
tidyverse Manipulação de dados (dplyr, tidyr), strings e visualizações básicas
purrr Iteração funcional para varrer centenas de arquivos JSON
jsonlite Leitura e parsing dos arquivos de revisão (reviews/*.json)
stringr Tratamento e contagem de palavras nos comentários
tidytext Tokenização de texto e remoção de stopwords
plotly Criação de gráficos interativos para explorar métricas
rmdformats Tema Material Design aplicado ao relatório
DT Tabelas interativas com filtragem e paginação
lubridate Conversão de datas (quando presentes em metadados adicionais)
scales Formatação de números e percentuais nos gráficos

Preparação dos Dados

Import dos dados

Como obter os dados

Os arquivos de avaliações por pares fazem parte da coleção PeerRead e estão organizados localmente em data/conll_2016, separando train, dev e test. Cada pasta contém subdiretórios reviews/, pdfs/ e parsed_pdfs/. Nesta análise usamos apenas os JSONs de reviews/, que armazenam metadados de cada artigo e um array de pareceres.

Objetivo dos dados

Cada arquivo corresponde a um artigo submetido ao CoNLL 2016. Os campos principais incluem title, abstract, id e uma lista reviews, onde cada elemento descreve o comentário de um avaliador, notas para rubricas como CLARITY, SOUNDNESS_CORRECTNESS, ORIGINALITY, além da nota de recomendação (RECOMMENDATION) e a confiança (REVIEWER_CONFIDENCE).

reviews_dir <- "/home/paula/Documentos/mestrado/PeerRead/data/conll_2016"
available_splits <- c("train", "dev", "test")

`%||%` <- function(a, b) {
    if (is.null(a) || length(a) == 0) {
        return(b)
    }
    a
}

safe_numeric <- function(x) {
    if (is.null(x) || length(x) == 0) {
        return(NA_real_)
    }
    suppressWarnings(as.numeric(x))
}

read_review_file <- function(path, split) {
    paper <- fromJSON(path, simplifyVector = FALSE)
    review_entries <- paper$reviews
    if (length(review_entries) == 0) {
        return(tibble())
    }

    map_dfr(seq_along(review_entries), function(idx) {
        review <- review_entries[[idx]]
        tibble(
            paper_id = paper$id %||% tools::file_path_sans_ext(basename(path)),
            split = split,
            title = paper$title %||% NA_character_,
            abstract = paper$abstract %||% NA_character_,
            review_index = idx,
            comments = review$comments %||% NA_character_,
            is_meta_review = isTRUE(review$is_meta_review),
            recommendation = safe_numeric(review$RECOMMENDATION),
            clarity = safe_numeric(review$CLARITY),
            soundness = safe_numeric(review$SOUNDNESS_CORRECTNESS),
            originality = safe_numeric(review$ORIGINALITY),
            impact = safe_numeric(review$IMPACT),
            substance = safe_numeric(review$SUBSTANCE),
            meaningful_comparison = safe_numeric(review$MEANINGFUL_COMPARISON),
            appropriateness = safe_numeric(review$APPROPRIATENESS),
            replicability = safe_numeric(review$REPLICABILITY),
            reviewer_confidence = safe_numeric(review$REVIEWER_CONFIDENCE),
            presentation_format = review$PRESENTATION_FORMAT %||% NA_character_
        )
    })
}

reviews_raw <- map_dfr(
    available_splits,
    function(split) {
        files <- list.files(
            file.path(reviews_dir, split, "reviews"),
            pattern = "\\.json$",
            full.names = TRUE
        )

        if (length(files) == 0) {
            return(tibble())
        }

        map_dfr(files, read_review_file, split = split)
    }
)

reviews_tbl <- reviews_raw %>%
    mutate(
        split = factor(split, levels = available_splits),
        comments = if_else(is.na(comments) | comments == "", NA_character_, comments),
        comment_word_count = if_else(
            !is.na(comments),
            str_count(comments, boundary("word")),
            NA_integer_
        ),
        recommendation_bucket = case_when(
            recommendation >= 4 ~ "Alta",
            recommendation <= 2 ~ "Baixa",
            TRUE ~ "Neutra"
        ),
        recommendation_bucket = factor(
            recommendation_bucket,
            levels = c("Baixa", "Neutra", "Alta")
        )
    )

datatable(head(reviews_tbl, 25), options = list(pageLength = 5))

Entendendo as Colunas

Os campos principais utilizados na análise são:

Coluna Descrição
paper_id Identificador único do artigo no CoNLL 2016
split Partição de origem (train, dev ou test)
review_index Índice da revisão dentro do arquivo do artigo
is_meta_review Indica se o texto é uma meta-review consolidada
recommendation Nota de 1 (rejeitar) a 5 (aceitar fortemente)
clarity, soundness, originality, impact, substance, meaningful_comparison, appropriateness, replicability Rubricas específicas avaliadas em escala ordinal
reviewer_confidence Confiança do avaliador em sua própria recomendação
presentation_format Formato sugerido na conferência (oral, pôster, etc.)
comments Texto livre do parecer
comment_word_count Número de palavras detectado em cada comentário
recommendation_bucket Agrupamento qualitativo (Baixa, Neutra, Alta) para facilitar comparações

Manipulações Executadas

Para padronizar as análises:

  1. Flatten dos JSONs: percorremos todos os arquivos de reviews/ e expandimos o array de avaliações para obter uma linha por combinação artigo–revisor.
  2. Conversão para numérico: todas as rubricas e notas textuais foram convertidas para numeric, preservando NA quando ausente ou inválida.
  3. Derivações textuais: calculamos o tamanho dos comentários (comment_word_count) para aproximar o esforço do avaliador e criamos a variável categórica recommendation_bucket.
  4. Agregação por artigo: resumimos métricas médias por paper_id para comparação entre trabalhos.
paper_level <- reviews_tbl %>%
    group_by(paper_id, split, title) %>%
    summarise(
        n_reviews = n(),
        mean_recommendation = mean(recommendation, na.rm = TRUE),
        mean_confidence = mean(reviewer_confidence, na.rm = TRUE),
        mean_soundness = mean(soundness, na.rm = TRUE),
        share_meta_review = mean(is_meta_review, na.rm = TRUE),
        total_words = sum(comment_word_count, na.rm = TRUE),
        .groups = "drop"
    ) %>%
    mutate(
        mean_recommendation = round(mean_recommendation, 2),
        mean_confidence = round(mean_confidence, 2),
        mean_soundness = round(mean_soundness, 2),
        share_meta_review = round(share_meta_review, 2)
    )

datatable(head(paper_level, 20), options = list(pageLength = 5))

Dataset resultado

Resultado final das manipulações

O objeto reviews_tbl consolidado contém uma linha por parecer, enquanto paper_level resume estatísticas por artigo. No total, carregamos 39 revisões.

Análise Exploratória dos Dados

Distribuição das recomendações

Analisamos a dispersão das notas de recomendação em cada partição para verificar se há diferenças sistemáticas entre conjuntos que poderiam enviesar experimentos.

plot_ly(
    data = reviews_tbl %>% filter(!is.na(recommendation)),
    x = ~split,
    y = ~recommendation,
    color = ~split,
    type = "box"
) %>%
    layout(
        title = "Distribuição das notas de recomendação por partição",
        xaxis = list(title = "Partição"),
        yaxis = list(title = "Recomendação (1-5)")
    )

Rubricas vs recomendação

Calculamos a correlação de Pearson entre a nota de recomendação e cada rubrica para entender quais critérios mais influenciam o parecer final.

rubrica_corr <- reviews_tbl %>%
    select(recommendation, clarity, soundness, originality, impact, substance, meaningful_comparison, appropriateness, replicability) %>%
    pivot_longer(-recommendation, names_to = "rubrica", values_to = "valor") %>%
    filter(!is.na(recommendation), !is.na(valor)) %>%
    group_by(rubrica) %>%
    summarise(correlacao = cor(recommendation, valor), .groups = "drop") %>%
    arrange(desc(correlacao))

plot_ly(rubrica_corr,
                x = ~correlacao,
                y = ~reorder(rubrica, correlacao),
                type = "bar",
                orientation = "h",
                marker = list(color = "#1f77b4")
) %>%
    layout(
        title = "Correlação entre rubricas e recomendação",
        xaxis = list(title = "Correlação de Pearson"),
        yaxis = list(title = "Rubrica")
    )

Confiança do revisor vs recomendação

Verificamos se revisores com maior confiança tendem a atribuir notas mais extremas e se há diferenças entre meta-reviews e revisões individuais.

plot_ly(
    data = reviews_tbl %>% filter(!is.na(recommendation), !is.na(reviewer_confidence)),
    x = ~reviewer_confidence,
    y = ~recommendation,
    color = ~is_meta_review,
    type = "scatter",
    mode = "markers",
    marker = list(opacity = 0.6)
) %>%
    layout(
        title = "Confiança do revisor x recomendação",
        xaxis = list(title = "Confiança"),
        yaxis = list(title = "Recomendação"),
        legend = list(title = list(text = "Meta review"))
    )

Vocabulário por polaridade

Textos de pareceres foram tokenizados para identificar termos característicos de avaliações altas e baixas. Agrupamos palavras, removemos stopwords e destacamos os 10 termos mais frequentes por polaridade.

data("stop_words")

tokens_top <- reviews_tbl %>%
    filter(!is.na(comments), !is.na(recommendation)) %>%
    mutate(polaridade = if_else(recommendation >= 4, "Alta", "Baixa")) %>%
    unnest_tokens(word, comments) %>%
    anti_join(stop_words, by = "word") %>%
    filter(str_detect(word, "^[a-zA-Z]+$")) %>%
    count(polaridade, word, sort = TRUE) %>%
    group_by(polaridade) %>%
    slice_max(n, n = 10, with_ties = FALSE) %>%
    ungroup() %>%
    arrange(polaridade, n) %>%
    mutate(label = paste(polaridade, word, sep = " | "),
                 label = factor(label, levels = label))

plot_ly(tokens_top,
                x = ~n,
                y = ~label,
                color = ~polaridade,
                type = "bar",
                orientation = "h"
) %>%
    layout(
        title = "Principais termos por polaridade de recomendação",
        xaxis = list(title = "Frequência"),
        yaxis = list(title = "Polaridade | termo"),
        legend = list(orientation = "h", x = 0.2, y = 1.1)
    )

Conclusões

Síntese da análise

  • As distribuições de notas são semelhantes entre train, dev e test, indicando que os splits preservam a variedade de recomendações.
  • Rubricas como IMPACT, SOUNDNESS_CORRECTNESS e ORIGINALITY apresentam as correlações mais fortes com a nota final, sugerindo que critérios técnicos pesam mais que aspectos formais.
  • Revisores com alta confiança tendem a emitir recomendações extremas, enquanto meta-reviews suavizam outliers ao resumir o consenso do comitê.
  • Comentários favoráveis mencionam com frequência termos como “strong”, “well”, “clear”, enquanto pareceres negativos enfatizam “unclear”, “weak” e “lack”, refletindo padrões qualitativos coerentes com as notas numéricas.

Implicações

  1. Para autores: acompanhar as rubricas com maior peso (impacto e solidez) ajuda a priorizar revisões antes da submissão. A análise textual entrega exemplos de feedback recorrente que podem orientar melhorias.
  2. Para revisores e chairs: identificar discrepâncias entre confiança declarada e severidade das notas permite calibrar discussões durante o meta-review, mitigando vieses individuais.
  3. Para pesquisadores em NLP: o pipeline reproduzível em R demonstra como transformar os JSONs do PeerRead em um dataframe tidy, servindo de base para estudos de vieses, modelos de recomendação ou geração automática de meta-reviews.

Limitações e próximos passos

  • Os JSONs não incluem timestamps detalhados, inviabilizando análises temporais similares às aplicadas em mercados financeiros.
  • Algumas rubricas possuem valores ausentes; imputações ou modelos bayesianos poderiam reduzir o viés de remoção.
  • A análise textual resumiu apenas unigramas. Futuras iterações podem extrair tópicos, medir sentimento específico para cada rubrica ou correlacionar tokens com decisões finais via regressões hierárquicas.

Para reproduzir este relatório, basta garantir que os dados estejam sob data/conll_2016 (ou data/conll_20216, conforme convenção local) e renderizar o arquivo com rmarkdown::render("analise2.Rmd") em um ambiente com os pacotes listados.