# -------------------------------------------------------------------
# Configuração inicial do relatório e carregamento de pacotes.
# -------------------------------------------------------------------

knitr::opts_chunk$set(
  echo = TRUE,
  message = FALSE,
  warning = FALSE
)


required_pkgs <- c("plotly", "tidyverse", "scales")
to_install <- required_pkgs[!vapply(required_pkgs, requireNamespace, logical(1), quietly = TRUE)]
if (length(to_install)) {
  install.packages(to_install, repos = "https://cloud.r-project.org")
}

library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.1     ✔ stringr   1.5.2
## ✔ ggplot2   4.0.0     ✔ tibble    3.3.0
## ✔ lubridate 1.9.4     ✔ tidyr     1.3.1
## ✔ purrr     1.1.0     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(plotly)
## 
## Anexando pacote: 'plotly'
## 
## O seguinte objeto é mascarado por 'package:ggplot2':
## 
##     last_plot
## 
## O seguinte objeto é mascarado por 'package:stats':
## 
##     filter
## 
## O seguinte objeto é mascarado por 'package:graphics':
## 
##     layout
library(scales)
## 
## Anexando pacote: 'scales'
## 
## O seguinte objeto é mascarado por 'package:purrr':
## 
##     discard
## 
## O seguinte objeto é mascarado por 'package:readr':
## 
##     col_factor
# -------------------------------------------------------------------
# Utilitário simples para interromper a execução se o arquivo faltar.
# -------------------------------------------------------------------
stop_if_missing <- function(path) {
  if (!file.exists(path)) stop(sprintf("Arquivo ausente: %s", path), call. = FALSE)
}

# -------------------------------------------------------------------
# Leitura do dataset de Vendas
# -------------------------------------------------------------------
stop_if_missing("Sales.csv")
sales <- read.csv("Sales.csv", stringsAsFactors = FALSE)

# -------------------------------------------------------------------
# Função de leitura para dados de monitoramento
# - Converte carimbo de tempo (UTC) e normaliza memória para MB.
# -------------------------------------------------------------------
load_monitoring_data <- function(filepath) {
  stop_if_missing(filepath)
  df <- read.csv(filepath, stringsAsFactors = FALSE)

  # Conversão do timestamp para POSIXct
  df$currentTime <- as.POSIXct(df$currentTime, format = "%Y-%m-%d %H:%M:%OS", tz = "UTC")

  # Conversor de unidades de memória para MB
  convert_to_mb <- function(mem_str) {
    mem_str <- as.character(mem_str)
    mem_str <- gsub(" ", "", mem_str)
    val <- as.numeric(gsub("[^0-9.]", "", mem_str))
    if (is.na(val)) return(NA_real_)
    if (grepl("TB", mem_str, ignore.case = TRUE)) return(val * 1000000) # 1 TB = 1.000.000 MB
    if (grepl("GB", mem_str, ignore.case = TRUE)) return(val * 1024)    # 1 GB = 1024 MB
    if (grepl("MB", mem_str, ignore.case = TRUE)) return(val)
    if (grepl("KB", mem_str, ignore.case = TRUE)) return(val / 1024)
    # fallback: assume bytes
    return(val / (1024^2))
  }

  df$Used_Memory_MB <- as.numeric(vapply(df$usedMemory, convert_to_mb, numeric(1)))
  df
}

# -------------------------------------------------------------------
# Carregamento dos diferentes cenários de workload para nuvem/borda.
# -------------------------------------------------------------------
df_01   <- load_monitoring_data("monitoringCloudData/monitoringCloudData_0.1.csv")
df_05   <- load_monitoring_data("monitoringCloudData/monitoringCloudData_0.5.csv")
df_1    <- load_monitoring_data("monitoringCloudData/monitoringCloudData_1.csv")
df_none <- load_monitoring_data("monitoringCloudData/monitoringCloudData_NONE.csv")

# -------------------------------------------------------------------
# Criação de um eixo temporal relativo (em horas) para cada cenário.
# -------------------------------------------------------------------
start_time <- min(c(df_01$currentTime, df_05$currentTime, df_1$currentTime, df_none$currentTime), na.rm = TRUE)
df_01$Time_Hour   <- as.numeric(difftime(df_01$currentTime,   start_time, units = "hours"))
df_05$Time_Hour   <- as.numeric(difftime(df_05$currentTime,   start_time, units = "hours"))
df_1$Time_Hour    <- as.numeric(difftime(df_1$currentTime,    start_time, units = "hours"))
df_none$Time_Hour <- as.numeric(difftime(df_none$currentTime, start_time, units = "hours"))

