Introdução: Qualidade Industrial na Pandemia

O Estudo busca entender o cenário da qualidade de produtos da categoria principal Industrial e Científica da Amazon durante o período da pandemia (2019-2023) para analisar como a crise impactou as vendas de tais produtos, como também identificar se houve impacto na qualidade e quantidade de vendas por sub categoria, afim de identificar características que favoreceram ou desfavoreceram o cenário de consumo online em um momento tão atípico do mundo.

Informações Gerais

Problema abordado

A pandemia de COVID-19 (2020-2022), devido ao aumento na procura de consumo online, impôs um estresse nas cadeias globais de suprimentos e fabricação, a partir desta afirmação, é possível se questionar: Será que este cenário impactou o ecosistemas logísticos e econômicos, prejudicando a qualidade percebida e no volume de vendas dos produtos da categoria Industrial and Scientific da Amazon?. Será analisado as falhas críticas (quebra, imprecisão, durabilidade), identificadas via análise de texto, e observar se houve o aumento significativamente nos períodos de pico e pós-pandemia, em comparação com o período pré-pandemia.

Abordagem e Metodologia

A abordagem foi baseada na criação variáveis temporal categórica (Periodo_Pandemia) e na comparação do comportamento de três métricas ao longo do tempo:

  • Métrica Quantitativa (Vendas): Volume mensal de avaliações.
  • Métrica de Qualidade (Falhas): Percentual de avaliações que contêm palavras-chave de falha.
  • Métrica de Sentimento: Média do rating ao longo do tempo.

Na metodologia de classificação temporal, o período de análise será dividido em três fases chave:

  1. Pré: Jan/2019 – Fev/2020
  2. Pico (Dificuldade Logística): Mar/2020 – Dez/2021
  3. Pós (Recuperação/Ajuste): Jan/2022 – Dez/2023

Para estas atingir estes objetivos, foi preciso realizar alguns tratamentos que facilitassem a manipulação das informações dentro dos gráficos, de modo que gerasse os insights desejados. Então, após o carregamento dos dados, foi realizado o merge entre os reviews e os metadados utilizando o parent_asin como chave, como também a padronização e limpeza de algumas colunas, como correção das colunas numérias de ratings e price, normalização da coluna text, a qual representa o texto de avaliação do usuário para o produto, criação de variáveis como marca, as próprias variáveis categórias de período e as informações de data facilitadas.

Pacotes Usados

Pacote Função
knitr Configuração do chunk.
tidyverse Usado extensivamente para manipulação de dados.
plotly Criação de todos os gráficos interativos.
rmdformats Define o formato de saída no cabeçalho YAML.
DT Exibição da tabela de dados final.
tidytext Processamento de texto para quebra de palavras.
stringr Manipulação de strings.
jsonlite Leitura e parsing de arquivos JSONL complexos.

Estrutura do Dataset

Fonte do Dataset

Dataset: Amazon Reviews 2023 — Industrial and Scientific.

Dataset metadados

Coluna Tipo Descrição
main_category str Categoria principal do produto.
title str Nome do produto.
average_rating float Nota média do produto.
rating_number int Número total de avaliações.
features list Características do produto em tópicos.
description list Descrição do produto.
price float Preço em Dólar Americano.
images list Imagens do produto.
videos list Vídeos do produto.
store str Nome da loja.
categories list Categorias hierárquicas.
details dict Detalhes do produto.
parent_asin str ID Pai do produto.
bought_together list Pacotes recomendados.

Dataset reviews

Coluna Tipo Descrição
rating float Nota do produto (1.0 a 5.0).
title str Título da avaliação.
text str Corpo do texto da avaliação.
images list Imagens postadas pelo usuário.
asin str ID do produto.
parent_asin str ID Pai do produto.
user_id str ID do avaliador.
timestamp int Tempo da avaliação, Unix time em milissegundos.
verified_purchase bool Verificação de compra.
helpful_vote int Votos de utilidade da avaliação.

Novas variáveis criadas neste projeto

Variável Tipo Propósito no Projeto
problema_critico Fator (Sim/Não) Métrica chave de Qualidade, detecção de falhas.
periodo_pandemia Fator Categórico Métrica chave Temporal, Pré-Pico, Pico, Pós-Pico.
date POSIXct Data e hora da avaliação, convertida do timestamp.
ano_mes str Ano e mês para gráficos de tendências (YYYY-MM).
brand str Marca do produto, extraída do campo details.
text_limpo str Texto da avaliação em minúsculas e sem pontuação.
comprimento_review int Número de palavras no review.

Preparação dos Dados

Importação, Limpeza e Criação de Variáveis

