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.
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.
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:
Na metodologia de classificação temporal, o período de análise será dividido em três fases chave:
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.
| 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. |
Dataset: Amazon Reviews 2023 — Industrial and Scientific.
| 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. |
| 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. |
| 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. |
#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")
nrow(merged_df)
## [1] 300107
datatable(head(merged_df, 10))
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.
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.
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.
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.
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.
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.
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.