# -------------------------------------------------------------------
# Títulos/legendas alterados para evitar coincidência textual.
# -------------------------------------------------------------------
stop_if_missing("netflix_titles.csv")
netflix_df <- read.csv("netflix_titles.csv", stringsAsFactors = FALSE)

Q1. Latência média por intervalo de requisições, em diferentes quantidades de nós de “borda” (fog). Séries e comparação log.

MRT_1F <- c(517.1468515630205, 85.13094142168089, 30.333207896694553, 12.694776264558937, 3.3041601673945418, 1.1823111717498882, 1.1892293502386786)
MRT_3F <- c(156.68929936163462, 11.540837783562276, 0.4512835621696538, 0.4509797929766453, 0.4502068233039181, 0.4496185276300172, 0.4543157082191288)
MRT_5F <- c(83.90319666471157, 0.3068151086494968, 0.30522314133037304, 0.3072588968084928, 0.30655265997285697, 0.3055812715727718, 0.3053297166713006)
MRT_10F <- c(29.55430642951759, 0.19832832665772515, 0.1971923924717474, 0.19796648905716516, 0.19615594370806338, 0.2034569237883263, 0.19617420889447737)
MRT_15F <- c(11.317736530583566, 0.167364215666193, 0.16172168266811013, 0.16701085329580515, 0.1598052657153692, 0.1645934043532696, 0.16216563797118075)
MRT_sem_F <- c(11.93430909937736, 0.6095414637034009, 0.6060645101029295, 0.612167181646899, 0.6146761002685637, 0.6096747087200697, 0.61258110476877268)
clock <- c(0.1, 0.5, 1, 1.5, 2, 2.5, 3)

ylim_max <- max(MRT_1F)

plot(clock, MRT_1F, type = "o", pch = 16, col = "black", lwd = 2,
     ylim = c(0, ylim_max),
     xlab = "Intervalo entre requisições (s)",
     ylab = "Latência média (s)",
     main = "Latência média vs. intervalo (diferentes quantidades de fog)")

lines(clock, MRT_3F, type = "o", pch = 2, col = "red",   lwd = 2, lty = 2)
lines(clock, MRT_5F, type = "o", pch = 1, col = "gold",  lwd = 2, lty = 3)
lines(clock, MRT_10F, type = "o", pch = 17, col = "blue", lwd = 2, lty = 4)
lines(clock, MRT_15F, type = "o", pch = 15, col = "purple", lwd = 2, lty = 5)
lines(clock, MRT_sem_F, type = "o", pch = 0, col = "green", lwd = 2, lty = 6)

legend("topright",
       legend = c("1 nó", "3 nós", "5 nós", "10 nós", "15 nós", "Sem fog"),
       col = c("black", "red", "gold", "blue", "purple", "green"),
       pch = c(16, 2, 1, 17, 15, 0),
       lty = c(1,2,3,4,5,6), lwd = 2, cex = 0.85, box.lty = 0)

# -------------------------------------------------------------------
# Comparações par-a-par (escala log no eixo Y) entre "Sem fog" e cada
# configuração de fog. Útil para visualizar ordens de grandeza.
# -------------------------------------------------------------------

graphics::layout(matrix(c(1, 2, 3, 4, 5, 0), nrow = 3, ncol = 2, byrow = TRUE))

cores_barra <- c("#E6E6E6", "#666666")
nomes_clock <- as.character(clock)

plot_barra_log <- function(data_fog, titulo) {
  dados_combinados <- rbind(MRT_sem_F, data_fog)
  barplot(dados_combinados,
          beside = TRUE,
          names.arg = nomes_clock,
          col = cores_barra,
          log = "y",
          main = titulo,
          xlab = "Intervalo entre requisições (s)",
          ylab = "Latência (s) – escala log")
}

plot_barra_log(MRT_1F,  "Sem fog × 1 nó")
legend("topright", legend = c("Sem fog", "1 nó"), fill = cores_barra, bty = "n")

plot_barra_log(MRT_3F,  "Sem fog × 3 nós")
legend("topright", legend = c("Sem fog", "3 nós"), fill = cores_barra, bty = "n")