#função para ler o arquivo jsonl
read_jsonl_flat <- function(path, n_max = 50000) {
 con <- file(path, "r")
 on.exit(close(con))

 result <- vector("list", n_max)
 i <- 1

 while (i <= n_max) {
  line <- readLines(con, n = 1)
  if (length(line) == 0) break

  json <- tryCatch(
   jsonlite::fromJSON(line, flatten = TRUE),
   error = function(e) NULL
  )
  if (is.null(json)) next

  if (!is.null(json$details)) {
   json$details_str <- jsonlite::toJSON(json$details, auto_unbox = TRUE)
   json$details <- NULL 
  } else {
   json$details_str <- NA_character_
  }

  json <- lapply(json, function(col) {
   if (is.null(col)) return(NA_character_)
   if (length(col) > 1 || is.list(col) || is.data.frame(col)) {
    return(jsonlite::toJSON(col, auto_unbox = TRUE))
   }
   return(as.character(col))
  })
  json <- lapply(json, function(x) as.character(x))
  
  result[[i]] <- json
  i <- i + 1
 }

 result <- result[!sapply(result, is.null)]
 dplyr::bind_rows(result)
}

#define a quantidade de linhas lidas (foi necessário limitar por falta de memória ram)
reviews_raw <- read_jsonl_flat("Industrial_and_Scientific.jsonl", n_max = 500000)
metadata_raw <- read_jsonl_flat("meta_Industrial_and_Scientific.jsonl", n_max = 500000)


#etapa de filtragem dos dados no período 2019-2023
if ("timestamp" %in% names(reviews_raw)) {
  reviews_period <- reviews_raw %>%
    mutate(review_date = as.POSIXct(as.numeric(timestamp)/1000, origin = "1970-01-01")) %>%
      filter(
    review_date >= as.Date("2019-01-01") &
    review_date <  as.Date("2024-01-01")
  )
} else {
  reviews_period <- reviews_raw
}

#ajustes nos tipos, nomes das colunas e criação de algumas colunas necessárias

clean_numeric <- function(x) {
  x <- gsub("[^0-9\\.]", "", x)
  suppressWarnings(as.numeric(x))
}

reviews_df <- reviews_period %>%
  mutate(
    parent_asin = parent_asin %||% parent_asin,
    rating = as.numeric(rating),
    helpful_vote = clean_numeric(helpful_vote),
    timestamp = as.numeric(timestamp),
    date = as.POSIXct(timestamp/1000,origin = "1970-01-01", tz = "UTC"),
    verified_purchase = verified_purchase
  ) %>%
  select(parent_asin, rating, text, helpful_vote, timestamp,date, verified_purchase)

metadata_df <- metadata_raw %>%
 mutate(price = clean_numeric(price),
        product_rating_number = as.numeric(rating_number),
        product_average_rating = as.numeric(average_rating),
        product_title = title) %>%
 select(parent_asin, price, main_category, details_str,product_rating_number,product_average_rating,product_title, store)

#realiza o merge dos dois datasets
merged_df <- reviews_df %>%
 inner_join(metadata_df, by = "parent_asin") %>%
 drop_na(parent_asin, rating, text)

#realiza tratamento de marca
extract_brand <- function(details_json) {
  if (is.na(details_json) || details_json == "{}") return(NA_character_)

  brand_val <- tryCatch({
    details_list <- jsonlite::fromJSON(details_json, simplifyVector = FALSE)
    brand_name <- details_list$Brand %||% details_list$brand
    if (is.null(brand_name)) return(NA_character_)
    return(as.character(brand_name))
    
  }, error = function(e) {
    return(NA_character_)
  })
  
  return(brand_val)
}

# cria a coluna separada de datils
merged_df <- merged_df %>%
  mutate(
    details_str = as.character(details_str),
    brand = sapply(details_str, extract_brand, USE.NAMES = FALSE)
  )


#etapa de criação da coluna de problema critico como analise de palavra chave
keywords_falha_list <- c(
 "broke","failed","inaccurate","stop working","malfunction",
 "durability","misleading","short-life","burned","inconsistent",
 "stopped working","defective"
)

merged_df <- merged_df %>%
 mutate(
  text_limpo = tolower(gsub("[[:punct:]]", "", text)),
  problema_critico = if_else(str_detect(text_limpo, paste(keywords_falha_list, collapse = "|")), "Sim", "Não"),
  rating_fator = factor(rating),
  comprimento_review = str_count(text, "\\w+"),
  price_tratado = if_else(is.na(price), median(price, na.rm=TRUE), price) 
 )

#aplica criação de colunas separadas para apenas mes-ano e definição categoria do periodo da pandemia
merged_df <- merged_df %>%
   mutate(
      ano_mes = format(date, "%Y-%m"), 
      periodo_pandemia = case_when(
         date < as.Date("2020-03-01") ~ "A. Pre-Pico",
         date >= as.Date("2022-01-01") ~ "C. Pos-Pico",
         date >= as.Date("2020-03-01") ~ "B. Pico",
         TRUE ~ "Z. Desconhecido"
      )
  ) %>%
 filter(periodo_pandemia != "Z. Desconhecido")

