1. Introdução e Objetivos

1.1 Contexto

Este projeto utiliza uma base de dados do Internet Movie Database (IMDb) contendo informações sobre filmes, séries, atores, diretores e avaliações da comunidade. O dataset foi obtido no Kaggle (https://www.kaggle.com/datasets/ashirwadsangwan/imdb-dataset) e totaliza mais de 5GB de dados.

A base de dados contém 12.083.771 títulos únicos no total, dos quais 1.603.100 possuem avaliações registradas pela comunidade do IMDb.

Diante dessa vasta quantidade de informações sobre a indústria cinematográfica e televisiva, este projeto se propõe a realizar uma análise exploratória profunda desses dados, buscando extrair insights relevantes sobre padrões de produção, preferências do público e fatores que influenciam o sucesso de títulos audiovisuais.

1.2 Base de Dados

O dataset original do IMDb disponibilizado no Kaggle contém 5 tabelas:

  1. title.basics.tsv: Informações básicas sobre títulos (filmes/séries), incluindo título original, ano de lançamento, duração, gêneros e tipo de produção
  2. title.ratings.tsv: Notas médias atribuídas pela comunidade e número total de votos para cada título
  3. title.akas.tsv: Títulos alternativos e traduções em diferentes idiomas e regiões
  4. name.basics.tsv: Informações sobre pessoas (atores, diretores, roteiristas), incluindo nome, ano de nascimento, profissões e títulos pelos quais são conhecidas
  5. title.principals.tsv: Equipe principal de cada produção, relacionando pessoas aos títulos, suas funções (ator, diretor, produtor, etc.) e personagens interpretados

Tabelas Selecionadas

Para este projeto, serão utilizadas apenas as 3 primeiras tabelas (title.basics, title.ratings e title.akas). As tabelas de pessoas (name.basics e title.principals) foram excluídas da análise pelos seguintes motivos:

  • Explosão de registros: Cada título possui múltiplos profissionais associados (atores, diretores, roteiristas, produtores). Ao realizar um JOIN dessas tabelas com a tabela principal, um único filme com 10 pessoas no elenco principal geraria 10 linhas na tabela final, multiplicando artificialmente o volume de dados.

  • Dificuldade em agregações: Com registros duplicados por título, cálculos como média de ratings, contagem de produções por ano ou distribuição de gêneros ficariam distorcidos, exigindo tratamentos adicionais como agrupamentos e distinct.

  • Complexidade desnecessária: O objetivo principal deste estudo é analisar características dos títulos (gênero, duração, avaliações, distribuição geográfica). Análises sobre o impacto de profissionais específicos, embora interessantes, fogem do escopo definido e demandariam uma estrutura de dados diferente.

Dessa forma, a tabela final consolidada terá uma linha por título, facilitando a geração de estimativas, visualizações e conclusões de forma direta.

1.3 Objetivos

Este projeto visa realizar uma análise exploratória profunda dos dados cinematográficos do IMDb. Devido ao grande volume de dados (mais de 12 milhões de títulos), o estudo será limitado aos conteúdos produzidos no intervalo de 2020 a 2025, permitindo um foco em produções recentes e tendências contemporâneas.

Durante o processo de preparação dos dados, as 3 tabelas selecionadas serão unificadas em uma única estrutura integrada, onde cada linha representa um título único. Essa abordagem facilita a análise e permite gerar estimativas e visualizações de forma direta, sem a complexidade adicional que surgiria ao incluir dados de múltiplos profissionais por título.

Com essa estratégia de filtragem e consolidação, os objetivos específicos da análise são:

Análise Temporal e de Tendências:

  • Identificar padrões e tendências na produção audiovisual recente (2020-2025)
  • Observar mudanças nas preferências de gêneros cinematográficos ao longo do tempo
  • Analisar a evolução da duração média das produções

Análise de Sucesso e Avaliações:

  • Identificar características comuns em produções bem avaliadas
  • Analisar a relação entre características dos filmes (gênero, duração) e suas avaliações
  • Investigar fatores que contribuem para o sucesso de uma produção junto ao público
  • Descobrir correlações entre variáveis como duração, gênero, ano de lançamento e nota média

Análise de Mercado e Distribuição:

  • Explorar a distribuição de produções por região e idioma
  • Compreender a distribuição geográfica e linguística das produções
  • Analisar a presença de títulos em múltiplos mercados através de traduções

2. Pacotes Requeridos

# pacotes necessários
library(dplyr)
library(readr)
library(ggplot2)
library(tidyr)
library(stringr)
library(scales)

3. Preparação dos Dados

3.1 Descrição das Tabelas

As 3 tabelas utilizadas neste projeto possuem a seguinte estrutura:

3.1.1 title.basics.tsv

Contém informações básicas sobre títulos (filmes, séries, episódios):

  • tconst (string): Identificador alfanumérico único do título
  • titleType (string): Tipo/formato do título (movie, short, tvSeries, tvEpisode, video, etc.)
  • primaryTitle (string): Título mais popular / título usado pelos cineastas em materiais promocionais no lançamento
  • originalTitle (string): Título original, no idioma original
  • isAdult (boolean): 0 = título não adulto; 1 = título adulto
  • startYear (YYYY): Ano de lançamento do título. Para séries de TV, é o ano de início da série
  • endYear (YYYY): Ano de término para séries de TV; vazio para outros tipos de título
  • runtimeMinutes (integer): Duração principal do título, em minutos
  • genres (array): Até três gêneros associados ao título

3.1.2 title.ratings.tsv

Contém as avaliações e votos do IMDb para os títulos:

  • tconst (string): Identificador alfanumérico único do título
  • averageRating (float): Média ponderada de todas as avaliações individuais dos usuários
  • numVotes (integer): Número de votos que o título recebeu

3.1.3 title.akas.tsv

Contém títulos alternativos e traduções:

  • titleId (string): Um tconst, identificador alfanumérico único do título
  • ordering (integer): Número para identificar exclusivamente linhas para um dado titleId
  • title (string): Título localizado
  • region (string): Região para esta versão do título
  • language (string): Idioma do título
  • types (array): Conjunto de atributos para este título alternativo (“alternative”, “dvd”, “festival”, “tv”, “video”, “working”, “original”, “imdbDisplay”)
  • attributes (array): Termos adicionais para descrever este título alternativo
  • isOriginalTitle (boolean): 0 = não é título original; 1 = título original

Observações:

  • Valores ausentes são representados por “\N” nos arquivos originais
  • O identificador tconst é a chave primária que permite relacionar as 3 tabelas entre si
  • As datas são representadas apenas por ano (formato YYYY)
  • Os arquivos originais estão em formato compactado (.gz)

3.2 Carregamento e Importação dos Dados

Nesta etapa, realizaremos a importação das 3 tabelas do IMDb para o ambiente R. O código verifica se os arquivos já foram processados anteriormente (formato .rds) para otimizar o tempo de execução.

Configuração

Definição dos caminhos dos arquivos originais (.tsv) e processados (.rds):

Tipo Arquivo Caminho
Original title.basics.tsv C:/Users/Rodrigo/Downloads/
Original title.ratings.tsv C:/Users/Rodrigo/Downloads/
Original title.akas.tsv C:/Users/Rodrigo/Downloads/
Processado tabela_final.rds C:/Users/Rodrigo/Downloads/

Estratégia de cache: Se a tabela final (tabela_final.rds) já existir, ela é carregada diretamente, pulando todo o processamento.

title.basics

Tabela principal contendo informações básicas sobre os títulos.

Processamento:

  1. Carregar arquivo .tsv (ou .rds se já existir)
  2. Converter startYear para numérico
  3. Filtrar apenas títulos com startYear >= 2020 e <= 2025
  4. Salvar versão filtrada em .rds
if (file.exists(path_final_rds)) {
  cat(">>> Tabela final já existe. Pulando processamento de title.basics...\n")
} else {
  if (file.exists(path_basics_rds)) {
    cat("Carregando title.basics.filtrado.rds...\n")
    title_basics_filtrado <- readRDS(path_basics_rds)
    cat("Registros:", nrow(title_basics_filtrado), "\n")
  } else {
    cat("Carregando title.basics.tsv (isso pode demorar)...\n")
    title_basics <- read.delim(
      path_title_basics,
      sep = "\t",
      na.strings = "\\N",
      quote = "",
      stringsAsFactors = FALSE
    )
    cat("Total de registros:", nrow(title_basics), "\n")
    
    title_basics$startYear <- as.numeric(title_basics$startYear)
    title_basics_filtrado <- title_basics %>%
      filter(!is.na(startYear) & startYear >= 2020 & startYear <= 2025)
    
    cat("Após filtro (2020-2025):", nrow(title_basics_filtrado), "registros\n")
    
    rm(title_basics)
    gc()
    
    saveRDS(title_basics_filtrado, file = path_basics_rds, compress = "gzip")
    cat("Salvo em:", path_basics_rds, "\n")
  }
}
## Carregando title.basics.tsv (isso pode demorar)...
## Total de registros: 12101466 
## Após filtro (2020-2025): 2712516 registros
## Salvo em: C:/Users/Rodrigo/Downloads/title.basics.filtrado.rds

title.ratings

Tabela contendo as avaliações (notas e votos) dos títulos.

Processamento:

  1. Carregar arquivo .tsv (ou .rds se já existir)
  2. Filtrar apenas títulos que existem em title_basics_filtrado
  3. Salvar versão filtrada em .rds

Colunas extraídas: averageRating, numVotes

if (file.exists(path_final_rds)) {
  cat(">>> Tabela final já existe. Pulando processamento de title.ratings...\n")
} else {
  if (file.exists(path_ratings_rds)) {
    cat("Carregando title.ratings.filtrado.rds...\n")
    title_ratings_filtrado <- readRDS(path_ratings_rds)
    cat("Registros:", nrow(title_ratings_filtrado), "\n")
  } else {
    cat("Carregando title.ratings.tsv...\n")
    title_ratings <- read.delim(
      path_title_ratings,
      sep = "\t",
      na.strings = "\\N",
      quote = "",
      stringsAsFactors = FALSE
    )
    cat("Total de registros:", nrow(title_ratings), "\n")
    
    title_ratings_filtrado <- title_ratings %>%
      filter(tconst %in% title_basics_filtrado$tconst)
    
    cat("Após filtro (títulos de 2020-2025):", nrow(title_ratings_filtrado), "registros\n")
    
    rm(title_ratings)
    gc()
    
    saveRDS(title_ratings_filtrado, file = path_ratings_rds, compress = "gzip")
    cat("Salvo em:", path_ratings_rds, "\n")
  }
}
## Carregando title.ratings.tsv...
## Total de registros: 1605930 
## Após filtro (títulos de 2020-2025): 361262 registros
## Salvo em: C:/Users/Rodrigo/Downloads/title.ratings.filtrado.rds

title.akas

Tabela contendo títulos alternativos e traduções em diferentes regiões/idiomas.

Processamento:

  1. Carregar arquivo .tsv (ou .rds se já existir)
  2. Filtrar apenas títulos que existem em title_basics_filtrado
  3. Agregar todas as regiões por título (ignorando valores nulos)
  4. Salvar versão filtrada em .rds

Coluna extraída: regions (todas as regiões concatenadas, ex: "US,BR,GB,FR")

if (file.exists(path_final_rds)) {
  cat(">>> Tabela final já existe. Pulando processamento de title.akas...\n")
} else {
  if (file.exists(path_akas_rds)) {
    cat("Carregando title.akas.filtrado.rds...\n")
    title_akas_filtrado <- readRDS(path_akas_rds)
    cat("Registros:", nrow(title_akas_filtrado), "\n")
  } else {
    cat("Carregando title.akas.tsv (isso pode demorar)...\n")
    title_akas <- read.delim(
      path_title_akas,
      sep = "\t",
      na.strings = "\\N",
      quote = "",
      stringsAsFactors = FALSE
    )
    cat("Total de registros:", nrow(title_akas), "\n")
    
    title_akas_filtrado <- title_akas %>%
      filter(titleId %in% title_basics_filtrado$tconst)
    
    cat("Após filtro (títulos de 2020-2025):", nrow(title_akas_filtrado), "registros\n")
    
    rm(title_akas)
    gc()
    
    saveRDS(title_akas_filtrado, file = path_akas_rds, compress = "gzip")
    cat("Salvo em:", path_akas_rds, "\n")
  }
}
## Carregando title.akas.tsv (isso pode demorar)...
## Total de registros: 54158849 
## Após filtro (títulos de 2020-2025): 10740154 registros
## Salvo em: C:/Users/Rodrigo/Downloads/title.akas.filtrado.rds

Tabela Final

Consolidação das 3 tabelas em uma estrutura única, onde cada linha representa um título.

Etapas:

  1. Adicionar averageRating e numVotes de title.ratings
  2. Agregar e adicionar regions de title.akas
  3. Agregar e adicionar languages de title.akas
  4. Renomear startYear para releaseYear
  5. Remover coluna endYear (desnecessária)
  6. Salvar tabela final em .rds
if (file.exists(path_final_rds)) {
  cat(">>> Carregando tabela final existente...\n")
  title_basics_filtrado <- readRDS(path_final_rds)
  cat("Tabela carregada com sucesso!\n")
} else {
  cat(">>> Consolidando tabelas...\n\n")
  
  # Adicionar ratings
  cat("Adicionando ratings à tabela principal...\n")
  title_basics_filtrado <- title_basics_filtrado %>%
    left_join(title_ratings_filtrado %>% select(tconst, averageRating, numVotes), by = "tconst")
  
  n_com_rating <- sum(!is.na(title_basics_filtrado$averageRating))
  cat("Títulos com rating:", n_com_rating, "(", round(n_com_rating/nrow(title_basics_filtrado)*100, 1), "%)\n")
  
  rm(title_ratings_filtrado)
  gc()
  
  # Agregar e adicionar regions
  cat("\nAgregando regiões por título...\n")
  regions_por_titulo <- title_akas_filtrado %>%
    filter(!is.na(region) & region != "\\N" & region != "") %>%
    group_by(titleId) %>%
    summarise(regions = paste(unique(region), collapse = ","), .groups = "drop")
  
  cat("Títulos com informação de região:", nrow(regions_por_titulo), "\n")
  
  title_basics_filtrado <- title_basics_filtrado %>%
    left_join(regions_por_titulo, by = c("tconst" = "titleId"))
  
  rm(regions_por_titulo)
  gc()
  
  # Agregar e adicionar languages
  cat("\nAgregando idiomas por título...\n")
  languages_por_titulo <- title_akas_filtrado %>%
    filter(!is.na(language) & language != "\\N" & language != "") %>%
    group_by(titleId) %>%
    summarise(languages = paste(unique(language), collapse = ","), .groups = "drop")
  
  cat("Títulos com informação de idioma:", nrow(languages_por_titulo), "\n")
  
  title_basics_filtrado <- title_basics_filtrado %>%
    left_join(languages_por_titulo, by = c("tconst" = "titleId"))
  
  rm(languages_por_titulo, title_akas_filtrado)
  gc()
  
  # Renomear startYear para releaseYear
  title_basics_filtrado <- title_basics_filtrado %>%
    rename(releaseYear = startYear)
  
  cat("Coluna 'startYear' renomeada para 'releaseYear'.\n")
  
  # Remover colunas desnecessárias
  title_basics_filtrado <- title_basics_filtrado %>%
    select(-endYear)
  
  cat("Coluna 'endYear' removida.\n")
  
  # Salvar tabela final
  saveRDS(title_basics_filtrado, file = path_final_rds, compress = "gzip")
  tamanho_mb <- file.size(path_final_rds) / (1024^2)
  cat("\n>>> Tabela final salva em:", path_final_rds)
  cat("\n>>> Tamanho:", round(tamanho_mb, 2), "MB\n")
}
## >>> Consolidando tabelas...
## 
## Adicionando ratings à tabela principal...
## Títulos com rating: 361262 ( 13.3 %)
## 
## Agregando regiões por título...
## Títulos com informação de região: 1656711 
## 
## Agregando idiomas por título...
## Títulos com informação de idioma: 1042457 
## Coluna 'startYear' renomeada para 'releaseYear'.
## Coluna 'endYear' removida.
## 
## >>> Tabela final salva em: C:/Users/Rodrigo/Downloads/tabela_final.rds
## >>> Tamanho: 78.38 MB

Visualização

Amostra da tabela final consolidada:

cat("========== ESTRUTURA DA TABELA FINAL ==========\n")
## ========== ESTRUTURA DA TABELA FINAL ==========
cat("Dimensões:", nrow(title_basics_filtrado), "linhas x", ncol(title_basics_filtrado), "colunas\n")
## Dimensões: 2712516 linhas x 12 colunas
cat("Colunas:", paste(names(title_basics_filtrado), collapse = ", "), "\n\n")
## Colunas: tconst, titleType, primaryTitle, originalTitle, isAdult, releaseYear, runtimeMinutes, genres, averageRating, numVotes, regions, languages
n_com_rating <- sum(!is.na(title_basics_filtrado$averageRating))
n_com_regiao <- sum(!is.na(title_basics_filtrado$regions))
n_com_idioma <- sum(!is.na(title_basics_filtrado$languages))
cat("Títulos com rating:", n_com_rating, "(", round(n_com_rating/nrow(title_basics_filtrado)*100, 1), "%)\n")
## Títulos com rating: 361262 ( 13.3 %)
cat("Títulos com região:", n_com_regiao, "(", round(n_com_regiao/nrow(title_basics_filtrado)*100, 1), "%)\n")
## Títulos com região: 1656711 ( 61.1 %)
cat("Títulos com idioma:", n_com_idioma, "(", round(n_com_idioma/nrow(title_basics_filtrado)*100, 1), "%)\n\n")
## Títulos com idioma: 1042457 ( 38.4 %)
# Amostra da tabela
head(title_basics_filtrado, 15)

4. Análise Exploratória

Nesta seção, realizamos análises exploratórias para investigar padrões e tendências nos dados do IMDb. As análises estão organizadas em abas temáticas.

4.1 Visão Geral dos Dados

Panorama completo da composição da tabela final, organizado em sub-abas por tipo de variável.

Total de registros: 2.712.516 títulos

Categóricas

Distribuição das variáveis categóricas: tipo de título, conteúdo adulto e ano de lançamento.

cat("📌 titleType (Tipo de Título)\n")
## 📌 titleType (Tipo de Título)
title_basics_filtrado %>%
  count(titleType, sort = TRUE) %>%
  mutate(percentual = paste0(round(n / total_registros * 100, 2), "%"))
cat("\n📌 isAdult (Conteúdo Adulto)\n")
## 
## 📌 isAdult (Conteúdo Adulto)
title_basics_filtrado %>%
  count(isAdult, sort = TRUE) %>%
  mutate(
    isAdult = ifelse(isAdult == 0, "Não (0)", "Sim (1)"),
    percentual = paste0(round(n / total_registros * 100, 2), "%")
  )
cat("\n📌 releaseYear (Ano de Lançamento)\n")
## 
## 📌 releaseYear (Ano de Lançamento)
title_basics_filtrado %>%
  count(releaseYear, sort = FALSE) %>%
  mutate(percentual = paste0(round(n / total_registros * 100, 2), "%"))

Numéricas

Estatísticas das variáveis numéricas: nota média, número de votos e duração.

# Calcular estatísticas
n_com_rating <- sum(!is.na(title_basics_filtrado$averageRating))
n_com_votos <- sum(!is.na(title_basics_filtrado$numVotes))
n_com_duracao <- sum(!is.na(title_basics_filtrado$runtimeMinutes))

cat("📌 averageRating (Nota Média)\n")
## 📌 averageRating (Nota Média)
cat("   Preenchidos:", format(n_com_rating, big.mark = "."), 
    "(", round(n_com_rating/total_registros*100, 1), "%)\n")
##    Preenchidos: 361.262 ( 13.3 %)
summary(title_basics_filtrado$averageRating[!is.na(title_basics_filtrado$averageRating)])
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    1.00    6.40    7.30    7.16    8.10   10.00
cat("\n📌 numVotes (Número de Votos)\n")
## 
## 📌 numVotes (Número de Votos)
cat("   Preenchidos:", format(n_com_votos, big.mark = "."), 
    "(", round(n_com_votos/total_registros*100, 1), "%)\n")
##    Preenchidos: 361.262 ( 13.3 %)
summary(title_basics_filtrado$numVotes[!is.na(title_basics_filtrado$numVotes)])
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##       5      10      24     692      95  990248
cat("\n📌 runtimeMinutes (Duração em Minutos)\n")
## 
## 📌 runtimeMinutes (Duração em Minutos)
cat("   Preenchidos:", format(n_com_duracao, big.mark = "."), 
    "(", round(n_com_duracao/total_registros*100, 1), "%)\n")
##    Preenchidos: 820.905 ( 30.3 %)
summary(title_basics_filtrado$runtimeMinutes[!is.na(title_basics_filtrado$runtimeMinutes)])
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
##     1.00    15.00    30.00    44.94    60.00 43200.00

Compostas

Top 10 valores das variáveis compostas (múltiplos valores separados por vírgula): gêneros, regiões e idiomas.

cat("📌 genres - Top 10 Gêneros\n")
## 📌 genres - Top 10 Gêneros
title_basics_filtrado %>%
  filter(!is.na(genres) & genres != "") %>%
  separate_rows(genres, sep = ",") %>%
  mutate(genres = trimws(genres)) %>%
  count(genres, sort = TRUE) %>%
  head(10)
cat("\n📌 regions - Top 10 Regiões\n")
## 
## 📌 regions - Top 10 Regiões
title_basics_filtrado %>%
  filter(!is.na(regions)) %>%
  separate_rows(regions, sep = ",") %>%
  mutate(regions = trimws(regions)) %>%
  count(regions, sort = TRUE) %>%
  head(10)
cat("\n📌 languages - Top 10 Idiomas\n")
## 
## 📌 languages - Top 10 Idiomas
title_basics_filtrado %>%
  filter(!is.na(languages)) %>%
  separate_rows(languages, sep = ",") %>%
  mutate(languages = trimws(languages)) %>%
  count(languages, sort = TRUE) %>%
  head(10)

4.2 Popularidade vs Qualidade

Objetivo: Investigar a relação entre a popularidade de um título (medida pelo número de votos) e sua qualidade percebida (nota média). A pergunta central é: títulos mais populares são necessariamente melhores avaliados?

Metodologia: Utilizamos um gráfico de dispersão (scatter plot) com escala logarítmica no eixo X para melhor visualização, já que o número de votos possui grande variação. Adicionamos uma linha de tendência (regressão) para identificar o padrão geral.

# Filtrar títulos com rating e votos válidos
dados_pop_qual <- title_basics_filtrado %>%
  filter(!is.na(averageRating) & !is.na(numVotes) & numVotes > 0)

# Calcular correlação
correlacao <- cor(dados_pop_qual$numVotes, dados_pop_qual$averageRating, use = "complete.obs")

# Gráfico de dispersão: Popularidade vs Qualidade
ggplot(dados_pop_qual, aes(x = numVotes, y = averageRating)) +
  geom_point(alpha = 0.3, color = "#2E86AB", size = 1) +
  geom_smooth(method = "lm", color = "#E94F37", linewidth = 1.2, se = TRUE) +
  scale_x_log10(labels = label_number(scale_cut = cut_short_scale())) +
  labs(
    title = "Popularidade vs Qualidade: Títulos Populares São Melhores?",
    subtitle = paste0("Correlação de Pearson: ", round(correlacao, 3)),
    x = "Número de Votos (escala log)",
    y = "Nota Média (IMDb)",
    caption = "Fonte: IMDb | Títulos de 2020-2025"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(face = "bold", size = 14),
    plot.subtitle = element_text(color = "gray40"),
    panel.grid.minor = element_blank()
  )

Interpretação: A correlação de Pearson obtida foi de -0.006, um valor extremamente próximo de zero. Isso indica que não existe relação linear entre popularidade (número de votos) e qualidade (nota média) nos títulos do IMDb.

Em termos práticos, isso significa que:

  • Títulos muito votados não são necessariamente melhores avaliados — um filme com milhões de votos pode ter nota alta ou baixa;
  • Títulos pouco votados também variam amplamente em qualidade — obras de nicho podem ser excelentes ou medíocres;
  • Popularidade e qualidade são dimensões independentes — um título pode ser popular sem ser bem avaliado (ex: blockbusters controversos) ou ser excelente sem atrair grande audiência (ex: filmes cult).

O sinal negativo (-0.006) é estatisticamente irrelevante dado sua proximidade com zero, não indicando uma tendência real de queda. A linha de regressão praticamente horizontal no gráfico confirma essa ausência de correlação.

4.3 Gêneros em Ascensão

Objetivo: Identificar quais gêneros cinematográficos estão em crescimento ou declínio ao longo dos anos recentes (2020-2025). Esta análise revela tendências na produção audiovisual e mudanças nas preferências do mercado.

Metodologia: Separamos os gêneros (que estão concatenados) em linhas individuais, contamos a produção por gênero e ano, e visualizamos a evolução temporal dos 8 gêneros mais populares.

# Separar gêneros em linhas individuais
generos_por_ano <- title_basics_filtrado %>%
  filter(!is.na(genres) & genres != "") %>%
  separate_rows(genres, sep = ",") %>%
  mutate(genres = trimws(genres)) %>%
  filter(genres != "")

# Identificar os 15 gêneros mais comuns no total
top_generos <- generos_por_ano %>%
  count(genres, sort = TRUE) %>%
  head(15) %>%
  pull(genres)

# Contar produção por gênero e ano (apenas top gêneros)
evolucao_generos <- generos_por_ano %>%
  filter(genres %in% top_generos) %>%
  count(releaseYear, genres) %>%
  mutate(genres = factor(genres, levels = top_generos))

# Gráfico de linhas: Evolução dos gêneros
ggplot(evolucao_generos, aes(x = releaseYear, y = n, color = genres, group = genres)) +
  geom_line(linewidth = 1.2) +
  geom_point(size = 2.5) +
  scale_color_manual(values = c("#E41A1C", "#377EB8", "#4DAF4A", "#984EA3", "#FF7F00", 
                                "#FFFF33", "#A65628", "#F781BF", "#999999", "#66C2A5",
                                "#FC8D62", "#8DA0CB", "#E78AC3", "#A6D854", "#FFD92F")) +
  scale_x_continuous(limits = c(2020, 2025), breaks = 2020:2025) +
  scale_y_continuous(labels = label_number(scale_cut = cut_short_scale())) +
  labs(
    title = "Evolução da Produção por Gênero (2020-2025)",
    subtitle = "Top 15 gêneros mais produzidos",
    x = "Ano de Lançamento",
    y = "Quantidade de Títulos",
    color = "Gênero",
    caption = "Fonte: IMDb | Um título pode ter múltiplos gêneros"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(face = "bold", size = 14),
    plot.subtitle = element_text(color = "gray40"),
    legend.position = "right",
    panel.grid.minor = element_blank()
  )

Interpretação: Gêneros com linhas ascendentes indicam crescimento na produção, enquanto linhas descendentes sugerem declínio. Observe que um título pode pertencer a múltiplos gêneros, portanto os números representam participações e não títulos únicos.

4.4 Séries vs Filmes: Ratings

Objetivo: Comparar as avaliações médias entre filmes (movies) e séries de TV (tvSeries). Esta análise busca responder: qual formato tende a ser melhor avaliado pelo público?

Metodologia: Filtramos apenas os tipos “movie” e “tvSeries”, calculamos estatísticas descritivas e visualizamos a distribuição das notas através de boxplots e histogramas sobrepostos.

# Filtrar apenas movies e tvSeries com rating válido
filmes_series <- title_basics_filtrado %>%
  filter(titleType %in% c("movie", "tvSeries") & !is.na(averageRating)) %>%
  mutate(titleType = factor(titleType, 
                            levels = c("movie", "tvSeries"),
                            labels = c("Filmes", "Séries de TV")))

# Estatísticas por tipo
stats_rating <- filmes_series %>%
  group_by(titleType) %>%
  summarise(
    n = n(),
    media = mean(averageRating, na.rm = TRUE),
    mediana = median(averageRating, na.rm = TRUE),
    desvio = sd(averageRating, na.rm = TRUE),
    .groups = "drop"
  )

cat("========== ESTATÍSTICAS DE RATING ==========\n")
## ========== ESTATÍSTICAS DE RATING ==========
print(stats_rating)
## # A tibble: 2 × 5
##   titleType        n media mediana desvio
##   <fct>        <int> <dbl>   <dbl>  <dbl>
## 1 Filmes       60374  6.35     6.5   1.60
## 2 Séries de TV 25131  6.88     7.1   1.41
# Gráfico: Distribuição de ratings (histograma + boxplot)
ggplot(filmes_series, aes(x = averageRating, fill = titleType)) +
  geom_histogram(bins = 20, alpha = 0.6, position = "identity", color = "white") +
  geom_boxplot(aes(y = -500), width = 300, alpha = 0.8, outlier.shape = NA) +
  scale_fill_manual(values = c("Filmes" = "#3498DB", "Séries de TV" = "#E74C3C")) +
  labs(
    title = "Distribuição de Notas: Filmes vs Séries de TV",
    subtitle = paste0("Filmes (n=", format(stats_rating$n[1], big.mark="."), 
                      ") | Séries (n=", format(stats_rating$n[2], big.mark="."), ")"),
    x = "Nota Média (IMDb)",
    y = "Frequência",
    fill = "Tipo",
    caption = "Fonte: IMDb | Títulos de 2020-2025"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(face = "bold", size = 14),
    plot.subtitle = element_text(color = "gray40"),
    legend.position = "top",
    panel.grid.minor = element_blank()
  )

Interpretação: A comparação revela diferenças na forma como o público avalia filmes versus séries. Diferenças nas médias e medianas indicam preferências sistemáticas, enquanto a dispersão (desvio padrão) mostra a consistência das avaliações em cada formato.

4.5 Séries vs Filmes: Duração

Objetivo: Comparar a duração (em minutos) entre filmes e séries de TV. Para séries, a duração geralmente representa a duração média de um episódio.

Metodologia: Filtramos títulos com duração válida e removemos outliers extremos (títulos com mais de 300 minutos) para uma visualização mais clara. Utilizamos boxplots para comparar as distribuições.

# Filtrar movies e tvSeries com duração válida (remover outliers extremos)
filmes_series_duracao <- title_basics_filtrado %>%
  filter(titleType %in% c("movie", "tvSeries") & 
         !is.na(runtimeMinutes) & 
         runtimeMinutes > 0 & 
         runtimeMinutes <= 300) %>%
  mutate(titleType = factor(titleType, 
                            levels = c("movie", "tvSeries"),
                            labels = c("Filmes", "Séries de TV")))

# Estatísticas de duração por tipo
stats_duracao <- filmes_series_duracao %>%
  group_by(titleType) %>%
  summarise(
    n = n(),
    media = mean(runtimeMinutes, na.rm = TRUE),
    mediana = median(runtimeMinutes, na.rm = TRUE),
    minimo = min(runtimeMinutes, na.rm = TRUE),
    maximo = max(runtimeMinutes, na.rm = TRUE),
    .groups = "drop"
  )

cat("========== ESTATÍSTICAS DE DURAÇÃO (minutos) ==========\n")
## ========== ESTATÍSTICAS DE DURAÇÃO (minutos) ==========
print(stats_duracao)
## # A tibble: 2 × 6
##   titleType        n media mediana minimo maximo
##   <fct>        <int> <dbl>   <dbl>  <int>  <int>
## 1 Filmes       84188  90.3      90      1    300
## 2 Séries de TV 18497  42.6      35      1    300
# Gráfico: Boxplot + Violin plot de duração
ggplot(filmes_series_duracao, aes(x = titleType, y = runtimeMinutes, fill = titleType)) +
  geom_violin(alpha = 0.3, color = NA) +
  geom_boxplot(width = 0.2, alpha = 0.8, outlier.alpha = 0.3) +
  stat_summary(fun = mean, geom = "point", shape = 18, size = 4, color = "black") +
  scale_fill_manual(values = c("Filmes" = "#3498DB", "Séries de TV" = "#E74C3C")) +
  labs(
    title = "Distribuição de Duração: Filmes vs Séries de TV",
    subtitle = "O losango (◆) indica a média | Duração máxima limitada a 300 min",
    x = "",
    y = "Duração (minutos)",
    caption = "Fonte: IMDb | Títulos de 2020-2025 | Para séries, representa duração do episódio"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(face = "bold", size = 14),
    plot.subtitle = element_text(color = "gray40"),
    legend.position = "none",
    panel.grid.minor = element_blank()
  ) +
  coord_flip()

Interpretação: Filmes tipicamente têm duração maior que episódios de séries. A distribuição “violin” mostra onde se concentram a maioria dos títulos, enquanto o boxplot destaca mediana, quartis e valores atípicos.

4.6 Evolução da Duração Média por Ano

Objetivo: Analisar como a duração média de filmes, séries e curtas evoluiu ao longo dos anos (2020-2025). Esta análise permite identificar tendências temporais na duração das produções audiovisuais.

Metodologia: Calculamos a duração média por tipo de título e ano, filtrando apenas os tipos movie, tvSeries e short. Utilizamos um gráfico de linhas para visualizar a evolução temporal.

# Filtrar tipos desejados e calcular média de duração por ano
evolucao_duracao <- title_basics_filtrado %>%
  filter(titleType %in% c("movie", "tvSeries", "short") & 
         !is.na(runtimeMinutes) & 
         runtimeMinutes > 0 & 
         runtimeMinutes <= 300) %>%
  group_by(releaseYear, titleType) %>%
  summarise(
    duracao_media = mean(runtimeMinutes, na.rm = TRUE),
    n = n(),
    .groups = "drop"
  ) %>%
  mutate(titleType = factor(titleType, 
                            levels = c("movie", "tvSeries", "short"),
                            labels = c("Filmes", "Séries de TV", "Curtas")))

# Estatísticas por tipo e ano
cat("========== DURAÇÃO MÉDIA POR TIPO E ANO ==========\n")
## ========== DURAÇÃO MÉDIA POR TIPO E ANO ==========
evolucao_duracao %>%
  select(releaseYear, titleType, duracao_media, n) %>%
  mutate(duracao_media = round(duracao_media, 1)) %>%
  print(n = 20)
## # A tibble: 18 × 4
##    releaseYear titleType    duracao_media     n
##          <dbl> <fct>                <dbl> <int>
##  1        2020 Filmes                86.9 12445
##  2        2020 Curtas                12   29323
##  3        2020 Séries de TV          39.7  3914
##  4        2021 Filmes                88.7 13781
##  5        2021 Curtas                12.7 26810
##  6        2021 Séries de TV          41.8  3848
##  7        2022 Filmes                90.6 15066
##  8        2022 Curtas                13.2 25559
##  9        2022 Séries de TV          43.4  3462
## 10        2023 Filmes                91   15338
## 11        2023 Curtas                13.2 26007
## 12        2023 Séries de TV          43.6  2966
## 13        2024 Filmes                91.5 15172
## 14        2024 Curtas                13   24863
## 15        2024 Séries de TV          43.6  2462
## 16        2025 Filmes                92.9 12386
## 17        2025 Curtas                13.2 18744
## 18        2025 Séries de TV          45.9  1845
# Gráfico de linhas: Evolução da duração média
ggplot(evolucao_duracao, aes(x = releaseYear, y = duracao_media, color = titleType, group = titleType)) +
  geom_line(linewidth = 1.3) +
  geom_point(size = 3) +
  scale_color_manual(values = c("Filmes" = "#3498DB", "Séries de TV" = "#E74C3C", "Curtas" = "#2ECC71")) +
  scale_x_continuous(limits = c(2020, 2025), breaks = 2020:2025) +
  labs(
    title = "Evolução da Duração Média por Tipo de Título (2020-2025)",
    subtitle = "Comparação entre Filmes, Séries de TV e Curtas",
    x = "Ano de Lançamento",
    y = "Duração Média (minutos)",
    color = "Tipo",
    caption = "Fonte: IMDb | Para séries, representa duração média do episódio"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    plot.title = element_text(face = "bold", size = 14),
    plot.subtitle = element_text(color = "gray40"),
    legend.position = "top",
    panel.grid.minor = element_blank()
  )

Interpretação: O gráfico mostra a evolução da duração média ao longo dos anos para cada tipo de produção. Filmes tendem a ter duração maior (90-120 min), séries de TV apresentam duração intermediária (duração do episódio, ~40-60 min), e curtas têm duração reduzida (<40 min). Variações ao longo dos anos podem indicar mudanças nas preferências de consumo ou tendências de produção.

5. Conclusão

Este projeto realizou uma análise exploratória dos dados do IMDb, focando em produções audiovisuais do período de 2020 a 2025. A partir da consolidação de três tabelas (title.basics, title.ratings e title.akas), foi possível extrair insights relevantes sobre a indústria cinematográfica e televisiva recente.

Principais Descobertas

1. Popularidade vs Qualidade

A análise revelou uma correlação praticamente nula (-0.006) entre o número de votos e a nota média dos títulos. Isso indica que popularidade e qualidade são dimensões independentes — títulos muito votados não são necessariamente melhores avaliados, e obras de nicho podem ter excelentes avaliações mesmo com poucos votos.

2. Tendências de Gêneros

A evolução da produção por gênero mostrou quais categorias estão em crescimento ou declínio no período analisado. Esta análise permite identificar as preferências do mercado e as tendências contemporâneas na produção audiovisual.

3. Séries vs Filmes

A comparação entre filmes e séries de TV revelou diferenças nas distribuições de avaliações e durações. Filmes apresentam duração média maior (86-93 min), enquanto episódios de séries têm duração típica de 40-46 minutos.

4. Evolução da Duração

Uma descoberta interessante foi a tendência de aumento na duração tanto de filmes quanto de séries ao longo do período:

  • Filmes: de 86.9 min (2020) para 92.9 min (2025) — aumento de ~6 minutos
  • Séries de TV: de 39.7 min (2020) para 45.9 min (2025) — aumento de ~6 minutos
  • Curtas: permaneceram estáveis em torno de 12-13 minutos

Este padrão pode refletir mudanças no consumo via streaming, onde o público tem maior flexibilidade de tempo para consumir conteúdos mais longos.

Limitações

  • Período temporal: A análise foi limitada ao intervalo 2020-2025, não capturando tendências históricas mais amplas
  • Dados de 2025: O ano de 2025 ainda está em andamento, resultando em números parciais
  • Ausência de dados financeiros: Não foram analisados dados de bilheteria ou orçamento, que poderiam enriquecer a análise de sucesso
  • Exclusão de profissionais: As tabelas de elenco e equipe técnica foram excluídas para simplificar a estrutura de dados

Considerações Finais

A base de dados do IMDb mostrou-se uma fonte rica para análise exploratória da indústria audiovisual. Os padrões identificados contribuem para a compreensão das tendências recentes de produção e consumo de conteúdo. Análises futuras poderiam incorporar dados de streaming, redes sociais e bilheteria para uma visão ainda mais completa do mercado audiovisual.

Vale ressaltar que a tabela final consolidada oferece diversas possibilidades além das apresentadas neste estudo. Com as variáveis disponíveis (tipo, gênero, ano, duração, avaliações, regiões e idiomas), é possível extrair análises adicionais, cruzamentos de dados e novas visualizações conforme o interesse específico do pesquisador. Este trabalho representa apenas uma amostra do potencial analítico contido nos dados.