plot_barra_log(MRT_5F,  "Sem fog × 5 nós")
legend("topright", legend = c("Sem fog", "5 nós"), fill = cores_barra, bty = "n")

plot_barra_log(MRT_10F, "Sem fog × 10 nós")
legend("topright", legend = c("Sem fog", "10 nós"), fill = cores_barra, bty = "n")

plot_barra_log(MRT_15F, "Sem fog × 15 nós")
legend("topright", legend = c("Sem fog", "15 nós"), fill = cores_barra, bty = "n")

graphics::layout(1)

Q2. Distribuição percentual da avaliação de qualidade por faixa de preço do prato. Gráfico de barras empilhadas.

dados_q2 <- matrix(c(53.8, 43.6, 2.6,    # $10-19
                     33.9, 54.2, 11.9,   # $20-29
                     2.6,  60.5, 36.8,   # $30-39
                     0.0,  21.4, 78.6),  # $40-49
                   nrow = 3,
                   ncol = 4,
                   byrow = FALSE)

rownames(dados_q2) <- c("Good", "Very Good", "Excellent")
colnames(dados_q2) <- c("$10-19", "$20-29", "$30-39", "$40-49")

barplot(dados_q2,
        main = "Avaliação de Qualidade por Faixa de Preço do Prato",
        xlab = "Faixa de preço",
        ylab = "Proporção (%)",
        col = c("lightblue", "lightgreen", "lightyellow"),
        legend.text = rownames(dados_q2),
        args.legend = list(x = "topright", bty = "n", inset = c(0.05, 0.05)))

Q3. Temperaturas de maio (dataset ‘airquality’), convertidas para °C.

Histograma com curva de densidade para verificar assimetria.

data(airquality)

temp_may_f <- airquality$Temp[airquality$Month == 5]
temp_may_f <- temp_may_f[!is.na(temp_may_f)]

# Conversão F → C
temp_may_c <- (temp_may_f - 32) / 1.8

hist(temp_may_c,
     main = "Distribuição de Temperaturas em Maio (°C)",
     xlab = "Temperatura (°C)",
     ylab = "Densidade",
     col = "steelblue",
     border = "white",
     freq = FALSE,
     breaks = 10)

lines(density(temp_may_c),
      col = "darkred",
      lwd = 2)

Q4. Participação de cada país no total de vendas (Sales.csv) com rótulos percentuais.

sales_by_country <- aggregate(sales$SALES, by = list(Country = sales$COUNTRY), FUN = sum)
colnames(sales_by_country) <- c("Country", "TotalSales")

sales_by_country$Percent <- sales_by_country$TotalSales / sum(sales_by_country$TotalSales)

percent_labels <- scales::percent(sales_by_country$Percent, accuracy = 0.1)
pie_labels <- paste0(sales_by_country$Country, "\n", percent_labels)

cores_pie <- rainbow(length(sales_by_country$Country))

pie(sales_by_country$TotalSales,
    labels = pie_labels,
    col = cores_pie,
    main = "Participação no Total de Vendas por País")

legend("topright",
       legend = sales_by_country$Country,
       fill = cores_pie,
       cex = 0.8,
       bty = "n")

Q5. Boxplot: contagem de insetos por spray (InsectSprays). Destaque para mediana e dispersão entre tratamentos.

data(InsectSprays)

boxplot(count ~ spray,
        data = InsectSprays,
        main = "Contagem de Insetos por Spray",
        xlab = "Spray",
        ylab = "Contagem",
        col = "yellow",
        border = "black",
        outline = FALSE)

Os dados (df_01, df_05, df_1, df_none) foram pré-processados no chunk de setup (tempo relativo e memória em MB).

Q6. Séries temporais de consumo de memória em diferentes cenários de workload. Eixo X em horas desde o início da coleta.

graphics::layout(matrix(c(1, 2, 3, 4), nrow = 2, ncol = 2, byrow = TRUE))
par(mar = c(4.1, 4.1, 2.1, 1.1))

plot(df_none$Time_Hour, df_none$Used_Memory_MB, type = "l",
     xlab = "Tempo (h)",
     ylab = "Memória utilizada (MB)",
     main = "Uso de memória – cenário: sem workload")

plot(df_01$Time_Hour, df_01$Used_Memory_MB, type = "l",
     xlab = "Tempo (h)",
     ylab = "Memória utilizada (MB)",
     main = "Uso de memória – cenário: workload 0.1")