Dataset Final

nrow(merged_df)
## [1] 300107
datatable(head(merged_df, 10))

Análise Exploratória

Tendência das vendas, qualidade e sentimento ao longo do tempo

Análise da evolução das três métricas volume de vendas, taxa de falhas e rating, ao longo do período de 2019 a 2023, com o objetivo de comparar o comportamento do mercado antes, durante e depois do pico da pandemia.

# realiza a agregação mensal
tendencia_mensal <- merged_df %>%
  group_by(ano_mes) %>%
  summarise(
    vendas = n(),
    taxa_falha = sum(problema_critico == "Sim") / vendas,
    rating_medio = mean(rating),
    .groups = 'drop'
  ) %>%
  mutate(Data = as.Date(paste0(ano_mes, "-01")))

# criação do gráfico
plot_ly(tendencia_mensal, x = ~Data) %>%
  add_lines(y = ~vendas, name = 'Volume de Vendas', yaxis = "y1", line = list(color = '#4A90E2')) %>%
  add_lines(y = ~taxa_falha * max(tendencia_mensal$vendas, na.rm = TRUE) * 0.5,
            name = 'Taxa de Falha Crítica', yaxis = "y2", line = list(color = '#c0392b', dash = 'dash')) %>%
  add_lines(y = ~rating_medio,
            name = 'Rating Médio', yaxis = "y3", line = list(color = '#e08e0b')) %>%
  
  layout(
    title = "Quantidade de Vendas e Qualidade ao Longo da Pandemia (2019-2023)",
    xaxis = list(title = "Período (Ano-Mês)"),
    yaxis = list(title = "Vendas (Reviews)", side = "left", showgrid = TRUE),
    yaxis2 = list(title = "Taxa de Falha (%)", overlaying = "y", side = "right", 
                  tickformat = ".1%", showgrid = FALSE, 
                  range = c(0, max(tendencia_mensal$vendas, na.rm = TRUE) * 0.5)),
    yaxis3 = list(title = "Rating Médio", overlaying = "y", side = "right", position = 1.05, 
                  tickformat = ".1f", showgrid = FALSE, range = c(3.5, 5))
  )

Embora o volume de vendas tenha sido caótico, as métricas de qualidade percebida não teve mudança perceptível no gráfico, porém, para a pontuação de rating, é possível notar uma influência negativa durante o período 2020-2022, com a tentativa de retomada a partir de 2022-2023.

Comparativo qualitativo por período

Realizado a comparação das três fases da pandemia (Pré, Pico, Pós) usando a taxa de falha e a distribuição do rating.

#Realiza o filtro para analisar quantidade de vendas por rating e falhas indicadas nas reviews
comparativo_periodo <- merged_df %>%
  group_by(periodo_pandemia) %>%
  summarise(
    total_vendas = n(),
    rating_medio = mean(rating),
    reviews_criticos = sum(problema_critico == "Sim"),
    porcentagem_falhas = round(reviews_criticos / total_vendas * 100, 2),
    .groups = 'drop'
  ) %>%
  arrange(periodo_pandemia)

plot_ly(comparativo_periodo, 
        x = ~periodo_pandemia, 
        y = ~porcentagem_falhas, 
        type = 'bar', 
        marker = list(color = ~porcentagem_falhas, colorscale = 'Reds', showscale = FALSE),
        text = ~paste0(porcentagem_falhas, "%"),
        textposition = 'outside'
) %>%
  layout(
    title = "Taxa Percentual de Falhas Críticas por Período",
    xaxis = list(title = "Período"),
    yaxis = list(title = "Taxa de Falha (%)", ticksuffix = "%")
  )

Houve um aumento gradual e constante na Taxa de Falha Crítica, começando no Pré-Pico e atingindo o nível mais alto no Pós-Pico, com aumento de 0,58 pontos percentuais.

Análise de sentimento das avaliações no período

Visualização da dispersão do rating em cada período, verificando se a crise causou um aumento ou queda generalizada do sentimento das avaliações registradas.

plot_ly(merged_df, 
        x = ~periodo_pandemia, 
        y = ~rating, 
        type = 'box', 
        color = ~periodo_pandemia) %>%
  layout(
    title = "Distribuição do Rating por Período da Pandemia",
    xaxis = list(title = "Período"),
    yaxis = list(title = "Rating (1 a 5 Estrelas)")
  )

O aumento na dispersão das notas dadas pelos avaliadores nos períodos B e C, especialmente a partir de 4.0 e 3.0, mostra uma maior polarização nos produtos durante e após a crise, mesmo que a maioria dos usuários continuassem dando nota máxima, percebe-se um aumento nas avaliações negativas, de 1.0 a 4.0.

