A qualidade percebida de um vinho e sua capacidade de harmonizar com diferentes pratos são influenciadas por uma combinação complexa de fatores químicos, sensoriais e culturais. Apesar disso, consumidores, sommeliers, restaurantes e produtores muitas vezes tomam decisões baseadas apenas em experiência pessoal ou em recomendações subjetivas, sem apoio quantitativo claro. Assim, permanece a questão central deste projeto: quais características químicas de um vinho mais contribuem para sua qualidade e quais fatores influenciam a qualidade de suas combinações gastronômicas com diferentes tipos de comidas e culinárias?
Esse problema é relevante porque une duas dimensões práticas:
Para abordar essa questão, utilizarei dois conjuntos de dados complementares:
A metodologia seguirá uma sequência lógica consistente com boas práticas em ciência de dados:
A abordagem técnica proposta combina métodos estatísticos, exploratórios e semânticos. Inicialmente, serão aplicadas técnicas de limpeza e organização tabular, garantindo consistência de tipos, padronização de categorias e tratamento de valores ausentes. Em seguida, a junção entre dados laboratoriais e dados de harmonização permitirá análises mais ricas.
Nota Metodológica: Devido à ausência de chaves primárias comuns entre as bases, utiliza-se uma abordagem de agrupamento. Assume-se que vinhos quimicamente similares (do mesmo cluster) compartilham o mesmo potencial de harmonização descrito na base de pairings.
A análise exploratória utilizará visualizações, correlações, comparações entre grupos e análise textual simples para identificar padrões relevantes. Além disso, algumas técnicas de modelagem e interpretação, como regressões, classificações e importância de variáveis, poderão ser aplicadas de forma complementar para verificar se determinados atributos químicos podem prever qualidade ou adequação gastronômica. Embora a modelagem não seja o foco principal, ela ajuda a validar tendências observadas e reforça a robustez dos insights obtidos.
A análise resultante beneficia vários perfis de usuários. Para produtores e enólogos, fornece evidências quantitativas sobre quais atributos químicos mais impactam a qualidade final do vinho. Para restaurantes, bares e sommeliers, oferece uma base objetiva para selecionar vinhos e criar harmonizações mais consistentes, reduzindo tentativas e erros. Para consumidores, permite entender melhor o motivo pelo qual certos vinhos combinam com determinados pratos, facilitando escolhas mais informadas. Para áreas de dados, o projeto demonstra como integrar dados laboratoriais e sensoriais para gerar insights comerciais e gastronômicos aplicáveis.
Em conjunto, esta análise transforma dados brutos em conhecimento útil, orientando decisões práticas e melhorando a compreensão sobre aquilo que torna um vinho mais apreciado e bem harmonizado.
Nesta seção são carregados todos os pacotes utilizados ao longo do projeto, permitindo que qualquer leitor reproduza integralmente a análise. Além da listagem, cada pacote é acompanhado de uma breve explicação sobre seu propósito e seu papel nas etapas de importação, limpeza, exploração e visualização dos dados.
# Manipulação e Transformação de Dados (O 'tidyverse' carrega ggplot2, dplyr, tidyr, readr, etc.)
library(tidyverse)
library(janitor) # Para limpeza de nomes de colunas (clean_names)
# Sumários e Tabelas
library(skimr) # Para sumários estatísticos rápidos (skim)
library(knitr) # Para gerar a tabela final formatada (kable)
# Visualização Avançada
library(patchwork) # Para combinar gráficos (plot1 + plot2)
library(plotly) # Para tornar os gráficos interativos
library(reshape2) # Para transformar a matriz de correlação (melt)
# Texto e Mineração de Dados
library(tidytext) # Para análise de texto (unnest_tokens, stop_words)Os dados utilizados neste projeto foram obtidos a partir de um dataset público amplamente utilizado em competições, pesquisas e projetos educacionais de ciência de dados. A fonte original é:
No repositório do dataset estão disponíveis dois arquivos csv: winequality-red.csv e wine_food_pairings.csv
## Rows: 1599 Columns: 12
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## dbl (12): fixed acidity, volatile acidity, citric acid, residual sugar, chlo...
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## Rows: 34933 Columns: 8
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (7): wine_type, wine_category, food_item, food_category, cuisine, qualit...
## dbl (1): pairing_quality
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
| Name | wine_quality_raw |
| Number of rows | 1599 |
| Number of columns | 12 |
| _______________________ | |
| Column type frequency: | |
| numeric | 12 |
| ________________________ | |
| Group variables | None |
Variable type: numeric
| skim_variable | n_missing | complete_rate | mean | sd | p0 | p25 | p50 | p75 | p100 | hist |
|---|---|---|---|---|---|---|---|---|---|---|
| fixed acidity | 0 | 1 | 8.32 | 1.74 | 4.60 | 7.10 | 7.90 | 9.20 | 15.90 | ▂▇▂▁▁ |
| volatile acidity | 0 | 1 | 0.53 | 0.18 | 0.12 | 0.39 | 0.52 | 0.64 | 1.58 | ▅▇▂▁▁ |
| citric acid | 0 | 1 | 0.27 | 0.19 | 0.00 | 0.09 | 0.26 | 0.42 | 1.00 | ▇▆▅▁▁ |
| residual sugar | 0 | 1 | 2.54 | 1.41 | 0.90 | 1.90 | 2.20 | 2.60 | 15.50 | ▇▁▁▁▁ |
| chlorides | 0 | 1 | 0.09 | 0.05 | 0.01 | 0.07 | 0.08 | 0.09 | 0.61 | ▇▁▁▁▁ |
| free sulfur dioxide | 0 | 1 | 15.87 | 10.46 | 1.00 | 7.00 | 14.00 | 21.00 | 72.00 | ▇▅▁▁▁ |
| total sulfur dioxide | 0 | 1 | 46.47 | 32.90 | 6.00 | 22.00 | 38.00 | 62.00 | 289.00 | ▇▂▁▁▁ |
| density | 0 | 1 | 1.00 | 0.00 | 0.99 | 1.00 | 1.00 | 1.00 | 1.00 | ▁▃▇▂▁ |
| pH | 0 | 1 | 3.31 | 0.15 | 2.74 | 3.21 | 3.31 | 3.40 | 4.01 | ▁▅▇▂▁ |
| sulphates | 0 | 1 | 0.66 | 0.17 | 0.33 | 0.55 | 0.62 | 0.73 | 2.00 | ▇▅▁▁▁ |
| alcohol | 0 | 1 | 10.42 | 1.07 | 8.40 | 9.50 | 10.20 | 11.10 | 14.90 | ▇▇▃▁▁ |
| quality | 0 | 1 | 5.64 | 0.81 | 3.00 | 5.00 | 6.00 | 6.00 | 8.00 | ▁▇▇▂▁ |
| Name | pairings_raw |
| Number of rows | 34933 |
| Number of columns | 8 |
| _______________________ | |
| Column type frequency: | |
| character | 7 |
| numeric | 1 |
| ________________________ | |
| Group variables | None |
Variable type: character
| skim_variable | n_missing | complete_rate | min | max | empty | n_unique | whitespace |
|---|---|---|---|---|---|---|---|
| wine_type | 0 | 1 | 4 | 18 | 0 | 29 | 0 |
| wine_category | 0 | 1 | 3 | 9 | 0 | 6 | 0 |
| food_item | 0 | 1 | 6 | 24 | 0 | 38 | 0 |
| food_category | 0 | 1 | 4 | 11 | 0 | 12 | 0 |
| cuisine | 0 | 1 | 4 | 17 | 0 | 17 | 0 |
| quality_label | 0 | 1 | 4 | 9 | 0 | 5 | 0 |
| description | 0 | 1 | 25 | 81 | 0 | 21 | 0 |
Variable type: numeric
| skim_variable | n_missing | complete_rate | mean | sd | p0 | p25 | p50 | p75 | p100 | hist |
|---|---|---|---|---|---|---|---|---|---|---|
| pairing_quality | 0 | 1 | 3.02 | 1.41 | 1 | 2 | 3 | 4 | 5 | ▇▆▇▆▇ |
# Selecionar apenas variáveis numéricas químicas
wine_features <- wine_quality %>%
select(-wine_id)
set.seed(123)
# Criação dos clusters
kmeans_model <- kmeans(scale(wine_features), centers = 4, nstart = 20)
# Adicionar cluster ao dataset químico
wine_quality <- wine_quality %>%
mutate(cluster = as.factor(kmeans_model$cluster))# Perfil químico médio geral dos vinhos tintos do dataset UCI
red_profile <- wine_quality %>%
summarise(across(where(is.numeric), mean))
# Calcular distância euclidiana entre perfil RED e clusters
cluster_dist <- cluster_profiles %>%
rowwise() %>%
mutate(
dist = sqrt(sum(
(c_across(starts_with("avg_")) -
as.numeric(red_profile))^2
))
) %>%
ungroup()
# Cluster mais representativo do vinho tinto
best_cluster_red <- cluster_dist %>%
slice_min(dist) %>%
pull(cluster)
best_cluster_red## [1] 4
## Levels: 1 2 3 4
# Realiza o cruzamento (cross join) entre os vinhos do cluster representativo e as harmonizações.
# Premissa: Assume-se que vinhos quimicamente similares (do mesmo cluster)
# compartilham o mesmo potencial de harmonização descrito na base de pairings.
dataset_final <- tidyr::crossing(wine_cluster_red, pairings_red) %>%
mutate(pairing_id = row_number())
dataset_final <- dataset_final %>%
select(rev(colnames(.)))| Name | dataset_final |
| Number of rows | 6144208 |
| Number of columns | 23 |
| _______________________ | |
| Column type frequency: | |
| character | 7 |
| factor | 1 |
| numeric | 15 |
| ________________________ | |
| Group variables | None |
Variable type: character
| skim_variable | n_missing | complete_rate | min | max | empty | n_unique | whitespace |
|---|---|---|---|---|---|---|---|
| description | 0 | 1 | 25 | 47 | 0 | 13 | 0 |
| quality_label | 0 | 1 | 4 | 9 | 0 | 5 | 0 |
| cuisine | 0 | 1 | 4 | 17 | 0 | 17 | 0 |
| food_category | 0 | 1 | 4 | 11 | 0 | 11 | 0 |
| food_item | 0 | 1 | 6 | 24 | 0 | 34 | 0 |
| wine_category | 0 | 1 | 3 | 3 | 0 | 1 | 0 |
| wine_type | 0 | 1 | 6 | 18 | 0 | 12 | 0 |
Variable type: factor
| skim_variable | n_missing | complete_rate | ordered | n_unique | top_counts |
|---|---|---|---|---|---|
| cluster | 0 | 1 | FALSE | 1 | 4: 6144208, 1: 0, 2: 0, 3: 0 |
Variable type: numeric
| skim_variable | n_missing | complete_rate | mean | sd | p0 | p25 | p50 | p75 | p100 | hist |
|---|---|---|---|---|---|---|---|---|---|---|
| pairing_id | 0 | 1 | 3072104.50 | 1773680.22 | 1.00 | 1536052.75 | 3072104.50 | 4608156.25 | 6144208.00 | ▇▇▇▇▇ |
| pairing_quality | 0 | 1 | 3.05 | 1.41 | 1.00 | 2.00 | 3.00 | 4.00 | 5.00 | ▇▆▇▇▇ |
| wine_id | 0 | 1 | 769.63 | 382.43 | 4.00 | 449.50 | 675.50 | 1077.25 | 1586.00 | ▂▇▃▅▂ |
| quality | 0 | 1 | 6.14 | 0.78 | 4.00 | 6.00 | 6.00 | 7.00 | 8.00 | ▁▃▇▅▁ |
| alcohol | 0 | 1 | 10.93 | 1.04 | 8.40 | 10.10 | 10.90 | 11.70 | 14.90 | ▃▇▆▂▁ |
| sulphates | 0 | 1 | 0.72 | 0.14 | 0.42 | 0.62 | 0.71 | 0.80 | 1.36 | ▃▇▃▁▁ |
| p_h | 0 | 1 | 3.21 | 0.12 | 2.86 | 3.14 | 3.21 | 3.28 | 3.54 | ▁▃▇▅▁ |
| density | 0 | 1 | 1.00 | 0.00 | 0.99 | 1.00 | 1.00 | 1.00 | 1.00 | ▁▆▇▃▁ |
| total_sulfur_dioxide | 0 | 1 | 29.14 | 16.02 | 6.00 | 17.00 | 25.00 | 41.00 | 99.00 | ▇▅▂▁▁ |
| free_sulfur_dioxide | 0 | 1 | 11.08 | 7.21 | 1.00 | 6.00 | 9.00 | 15.00 | 42.00 | ▇▅▂▁▁ |
| chlorides | 0 | 1 | 0.08 | 0.03 | 0.04 | 0.07 | 0.08 | 0.09 | 0.25 | ▇▆▁▁▁ |
| residual_sugar | 0 | 1 | 2.63 | 1.25 | 1.30 | 2.00 | 2.30 | 2.80 | 15.50 | ▇▁▁▁▁ |
| citric_acid | 0 | 1 | 0.47 | 0.12 | 0.19 | 0.39 | 0.47 | 0.53 | 0.79 | ▂▆▇▃▁ |
| volatile_acidity | 0 | 1 | 0.39 | 0.12 | 0.12 | 0.31 | 0.38 | 0.45 | 0.89 | ▂▇▃▁▁ |
| fixed_acidity | 0 | 1 | 10.05 | 1.72 | 6.40 | 8.80 | 10.00 | 11.20 | 15.90 | ▃▇▆▂▁ |
Nesta seção, focamos em entender a distribuição das variáveis físico-químicas dos vinhos selecionados (do cluster representativo).
# Pivotamos os dados para formato longo para facilitar o uso do facet_wrap
wine_cluster_red %>%
select(fixed_acidity, volatile_acidity, citric_acid, residual_sugar,
chlorides, alcohol, p_h, sulphates) %>%
pivot_longer(everything(), names_to = "variavel", values_to = "valor") %>%
ggplot(aes(x = valor)) +
geom_histogram(bins = 30, fill = "#800020", color = "white", alpha = 0.8) + # Cor vinho
facet_wrap(~variavel, scales = "free") +
theme_minimal() +
labs(title = "Distribuicao das Variaveis Quimicas (Cluster Selecionado)",
subtitle = "Caracteristicas fisico-quimicas predominantes nos vinhos analisados",
y = "Frequencia", x = "")# Entender como as variáveis químicas conversam entre si
cor_matrix <- wine_cluster_red %>%
select(where(is.numeric), -wine_id) %>%
cor()
# Transformar para formato longo para ggplot
cor_melted <- melt(cor_matrix)
ggplot(cor_melted, aes(Var1, Var2, fill = value)) +
geom_tile(color = "white") +
scale_fill_gradient2(low = "blue", high = "red", mid = "white",
midpoint = 0, limit = c(-1,1), name="Correlacao") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1)) +
labs(title = "Correlacao entre Atributos Quimicos",
x = "", y = "")Aqui analisamos o texto e as notas das harmonizações. Usaremos o tidytext para ver quais ingredientes aparecem mais.
food_tokens <- pairings_red %>%
unnest_tokens(word, food_item) %>%
anti_join(stop_words) %>% # Remove 'and', 'with', 'the'
count(word, sort = TRUE) %>%
head(15)## Joining with `by = join_by(word)`
# 2. Criar o gráfico
plot_food <- food_tokens %>%
mutate(word = reorder(word, n)) %>%
ggplot(aes(x = n, y = word)) +
geom_col(fill = "#800020") + # Cor vinho tinto
theme_minimal() +
labs(title = "Top 15 Termos em Pratos",
subtitle = "Ingredientes mais frequentes nas harmonizacoes",
x = "Frequencia", y = "")# 1. Ajustar o gráfico de Ratings
plot_ratings <- pairings_red %>%
ggplot(aes(x = pairing_quality)) +
geom_histogram(bins = 10, fill = "#E69F00", color = "white") +
theme_minimal() +
labs(title = "Distribuicao das Notas",
subtitle = "Avaliacao da qualidade da harmonizacao",
x = "Nota (Pairing Quality)",
y = "Contagem")
# 2. Exibir lado a lado
plot_food + plot_ratings# wine_cluster_red para evitar recalcular a mesma média milhares de vezes
stats_summary <- wine_cluster_red %>%
summarise(
Alcohol = mean(alcohol, na.rm = TRUE),
`Res. Sugar` = mean(residual_sugar, na.rm = TRUE),
pH = mean(p_h, na.rm = TRUE),
Sulphates = mean(sulphates, na.rm = TRUE)
) %>%
pivot_longer(everything(), names_to = "Atributo", values_to = "Media")
grafico_perfil <- ggplot(stats_summary, aes(x = Atributo, y = Media, fill = Atributo)) +
geom_col(show.legend = FALSE) +
geom_text(aes(label = round(Media, 2)), vjust = -0.5) +
scale_fill_brewer(palette = "Dark2") +
theme_minimal() +
labs(title = "Perfil Quimico Medio",
subtitle = "Caracteristicas do cluster selecionado",
y = "Valor Medio", x = "")
# Exibir o gráfico de barras (Perfil Químico)
grafico_perfil# Análise de Bigramas: Identificar pares de palavras que aparecem juntos
bigrams_analysis <- pairings_red %>%
# 1. Quebrar o texto em pares de palavras (ngrams, n=2)
unnest_tokens(bigram, description, token = "ngrams", n = 2) %>%
# 2. Separar para limpar "stop words" (the, and, with)
separate(bigram, c("word1", "word2"), sep = " ") %>%
filter(!word1 %in% stop_words$word,
!word2 %in% stop_words$word,
!is.na(word1), !is.na(word2)) %>% # Remover NAs
# 3. Contar e reunir novamente
count(word1, word2, sort = TRUE) %>%
unite(bigram, word1, word2, sep = " ") %>%
# 4. Pegar os top 15 pares
head(15)
# Visualização
bigrams_analysis %>%
mutate(bigram = reorder(bigram, n)) %>%
ggplot(aes(x = n, y = bigram)) +
geom_col(fill = "#4E2A84", alpha = 0.8) + # Um roxo escuro (referência a uvas)
geom_text(aes(label = n), hjust = -0.2, size = 3.5, color = "gray30") +
theme_minimal() +
labs(title = "Contexto Gastronomico: Principais Pares de Termos",
subtitle = "Bigramas mais frequentes nas descricoes de harmonizacao",
x = "Frequencia",
y = "") +
theme(panel.grid.major.y = element_blank()) # Preparar o gráfico base com ggplot2
p <- wine_cluster_red %>%
mutate(quality_factor = as.factor(quality)) %>%
ggplot(aes(x = alcohol, y = sulphates, color = quality_factor)) +
# Tooltip TOTALMENTE sem acentos ou caracteres especiais
geom_point(alpha = 0.7, size = 2,
aes(text = paste0("ID Vinho: ", wine_id,
"<br>Qualidade: ", quality,
"<br>Alcool: ", alcohol, "%",
"<br>Sulfatos: ", sulphates,
"<br>pH: ", p_h,
"<br>Acucar Res.: ", residual_sugar))) +
scale_color_viridis_d(option = "magma", begin = 0.1, end = 0.9, direction = -1,
name = "Qualidade") +
theme_minimal() +
# Títulos e eixos SEM acentos e SEM o '³' (usando dm3)
labs(title = "Interacao Multivariada: Alcool x Sulfatos x Qualidade",
subtitle = "Explore os dados passando o mouse sobre os pontos",
x = "Teor Alcoolico (% vol)",
y = "Sulfatos (g/dm3)")## Warning in geom_point(alpha = 0.7, size = 2, aes(text = paste0("ID Vinho: ", :
## Ignoring unknown aesthetics: text
A análise integrada entre as propriedades físico-químicas e os dados de harmonização permitiu traçar o perfil do “Vinho Tinto Representativo” e seu contexto gastronômico ideal.
Através da clusterização, isolamos um grupo de vinhos com características químicas consistentes. Cruzando este perfil com a análise textual, identificamos que a estrutura química deste grupo (definida por acidez, álcool e sulfatos) favorece fortemente pratos ricos em proteínas e gorduras.
Abaixo, apresentamos o “Perfil do Arquétipo”: um resumo estatístico do vinho analisado que serve como base para as recomendações.
# Tabela resumo final
# Cria um "Scorecard" do vinho analisado para encerrar o relatório
summary_table <- wine_cluster_red %>%
summarise(
`Álcool Médio (%)` = mean(alcohol),
`Acidez Volátil` = mean(volatile_acidity),
`Açúcar Residual` = mean(residual_sugar),
`pH Médio` = mean(p_h),
`Qualidade Média` = mean(quality)
) %>%
pivot_longer(everything(), names_to = "Métrica", values_to = "Valor")
# Renderizar tabela simples e elegante
knitr::kable(summary_table,
digits = 2,
caption = "Resumo do Perfil Químico: Métricas centrais do cluster analisado",
col.names = c("Métrica", "Valor Médio"))| Métrica | Valor Médio |
|---|---|
| Álcool Médio (%) | 10.93 |
| Acidez Volátil | 0.39 |
| Açúcar Residual | 2.63 |
| pH Médio | 3.21 |
| Qualidade Média | 6.14 |
A Química da Qualidade: Existe uma correlação positiva observável entre o teor alcoólico e a nota de qualidade. Vinhos com maior corpo (álcool mais elevado) e equilíbrio de acidez tendem a receber avaliações melhores.
O Contexto Gastronômico: A análise de bigramas revelou que este perfil de vinho não é consumido isoladamente. Termos como “tomato sauce”, “grilled meat” e “black pepper” dominam as descrições, indicando que a acidez e o álcool destes vinhos são funcionais para “limpar” o paladar em pratos untuosos.
Os resultados desta análise exploratória trazem implicações práticas para diferentes stakeholders:
Para Produtores: A evidência de que certos parâmetros químicos (como o equilíbrio pH/álcool) estão ligados à percepção de qualidade permite ajustes finos no processo de vinificação para atingir notas sensoriais mais altas.
Para Restaurantes e Sommeliers: O estudo valida, com dados, a prática empírica. Em vez de recomendar vinhos baseando-se apenas na intuição, estabelecimentos podem criar cartas de vinhos baseadas em clusters químicos que comprovadamente harmonizam com as categorias de pratos mais pedidos (carnes, molhos vermelhos).
Para o Varejo: A criação de gôndolas organizadas não por uva, mas por “Perfil de Harmonização” (ex: “Vinhos para Carnes Grelhadas”), baseada nos dados de pairings, pode simplificar a jornada de compra do consumidor leigo.
Apesar dos insights gerados, é crucial reconhecer as limitações metodológicas impostas pela natureza dos dados:
A “Assunção de Homogeneidade”: O uso de K-Means seguido de um Cross Join assume que todos os vinhos dentro de um cluster químico comportam-se de maneira idêntica na harmonização. Na realidade, nuances sutis (taninos, notas de carvalho) que não estão presentes nas variáveis químicas podem alterar drasticamente a harmonização.
Generalização da Categoria “Red”: O dataset de harmonização agrupa vinhos apenas como “Red”. Isso ignora as diferenças massivas entre um Pinot Noir (leve) e um Cabernet Sauvignon (encorpado), que quimicamente são distintos e pedem pratos diferentes.
Viés de Seleção: Os dados de qualidade são provenientes de uma região específica (Vinho Verde/Portugal para a base química), enquanto as harmonizações podem ser globais. Extrapolar essas conclusões para vinhos de terroirs muito diferentes (como Napa Valley ou Mendoza) exige cautela.
Para evoluir este estudo de uma análise exploratória para um sistema de recomendação robusto, sugerem-se as seguintes etapas:
Modelagem Preditiva (Machine Learning): Treinar um modelo de classificação (Random Forest ou XGBoost) para prever a nota da harmonização (pairing_quality) com base nas variáveis químicas.
Enriquecimento de Dados: Integrar uma base de dados que contenha a uva (varietal) específica nos dados químicos. Isso permitiria joins mais precisos do que a clusterização genérica.
Análise de Sentimento: Aplicar técnicas avançadas de NLP (Processamento de Linguagem Natural) nas descrições completas para capturar sentimentos subjetivos (“delicioso”, “ácido demais”) que escapam da contagem simples de palavras.
O código deste projeto foi desenvolvido seguindo as diretrizes do “Tidyverse Style Guide”, priorizando a legibilidade humana e a eficiência computacional. Os principais pilares adotados foram:
Operador Pipe (%>%): Utilizado
extensivamente para encadear operações, tornando o fluxo de manipulação
de dados linear e fácil de ler (da esquerda para a direita, de cima para
baixo).
Nomenclatura Snake Case: Todas as variáveis e
nomes de colunas foram padronizados para snake_case
(minúsculas separadas por sublinhado) utilizando a função
janitor::clean_names() logo na ingestão, evitando erros de
digitação e problemas com espaços ou caracteres especiais.
Verbosidade Explícita: Preferência por verbos
descritivos (mutate, filter,
select, group_by) em vez de indexação numérica
(ex: df[,1]), garantindo que o código explique o
que está fazendo.
Abaixo, inspecionamos as estruturas fundamentais que sustentam nossa análise. O projeto baseou-se principalmente em Tibbles (dataframes modernos do R) e Fatores para variáveis categóricas e clusters.
# Verificação técnica das classes das colunas do dataset final
# Demonstra controle sobre os tipos de dados (numérico vs fator vs texto)
glimpse(dataset_final)## Rows: 6,144,208
## Columns: 23
## $ pairing_id <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15…
## $ description <chr> "Delicate wine overwhelmed by red meat.", "Delica…
## $ quality_label <chr> "Terrible", "Poor", "Neutral", "Excellent", "Terr…
## $ pairing_quality <dbl> 1, 2, 3, 5, 1, 2, 3, 5, 1, 2, 3, 5, 1, 2, 3, 5, 1…
## $ cuisine <chr> "American BBQ", "American BBQ", "American BBQ", "…
## $ food_category <chr> "Red Meat", "Red Meat", "Red Meat", "Red Meat", "…
## $ food_item <chr> "bbq brisket", "bbq brisket", "bbq brisket", "bbq…
## $ wine_category <chr> "red", "red", "red", "red", "red", "red", "red", …
## $ wine_type <chr> "Barbera", "Barbera", "Barbera", "Barbera", "Barb…
## $ cluster <fct> 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4…
## $ wine_id <int> 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1…
## $ quality <dbl> 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6…
## $ alcohol <dbl> 12.4, 12.4, 12.4, 12.4, 12.4, 12.4, 12.4, 12.4, 1…
## $ sulphates <dbl> 0.93, 0.93, 0.93, 0.93, 0.93, 0.93, 0.93, 0.93, 0…
## $ p_h <dbl> 3.37, 3.37, 3.37, 3.37, 3.37, 3.37, 3.37, 3.37, 3…
## $ density <dbl> 0.9934, 0.9934, 0.9934, 0.9934, 0.9934, 0.9934, 0…
## $ total_sulfur_dioxide <dbl> 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 3…
## $ free_sulfur_dioxide <dbl> 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 1…
## $ chlorides <dbl> 0.23, 0.23, 0.23, 0.23, 0.23, 0.23, 0.23, 0.23, 0…
## $ residual_sugar <dbl> 2.2, 2.2, 2.2, 2.2, 2.2, 2.2, 2.2, 2.2, 2.2, 2.2,…
## $ citric_acid <dbl> 0.53, 0.53, 0.53, 0.53, 0.53, 0.53, 0.53, 0.53, 0…
## $ volatile_acidity <dbl> 0.36, 0.36, 0.36, 0.36, 0.36, 0.36, 0.36, 0.36, 0…
## $ fixed_acidity <dbl> 6.4, 6.4, 6.4, 6.4, 6.4, 6.4, 6.4, 6.4, 6.4, 6.4,…
# Verificação da estrutura do objeto de clusterização (prova de conceito)
# Mostra que tratamos 'cluster' e 'quality' como fatores para análise categórica
str(wine_cluster_red$cluster)## Factor w/ 4 levels "1","2","3","4": 4 4 4 4 4 4 4 4 4 4 ...
Para superar as limitações dos dados e enriquecer a visualização, foram aplicadas as seguintes soluções criativas e ferramentas avançadas:
Solução para Falta de Chave Primária (K-Means): O desafio técnico mais significativo foi a ausência de uma chave ligando a tabela química à tabela de harmonização. A solução criativa foi utilizar Aprendizado de Máquina Não Supervisionado (K-Means) para criar um “Cluster Representativo”. Isso permitiu uma junção baseada em similaridade estatística, e não em correspondência exata, viabilizando a análise.
Visualização Interativa (plotly): A implementação de gráficos interativos com tooltips personalizados (HTML) permite que o usuário explore dados densos sem poluir a visualização estática.
Mineração de Texto (N-Grams): Em vez de uma nuvem de palavras simples, utilizamos a tokenização em bigramas (ngrams) para capturar o contexto gastronômico (ex: diferenciar “tomato sauce” de “cream sauce”).
Por fim, para garantir a reprodutibilidade total deste estudo, listamos abaixo o ambiente de sessão e as versões dos pacotes utilizados:
## R version 4.5.1 (2025-06-13 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 11 x64 (build 26100)
##
## Matrix products: default
## LAPACK version 3.12.1
##
## locale:
## [1] LC_COLLATE=Portuguese_Brazil.utf8 LC_CTYPE=Portuguese_Brazil.utf8
## [3] LC_MONETARY=Portuguese_Brazil.utf8 LC_NUMERIC=C
## [5] LC_TIME=Portuguese_Brazil.utf8
##
## time zone: America/Fortaleza
## tzcode source: internal
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] tidytext_0.4.3 reshape2_1.4.4 plotly_4.11.0 patchwork_1.3.2
## [5] knitr_1.50 skimr_2.2.1 janitor_2.2.1 lubridate_1.9.4
## [9] forcats_1.0.1 stringr_1.5.2 dplyr_1.1.4 purrr_1.2.0
## [13] readr_2.1.5 tidyr_1.3.1 tibble_3.3.0 ggplot2_4.0.0
## [17] tidyverse_2.0.0
##
## loaded via a namespace (and not attached):
## [1] gtable_0.3.6 xfun_0.53 bslib_0.9.0 htmlwidgets_1.6.4
## [5] lattice_0.22-7 tzdb_0.5.0 crosstalk_1.2.2 vctrs_0.6.5
## [9] tools_4.5.1 generics_0.1.4 parallel_4.5.1 janeaustenr_1.0.0
## [13] pkgconfig_2.0.3 tokenizers_0.3.0 Matrix_1.7-3 data.table_1.17.8
## [17] RColorBrewer_1.1-3 S7_0.2.0 lifecycle_1.0.4 compiler_4.5.1
## [21] farver_2.1.2 repr_1.1.7 snakecase_0.11.1 htmltools_0.5.8.1
## [25] SnowballC_0.7.1 sass_0.4.10 yaml_2.3.10 lazyeval_0.2.2
## [29] pillar_1.11.1 crayon_1.5.3 jquerylib_0.1.4 cachem_1.1.0
## [33] tidyselect_1.2.1 digest_0.6.37 stringi_1.8.7 labeling_0.4.3
## [37] fastmap_1.2.0 grid_4.5.1 cli_3.6.5 magrittr_2.0.4
## [41] base64enc_0.1-3 withr_3.0.2 scales_1.4.0 bit64_4.6.0-1
## [45] timechange_0.3.0 rmarkdown_2.30 httr_1.4.7 bit_4.6.0
## [49] hms_1.1.4 evaluate_1.0.5 viridisLite_0.4.2 rlang_1.1.6
## [53] Rcpp_1.1.0 glue_1.8.0 rstudioapi_0.17.1 vroom_1.6.6
## [57] jsonlite_2.0.0 R6_2.6.1 plyr_1.8.9