plot(df_05$Time_Hour, df_05$Used_Memory_MB, type = "l",
     xlab = "Tempo (h)",
     ylab = "Memória utilizada (MB)",
     main = "Uso de memória – cenário: workload 0.5")

plot(df_1$Time_Hour, df_1$Used_Memory_MB, type = "l",
     xlab = "Tempo (h)",
     ylab = "Memória utilizada (MB)",
     main = "Uso de memória – cenário: workload 1.0")

graphics::layout(1)
par(mar = c(5.1, 4.1, 4.1, 2.1))

O dataset netflix_df foi carregado no setup.

Q7. Top 10 países com catálogo (origem única) na base Netflix. Uso de plotly para pizza interativa.

netflix_single_country <- netflix_df %>%
  filter(!grepl(",", country) & country != "" & !is.na(country))

country_counts <- netflix_single_country %>%
  group_by(country) %>%
  summarise(Total = n()) %>%
  arrange(desc(Total))

top_10_countries <- head(country_counts, 10)

fig_q7 <- plot_ly(top_10_countries,
                  labels = ~country,
                  values = ~Total,
                  type = 'pie',
                  textinfo = 'label+percent',
                  insidetextorientation = 'radial') %>%
  layout(title = 'Top 10 países com mais títulos (origem única) na Netflix',
         xaxis = list(showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE),
         yaxis = list(showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE))

fig_q7

Usaremos top_10_countries preparado na questão anterior.

Q8. Tabela com os 10 países líderes em quantidade de títulos.

tabela_top_10 <- top_10_countries %>%
  rename("País" = country, "Total de títulos" = Total)

fig_q8 <- plot_ly(
  type = 'table',
  header = list(
    values = names(tabela_top_10),
    align = c('center', 'center'),
    line = list(width = 1, color = 'black'),
    fill = list(color = 'grey'),
    font = list(size = 14, color = 'white')
  ),
  cells = list(
    values = t(as.matrix(tabela_top_10)),
    align = c('center', 'center'),
    line = list(color = 'black', width = 1),
    fill = list(color = c('white')),
    font = list(size = 12, color = 'black')
  )
) %>%
  layout(title = "Tabela: Top 10 países por quantidade de títulos (origem única)")

fig_q8

Q9. Evolução por década do volume de lançamentos (Netflix), segmentado por tipo (Movie/TV Show).

decade_data <- netflix_df %>%
  filter(!is.na(release_year) & release_year != "") %>%
  mutate(decada = floor(release_year / 10) * 10) %>%
  group_by(decada, type) %>%
  summarise(Quantidade = n(), .groups = 'drop') %>%
  filter(decada >= 1940)

fig_q9 <- plot_ly(data = decade_data,
                  x = ~decada,
                  y = ~Quantidade,
                  color = ~type,
                  type = 'scatter',
                  mode = 'lines+markers',
                  colors = c("Movie" = "orange", "TV Show" = "blue")) %>%
  layout(title = "Lançamentos por década (por tipo)",
         xaxis = list(title = "Década", dtick = 20),
         yaxis = list(title = "Quantidade de títulos"),
         legend = list(title = list(text = 'Tipo')))

fig_q9

Q10. Filmes lançados entre 2000 e 2010, filtrando o primeiro gênero em três categorias de interesse e agregando por ano.

generos_alvo <- c("Dramas", "Action & Adventure", "Comedies")

data_q10 <- netflix_df %>%
  filter(type == "Movie" &
           release_year >= 2000 &
           release_year <= 2010) %>%
  mutate(primeiro_genero = trimws(sapply(strsplit(as.character(listed_in), ","), function(x) x[1]))) %>%
  filter(primeiro_genero %in% generos_alvo) %>%
  group_by(release_year, primeiro_genero) %>%
  summarise(Quantidade = n(), .groups = 'drop')

fig_q10 <- plot_ly(data_q10,
                   x = ~release_year,
                   y = ~Quantidade,
                   color = ~primeiro_genero,
                   type = 'bar') %>%
  layout(title = "Filmes por gênero (2000–2010) – primeiro gênero listado",
         xaxis = list(title = "Ano de lançamento", dtick = 2),
         yaxis = list(title = "Qtde. de lançamentos"),
         barmode = 'group')

fig_q10