Falhas específicas, maiores palavras-chaves no periodo da crise

As palavras-chave de falha mais frequentes no período de crise (Pico) para identificar o foco dos problemas de qualidade.

#filtra o período de crise
top_keywords_crise <- merged_df %>%
  filter(periodo_pandemia == "B. Pico") %>%
  unnest_tokens(word, text_limpo) %>%
  filter(word %in% keywords_falha_list) %>%
  count(word, sort = TRUE) %>%
  top_n(10, n)

top_keyword_crise <- top_keywords_crise$word[1]

plot_ly(top_keywords_crise,
        x = ~n,
        y = ~reorder(word,n),
        type='bar', orientation='h',
        marker = list(color = '#e08e0b')) %>%
  layout(title = "Top 10 Falhas Críticas Reportadas Durante o Pico")

O principal problema de qualidade durante o auge da crise não foi imprecisão ou software, mas sim a integridade física e falha de hardware.

Crescimento Proporcional das Vendas (Pós vs. Pré-Pico)

O gráfico abaixo foca no crescimento percentual das vendas de cada categoria, comparando o período Pós-Pico (C) com o período Pré-Pico (A), esta métrica identifica categorias que, embora possam não ter o maior volume total, tiveram a maior expansão proporcional de mercado após a crise.

crescimento_categorias <- merged_df %>%
   mutate(
      periodo_simples = case_when(
         str_detect(periodo_pandemia, "Pre-Pico") ~ "Pre", 
         str_detect(periodo_pandemia, "Pos-Pico") ~ "Pos", 
         TRUE ~ "Pico"
      )
   ) %>%
    
   # calcula vendas (reviews) por categoria
   group_by(main_category, periodo_simples) %>%
   summarise(vendas_periodo = n(), .groups = 'drop') %>%
    
   pivot_wider(
      names_from = periodo_simples,
      values_from = vendas_periodo,
      names_prefix = "vendas_",
      values_fill = 0
   ) %>%
    
   #calculo do crescimento proporcional
   mutate(
      Crescimento_Pct = 100 * (vendas_Pos - vendas_Pre) / vendas_Pre
   ) %>%
   filter(vendas_Pre >= 20) %>%
   arrange(desc(Crescimento_Pct)) %>%
   head(10)

plot_ly(crescimento_categorias,
        x = ~Crescimento_Pct,
        y = ~reorder(main_category, Crescimento_Pct),
        type = 'bar', 
        orientation = 'h',
        marker = list(color = '#2ecc71'),
        text = ~paste0(round(Crescimento_Pct, 0), "%"),
        textposition = 'outside'
) %>%
  layout(
    title = "Top Categorias com Maior Crescimento (pós vs. pré)",
    xaxis = list(title = "Crescimento Percentual (%)"),
    yaxis = list(title = "Categoria Principal")
  )

É possível perceber que houve um crescimento enorme nas áreas de beleza, eletrônicos e cuidados pessoais.

Conclusões

Reflexões sobre o estudo

A criação da variável Periodo_Pandemia permitiu segmentar e quantificar o impacto de um evento global na percepção da qualidade dos produtos, as análises mostram que houve um estresse na demanda de compras e aumenta nas avaliações de produtos, com uma leve influencia na avaliação de qualidade dos produtos em questão, manifestada como um aumento na taxa de falhas no período de pós pandemia, após uma escalada considerável iniciada no pico do período, como também uma tendência de desestabilização da nota média dada nos produtos em geral.

Um dos pontos importantes a se concluir é que a Taxa de Falhas Críticas aumentou progressivamente de 2,76% (pré-pandemia) para 3,34% (pós-pandemia), indicando que os problemas de qualidade não foram resolvidos após o fim da crise.

Outro ponto relevante é a questão da distribuição dos ratings realizados durante as compras no período, a qual passou a ser menos centrado na nota máximo, dispersando mais entre os valores menores, mesmo mantendo majoritariamente notas 5, o que pode ser um reflexo do momento tenso em que as pessoas estavam vivenciando.

E como evidência, foi possível analisar que “broke” e “failed” estavam liderando entre nas menções de falhas das avaliações do período da pandemia.

Próximos passos

Como continuação do estudo em questão, seria interessante avaliar tais comportamentos com uma visão mais macro, utilizando todo o range do período de avaliações registrados no dataset, oque acabou não sendo possível por limitações de de hardware para executar os processamentos. Outro ponto interessante é adentrar mais a fundo em cada sub-categoria, realizar análises comparativas entre sí e entre as sub-categorias para identificar comportamentos anômalos que podem ter ocorrido durante o período da pandemia, como as questões das EPIs na área industrial hospitalar.