Neste relatório, as questões 1 a 6 utilizam gráficos do R base e as questões 7 a 10 utilizam Plotly, conforme o enunciado. Cada aba reúne explicação, código e visualização. Os gráficos da questão 6 são produzidos a partir dos quatro arquivos de monitoramento anexados.
Objetivo: comparar os tempos médios de resposta de diferentes quantidades de fogs em função do intervalo entre requisições. O gráfico de linhas mostra todas as séries. Os cinco gráficos de barras comparam cada quantidade de fog com a configuração sem fog, utilizando escala logarítmica e as duas cores especificadas no enunciado.
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.6125810476877268)
clock <- c(0.1, 0.5, 1, 1.5, 2, 2.5, 3)
series <- list("1 Fog" = MRT_1F, "3 Fogs" = MRT_3F,
"5 Fogs" = MRT_5F, "10 Fogs" = MRT_10F,
"15 Fogs" = MRT_15F, "Sem Fog" = MRT_sem_F)
cores_linha <- c("#424242", "#D79123", "#CEB01D",
"#4383B8", "#9B72BE", "#259D83")
# Primeiro gráfico ocupa as duas colunas; os demais ocupam células individuais.
graphics::layout(matrix(c(1, 1, 2, 3, 4, 5, 6, 0), ncol = 2, byrow = TRUE),
heights = c(2.8, 2, 2, 2))
par(mar = c(4, 4.5, 3, 1))
plot(clock, MRT_1F, type = "b", pch = 4, lwd = 1.5,
col = cores_linha[1], xlim = range(clock),
ylim = c(0, max(MRT_1F) * 1.04),
xlab = "Intervalo entre requisições (s)",
ylab = "Tempo de resposta (s)",
main = "Tempo de resposta por configuração")
for (i in 2:length(series)) {
lines(clock, series[[i]], type = "b", pch = i,
col = cores_linha[i], lwd = 1.5)
}
legend("topright", legend = names(series), col = cores_linha,
lty = 1, pch = c(4, 2:6), bty = "n", cex = 0.82)
grid(col = "#EDEDED")
# Exatamente as duas cores exigidas e eixo Y logarítmico.
for (i in 1:5) {
comparacao <- rbind("Sem Fog" = MRT_sem_F, series[[i]])
rownames(comparacao) <- c("Sem Fog", names(series)[i])
barplot(comparacao, beside = TRUE, log = "y",
col = c("#E6E6E6", "#666666"), border = "#333333",
names.arg = clock, ylim = c(0.1, 1000),
xlab = "Intervalo (s)", ylab = "Resposta (s) — log",
main = paste("Sem Fog vs.", names(series)[i]), cex.main = 0.95)
legend("topright", legend = rownames(comparacao),
fill = c("#E6E6E6", "#666666"), cex = 0.67, bty = "n")
}Leitura dos resultados: tempos altos no primeiro intervalo tornam algumas curvas difíceis de distinguir no gráfico de linhas. Por isso, a escala logarítmica nos gráficos de barras ajuda a comparar configurações cujos tempos possuem ordens de grandeza muito diferentes.
Objetivo: construir barras empilhadas para comparar as porcentagens de avaliações Good, Very Good e Excellent em cada faixa de preço. A matriz reproduz os números da tabela fornecida no enunciado (os totais podem variar em 0,1 ponto percentual por arredondamento).
qualidade <- matrix(
c(53.8, 43.6, 2.6,
33.9, 54.2, 11.9,
2.6, 60.5, 36.8,
0.0, 21.4, 78.6),
nrow = 3, ncol = 4,
dimnames = list(c("Good", "Very Good", "Excellent"),
c("$10–19", "$20–29", "$30–39", "$40–49"))
)
barplot(qualidade, beside = FALSE, col = c("#C6D8ED", "#5B91BF", "#234F78"),
border = "white", ylim = c(0, 105),
xlab = "Faixa de preço da refeição", ylab = "Avaliações (%)",
main = "Qualidade da refeição por faixa de preço",
legend.text = rownames(qualidade),
args.legend = list(x = "topright", bty = "n", cex = 0.82))Leitura dos resultados: nas refeições de preço mais elevado, aumenta a participação de avaliações Excellent, enquanto Good se torna menos frequente.
Objetivo: selecionar o mês de maio de
airquality, converter graus Fahrenheit para Celsius e
apresentar um histograma de densidade com uma curva suavizada.
data(airquality)
maio <- subset(airquality, Month == 5)
temperatura_c <- (maio$Temp - 32) / 1.8
temperatura_c <- temperatura_c[!is.na(temperatura_c)]
hist(temperatura_c, probability = TRUE,
col = "#73B6CC", border = "white", breaks = "Sturges",
main = "Distribuição das temperaturas de maio",
xlab = "Temperatura (°C)", ylab = "Densidade")
lines(density(temperatura_c), col = "#C9564C", lwd = 2.5)
rug(temperatura_c, col = "#555555")Leitura dos resultados:
probability=TRUE ajusta o histograma para a escala de
densidade, permitindo sobrepor density() corretamente. A
conversão usa a fórmula pedida: °C = (°F - 32)/1.8.
Objetivo: apresentar o percentual das vendas de cada
país em um gráfico de pizza, com fatias coloridas, porcentagens e
legenda. Os dados de Sales.txt acompanham este relatório
para permitir uma execução sem internet.
sales <- read.table("Sales.txt", header = TRUE)
vendas_pais <- aggregate(SALES ~ COUNTRY, data = sales, FUN = sum)
porcentagem <- 100 * vendas_pais$SALES / sum(vendas_pais$SALES)
rotulos <- paste0(round(porcentagem, 1), "%")
cores <- c("#3A87A6", "#E7A346", "#80A77C",
"#9B86BD", "#D27C75", "#638FC4")
pie(vendas_pais$SALES, labels = rotulos,
col = cores, border = "white", clockwise = TRUE,
main = "Participação percentual nas vendas por país")
legend("topright", legend = vendas_pais$COUNTRY,
fill = cores, bty = "n", cex = 0.82)Leitura dos resultados: a participação de cada país é calculada dividindo suas vendas pelo total de vendas, multiplicado por 100. A legenda identifica os países representados pelas cores.
Objetivo: mostrar as contagens de insetos para os
seis inseticidas do dataset InsectSprays, em caixas
amarelas e sem desenhar os pontos dos outliers, como
solicita a questão.
data(InsectSprays)
boxplot(count ~ spray, data = InsectSprays,
col = "yellow", outline = FALSE,
main = "Contagens de insetos por inseticida",
xlab = "Tipo de inseticida", ylab = "Número de insetos")Leitura dos resultados: o boxplot compara a mediana,
a variação e os quartis de cada tratamento. outline=FALSE
oculta os símbolos dos valores discrepantes; não remove registros da
base.
Objetivo: ler os quatro arquivos de monitoramento,
transformar currentTime em uma escala contínua de
horas desde a primeira coleta, converter
usedMemory para megabytes e organizar os quatro gráficos
usando layout().
Arquivos necessários:
monitoringCloudData_NONE.csv,
monitoringCloudData_0.1.csv,
monitoringCloudData_0.5.csv e
monitoringCloudData_1.csv. Os arquivos devem ficar na
mesma pasta do .Rmd antes de publicar.
Como não foram incluídos no PDF, nenhum valor foi criado
artificialmente.
arquivos <- c(
"monitoringCloudData_NONE.csv",
"monitoringCloudData_0.1.csv",
"monitoringCloudData_0.5.csv",
"monitoringCloudData_1.csv"
)
titulos <- c("Sem carga", "Carga de 0.1", "Carga de 0.5", "Carga de 1.0")
# Os horários dos CSVs incluem microssegundos.
# Um trecho do arquivo 0.5 tem DUAS datas no mesmo registro:
# 2020-01-31 2020-01-30 00:01:04.385180
# Nesses casos, testamos as duas datas possíveis e escolhemos
# aquela que mantém a sequência cronológica das coletas.
converter_tempo <- function(x) {
texto <- trimws(as.character(x))
padrao_duplo <- paste0(
"^([0-9]{4}-[0-9]{2}-[0-9]{2}) ",
"([0-9]{4}-[0-9]{2}-[0-9]{2}) ",
"([0-9]{2}:[0-9]{2}:[0-9]{2}(?:\\.[0-9]+)?)$"
)
duplicados <- grepl(padrao_duplo, texto, perl = TRUE)
# Primeiro faz a conversão explícita, sem tentar adivinhar o formato.
# Para os registros com data duplicada, usa temporariamente a 1ª data.
texto_normalizado <- texto
texto_normalizado[duplicados] <- sub(
padrao_duplo, "\\1 \\3", texto[duplicados], perl = TRUE
)
formato <- "%Y-%m-%d %H:%M:%OS"
tempo <- as.POSIXct(texto_normalizado, format = formato, tz = "UTC")
# Decide, em cada registro malformado, qual data dá continuidade
# ao registro anterior (e não uma volta de 24 horas).
for (i in which(duplicados)) {
partes <- regmatches(
texto[i], regexec(padrao_duplo, texto[i], perl = TRUE)
)[[1]]
candidatos <- as.POSIXct(
c(paste(partes[2], partes[4]), paste(partes[3], partes[4])),
format = formato, tz = "UTC"
)
if (i > 1 && !is.na(tempo[i - 1])) {
diferencas <- as.numeric(difftime(candidatos, tempo[i - 1], units = "secs"))
validos <- which(is.finite(diferencas) & diferencas >= 0)
if (length(validos) > 0) {
mais_proximo <- validos[which.min(diferencas[validos])]
tempo[i] <- candidatos[mais_proximo]
}
}
}
if (anyNA(tempo)) {
stop("Horário inválido em currentTime: ", texto[which(is.na(tempo))[1]])
}
tempo
}
# Extrai o valor e a unidade da memória (sem descartar dados).
# Conversões exigidas pelo enunciado: 1 GB = 1024 MB; 1 TB = 1000000 MB.
converter_memoria_mb <- function(x) {
texto <- toupper(gsub("\\s+", "", as.character(x)))
valor_texto <- sub("^([0-9]+(?:[.,][0-9]+)?).*$", "\\1", texto, perl = TRUE)
valor <- suppressWarnings(as.numeric(gsub(",", ".", valor_texto)))
unidade <- sub("^[0-9]+(?:[.,][0-9]+)?", "", texto, perl = TRUE)
fator <- c("B" = 1/1000000, "KB" = 1/1024,
"MB" = 1, "GB" = 1024, "TB" = 1000000)
if (anyNA(valor) || any(!unidade %in% names(fator))) {
stop("Formato inválido na coluna usedMemory.")
}
valor * unname(fator[unidade])
}
preparar_serie <- function(arquivo) {
dados <- read.csv(arquivo, stringsAsFactors = FALSE)
stopifnot(all(c("currentTime", "usedMemory") %in% names(dados)))
linhas_ajustadas <- sum(grepl(
"^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{4}-[0-9]{2}-[0-9]{2} ",
dados$currentTime
))
dados$tempo <- converter_tempo(dados$currentTime)
dados$memoria_mb <- converter_memoria_mb(dados$usedMemory)
dados <- dados[order(dados$tempo), ]
dados$horas <- as.numeric(difftime(dados$tempo, min(dados$tempo),
units = "hours"))
attr(dados, "datas_corrigidas") <- linhas_ajustadas
dados
}
faltantes <- arquivos[!file.exists(arquivos)]
if (length(faltantes) > 0) {
stop(paste("Arquivos da Questão 6 não encontrados:",
paste(faltantes, collapse = ", ")))
}
series_memoria <- lapply(arquivos, preparar_serie)
# Painel com os quatro gráficos, como no enunciado.
graphics::layout(matrix(1:4, nrow = 2, byrow = TRUE))
par(mar = c(4.5, 4.6, 3, 1))
for (i in seq_along(series_memoria)) {
dados <- series_memoria[[i]]
plot(dados$horas, dados$memoria_mb, type = "l", col = "#263746",
lwd = 1.1, main = paste("Análise de memória —", titulos[i]),
xlab = "Tempo desde o início (horas)",
ylab = "Memória usada (MB)")
grid(col = "#E4E8EB")
}graphics::layout(1)
# Resumo da limpeza e transformação de unidades, com os dados reais.
resumo_6 <- data.frame(
Condicao = titulos,
Amostras = vapply(series_memoria, nrow, integer(1)),
Datas_ajustadas = vapply(series_memoria, function(d)
attr(d, "datas_corrigidas"), numeric(1)),
Horas = round(vapply(series_memoria, function(d)
max(d$horas), numeric(1)), 2),
Minimo_MB = round(vapply(series_memoria, function(d)
min(d$memoria_mb), numeric(1)), 2),
Maximo_MB = round(vapply(series_memoria, function(d)
max(d$memoria_mb), numeric(1)), 2)
)
cat("\n\n**Resumo dos arquivos processados:**\n\n")Resumo dos arquivos processados:
| Condicao | Amostras | Datas_ajustadas | Horas | Minimo_MB | Maximo_MB |
|---|---|---|---|---|---|
| Sem carga | 4302 | 0 | 71.98 | 95.70 | 106.52 |
| Carga de 0.1 | 4275 | 0 | 72.00 | 245.03 | 3584.00 |
| Carga de 0.5 | 4303 | 194 | 72.00 | 257.46 | 1198.08 |
| Carga de 1.0 | 4303 | 0 | 72.00 | 241.10 | 254.23 |
Leitura dos resultados: difftime()
transforma os horários de coleta em tempo decorrido; a conversão mantém
todas as amostras em MB. Foram corrigidos registros de data duplicada
que impediam a conversão de horários. A tabela de diagnóstico logo acima
informa quantos registros foram corrigidos em cada arquivo.
Objetivo: considerar apenas as linhas com um único país de origem, excluir países ausentes e construir um gráfico de pizza Plotly com os dez países mais frequentes.
Nota sobre a versão dos dados: o arquivo
netflix_titles.csv enviado para esta atividade contém
7787 títulos, incluindo registros adicionados até 2021,
diferentemente da descrição da base de 2019 no enunciado. Os gráficos
das questões 7 a 10 usam o CSV presente nesta pasta,
sem substituir ou inventar registros. Caso o professor forneça uma
edição de 2019, substitua o arquivo e execute novamente o Knit.
if (file.exists("netflix_titles.csv")) {
netflix <- read.csv("netflix_titles.csv", stringsAsFactors = FALSE,
na.strings = c("", "NA"), fileEncoding = "UTF-8")
} else {
endereco <- paste0(
"https://raw.githubusercontent.com/byui-cse/",
"cse450-course/master/data/netflix_titles.csv"
)
netflix <- tryCatch(
read.csv(endereco, stringsAsFactors = FALSE, na.strings = c("", "NA"),
fileEncoding = "UTF-8"),
error = function(e) NULL
)
}
if (is.null(netflix)) {
cat("Não foi possível ler a base Netflix. Baixe netflix_titles.csv e coloque ao lado do .Rmd.")
} else {
pais <- trimws(netflix$country)
um_pais <- !is.na(pais) & nzchar(pais) & !grepl(",", pais, fixed = TRUE)
contagens <- sort(table(pais[um_pais]), decreasing = TRUE)
dez_maiores <- head(contagens, 10)
top_10 <- data.frame(Pais = names(dez_maiores),
Total = as.integer(dez_maiores))
grafico_7 <- plot_ly(data = top_10, labels = ~Pais, values = ~Total,
type = "pie", hole = 0.2,
textinfo = "label+percent",
hovertemplate = "%{label}<br>%{value} conteúdos<br>%{percent}<extra></extra>") %>%
layout(title = "Dez países com mais conteúdos — país único",
showlegend = TRUE)
grafico_7
}Leitura dos resultados: uma produção com países separados por vírgula não entra nessa contagem. Cada título com um único país conta exatamente uma vez.
Objetivo: utilizar os mesmos dez países da questão 7 e mostrar as colunas País e Total de conteúdos em uma tabela Plotly. O cabeçalho deve ter fundo cinza, letras brancas, e as células devem estar centralizadas.
if (!exists("top_10")) {
cat("Execute a Questão 7 com a base Netflix disponível.")
} else {
tabela_8 <- plot_ly(
type = "table",
header = list(
values = c("<b>País</b>", "<b>Total de conteúdos</b>"),
align = c("center", "center"),
fill = list(color = "#666666"),
font = list(color = "white", size = 14),
height = 36
),
cells = list(
values = list(top_10$Pais, top_10$Total),
align = c("center", "center"),
fill = list(color = "#F1F4F7"),
font = list(color = "#263746", size = 12),
height = 30
)
) %>% layout(title = "Países com mais conteúdos na Netflix")
tabela_8
}Leitura dos resultados: a tabela mostra os números absolutos associados às fatias do gráfico da aba anterior.
Objetivo: contar os títulos da base Netflix por
década de lançamento (release_year),
distinguindo séries (TV Show, linha azul) e
filmes (Movie, linha amarela) no Plotly. Por
exemplo, anos 2000 a 2009 pertencem à década 2000.
if (is.null(netflix)) {
cat("A base Netflix não foi carregada na Questão 7.")
} else {
validos <- netflix[!is.na(netflix$release_year) &
netflix$type %in% c("TV Show", "Movie"), ]
validos$decada <- floor(as.numeric(validos$release_year) / 10) * 10
decadas <- sort(unique(validos$decada))
qt_series <- sapply(decadas, function(d) sum(validos$decada == d &
validos$type == "TV Show"))
qt_filmes <- sapply(decadas, function(d) sum(validos$decada == d &
validos$type == "Movie"))
grafico_9 <- plot_ly(x = decadas, y = qt_series,
type = "scatter", mode = "lines+markers",
name = "Séries", line = list(color = "#277DB4", width = 3)) %>%
add_trace(x = decadas, y = qt_filmes,
type = "scatter", mode = "lines+markers",
name = "Filmes", line = list(color = "#E6B526", width = 3)) %>%
layout(title = "Conteúdos Netflix por década de lançamento",
xaxis = list(title = "Década", tickmode = "array", tickvals = decadas),
yaxis = list(title = "Quantidade de conteúdos"),
legend = list(orientation = "h"))
grafico_9
}Leitura dos resultados: a década considera o ano de lançamento do título, não o ano em que ele entrou no catálogo Netflix.
Objetivo: mostrar, em cada ano de 2000 a
2010 (inclusive), barras lado a lado para os filmes dos gêneros
Dramas, Action & Adventure e
Comedies. Se listed_in contiver vários
gêneros, será considerado apenas o primeiro.
if (is.null(netflix)) {
cat("A base Netflix não foi carregada na Questão 7.")
} else {
filmes <- netflix[netflix$type == "Movie" &
!is.na(netflix$release_year) &
netflix$release_year >= 2000 &
netflix$release_year <= 2010, ]
filmes$primeiro_genero <- trimws(sub(",.*$", "", filmes$listed_in))
anos <- 2000:2010
generos <- c("Dramas", "Action & Adventure", "Comedies")
quantidades <- sapply(generos, function(g) {
sapply(anos, function(a) {
sum(filmes$release_year == a & filmes$primeiro_genero == g,
na.rm = TRUE)
})
})
grafico_10 <- plot_ly(x = as.character(anos), y = quantidades[, "Dramas"],
type = "bar", name = "Dramas",
marker = list(color = "#287DB0")) %>%
add_bars(x = as.character(anos), y = quantidades[, "Action & Adventure"],
name = "Action & Adventure", marker = list(color = "#E69C34")) %>%
add_bars(x = as.character(anos), y = quantidades[, "Comedies"],
name = "Comedies", marker = list(color = "#36A66A")) %>%
layout(title = "Filmes por gênero e ano de lançamento (2000–2010)",
xaxis = list(title = "Ano de lançamento"),
yaxis = list(title = "Quantidade de filmes"),
barmode = "group")
grafico_10
}Leitura dos resultados: filtrar somente
type == "Movie" evita incluir séries. A extração anterior à
primeira vírgula de listed_in faz com que cada filme entre
em, no máximo, um dos três gêneros mostrados.
Fontes dos dados: enunciado Exercício 12 —
Visualização de Dados (fornecido pelo professor); conjuntos
airquality e InsectSprays do R;
Sales.txt de Training
Course Material; base Netflix enviada pelo aluno (7787 registros;
edição mais recente do que a versão de 2019 citada no PDF). Os arquivos
de monitoramento da questão 6 acompanham o pacote corrigido.