#| '!! shinylive warning !!': |
#| shinylive does not work in self-contained HTML documents.
#| Please set `embed-resources: false` in your metadata.
#| standalone: true
#| viewerHeight: 800
library(shiny)
library(bslib)
library(dplyr)
library(ggplot2)
library(readr)
library(httr)
library(gt)
library(plotly)
# Function to load data with error handling and logging
carregar_dados <- function(url) {
message("Tentando acessar URL: ", url)
tryCatch({
dados <- read_csv(url,
col_types = cols(
Ano = col_character(),
Turma = col_character(),
Disciplina = col_character(),
Notas = col_double()
),
locale = locale(decimal_mark = ",",
grouping_mark = "."))
return(dados)
}, error = function(e) {
message("Erro ao carregar dados: ", e$message)
dados_exemplo <- data.frame(
Ano = rep(c("10º", "11º", "12º"), each = 100),
Turma = rep(LETTERS[1:3], times = 100),
Disciplina = rep(c("Matemática", "Português", "Inglês", "História", "Geografia"), times = 60),
Notas = round(rnorm(300, mean = 14, sd = 3))
)
dados_exemplo$Notas[dados_exemplo$Notas < 0] <- 0
dados_exemplo$Notas[dados_exemplo$Notas > 20] <- 20
return(dados_exemplo)
})
}
url <- "https://gist.githubusercontent.com/karlosgomes/3758e64d86522ae8e796ba490c8c111c/raw/d3c88aee7d0815814e666f5f71fd524c7e65198a/dados.csv"
dados <- carregar_dados(url) %>%
filter(Disciplina != "Educação Moral e Religiosa")
# Preparar valores iniciais
ano_inicial <- unique(dados$Ano)[1]
disciplinas_iniciais <- dados %>%
filter(Ano == ano_inicial) %>%
pull(Disciplina) %>%
unique() %>%
sort()
turmas_iniciais <- dados %>%
filter(Ano == ano_inicial,
Disciplina == disciplinas_iniciais[1]) %>%
pull(Turma) %>%
unique() %>%
sort()
ui <- page_sidebar(
title = "Análise de Notas Escolares",
theme = bs_theme(preset = "default"),
fillable = TRUE,
sidebar = sidebar(
width = "200px",
selectInput("ano", "Ano Escolar",
choices = unique(dados$Ano),
selected = ano_inicial),
selectInput("disciplina", "Disciplina",
choices = disciplinas_iniciais,
selected = disciplinas_iniciais[1]),
selectInput("turma", "Turma",
choices = turmas_iniciais,
selected = turmas_iniciais[1], # Change this line: select only first turma by default
multiple = FALSE)
),
layout_columns(
fill = TRUE,
height = "100px",
value_box(
title = "Média do Ano na Disciplina",
value = textOutput("media_global"),
theme = "primary"
),
value_box(
title = "Número de Notas do Ano na Disciplina",
value = textOutput("n_notas"),
theme = "secondary"
),
value_box(
title = "% Notas Positivas do Ano na Disciplina",
value = textOutput("perc_positivas"),
theme = "success"
)
),
layout_columns(
fill = TRUE,
heights_equal = "row",
card(
full_screen = TRUE,
card_header("Disciplina na Turma Vs. Disciplina no Ano"),
plotlyOutput("box_comparison")
),
card(
full_screen = TRUE,
card_header("Análise da Disciplina"),
gt_output("metrics_table")
),
card(
full_screen = TRUE,
card_header("Distribuição das Notas da Turma"),
plotlyOutput("histogram_plot")
)
)
)
server <- function(input, output, session) {
# Update disciplinas based on ano
observe({
req(input$ano)
disciplinas_do_ano <- dados %>%
filter(Ano == input$ano) %>%
pull(Disciplina) %>%
unique() %>%
sort()
updateSelectInput(session, "disciplina",
choices = disciplinas_do_ano,
selected = disciplinas_do_ano[1])
})
# Update turmas based on ano and disciplina
observe({
req(input$ano, input$disciplina)
turmas_disponiveis <- dados %>%
filter(Ano == input$ano,
Disciplina == input$disciplina) %>%
pull(Turma) %>%
unique() %>%
sort()
current_selected <- input$turma
new_selected <- intersect(current_selected, turmas_disponiveis)
if (length(new_selected) == 0) new_selected <- turmas_disponiveis[1]
updateSelectInput(session, "turma",
choices = turmas_disponiveis,
selected = new_selected)
})
dados_ano_disc <- reactive({
req(dados, input$ano, input$disciplina)
dados %>%
filter(Ano == input$ano,
Disciplina == input$disciplina)
})
dados_filtrados <- reactive({
req(dados)
dados_temp <- dados_ano_disc()
if (!is.null(input$turma) && length(input$turma) > 0)
dados_temp <- dados_temp %>% filter(Turma %in% input$turma)
dados_temp
})
output$media_global <- renderText({
df <- dados_ano_disc()
if (nrow(df) > 0) {
media <- round(mean(df$Notas, na.rm = TRUE), 1)
paste0(media, " valores")
} else "N/A"
})
output$n_notas <- renderText({
df <- dados_ano_disc()
nrow(df)
})
output$perc_positivas <- renderText({
df <- dados_ano_disc()
if (nrow(df) > 0) {
paste0(round(mean(df$Notas >= 10, na.rm = TRUE) * 100, 1), "%")
} else "N/A"
})
output$box_comparison <- renderPlotly({
req(input$ano, input$disciplina, length(input$turma) > 0)
# Dados da turma na disciplina
dados_turma <- dados_filtrados()
# Dados do ano na disciplina
dados_ano <- dados %>%
filter(Ano == input$ano,
Disciplina == input$disciplina)
# Combinar os dados para o plot
dados_plot <- bind_rows(
mutate(dados_turma, Grupo = "Turma"),
mutate(dados_ano, Grupo = "Ano")
) %>%
mutate(Grupo = factor(Grupo, levels = c("Ano", "Turma")))
# Criar o plot diretamente com plotly
plot_ly(dados_plot, y = ~Grupo, x = ~Notas, type = "box",
color = ~Grupo,
colors = c("lightblue", "steelblue")) %>%
layout(
xaxis = list(title = "Notas", range = c(0, 20), dtick = 2),
yaxis = list(title = ""),
showlegend = FALSE,
margin = list(t = 50, r = 50, b = 50, l = 50)
)
})
output$metrics_table <- render_gt({
req(nrow(dados_filtrados()) > 0, input$ano, input$disciplina, length(input$turma) > 0)
media_ano_disciplina <- dados %>%
filter(Ano == input$ano,
Disciplina == input$disciplina) %>%
summarise(media = mean(Notas, na.rm = TRUE)) %>%
pull(media)
media_turma_disciplina <- dados_filtrados() %>%
summarise(media = mean(Notas, na.rm = TRUE)) %>%
pull(media)
media_turma_geral <- dados %>%
filter(Ano == input$ano,
Turma %in% input$turma) %>%
summarise(media = mean(Notas, na.rm = TRUE)) %>%
pull(media)
media_geral_ano <- dados %>%
filter(Ano == input$ano) %>%
summarise(media = mean(Notas, na.rm = TRUE)) %>%
pull(media)
desvio_disciplina_ano <- media_turma_disciplina - media_ano_disciplina
desvio_relativo_turma <- desvio_disciplina_ano - (media_turma_geral - media_geral_ano)
metricas <- data.frame(
Métrica = c(
"Média da Turma na Disciplina",
"Média do Ano na Disciplina",
"Média Geral da Turma",
"Média Geral do Ano",
"Desvio para Média do Ano",
"Desvio Relativo à Turma"
),
Valor = c(
round(media_turma_disciplina, 2),
round(media_ano_disciplina, 2),
round(media_turma_geral, 2),
round(media_geral_ano, 2),
round(desvio_disciplina_ano, 2),
round(desvio_relativo_turma, 2)
)
)
gt(metricas) %>%
tab_header(
title = md(paste("Métricas para", input$disciplina))
) %>%
fmt_number(
columns = "Valor",
decimals = 2
) %>%
tab_style(
style = list(
cell_fill(color = "lightgreen"),
cell_text(weight = "bold")
),
locations = cells_body(
columns = "Valor",
rows = Valor > 0 & (Métrica %in% c("Desvio para Média do Ano", "Desvio Relativo à Turma"))
)
) %>%
tab_style(
style = list(
cell_fill(color = "lightpink"),
cell_text(weight = "bold")
),
locations = cells_body(
columns = "Valor",
rows = Valor < 0 & (Métrica %in% c("Desvio para Média do Ano", "Desvio Relativo à Turma"))
)
) %>%
cols_align(
align = "left",
columns = "Métrica"
) %>%
cols_align(
align = "right",
columns = "Valor"
) %>%
tab_options(
table.width = pct(100),
column_labels.font.weight = "bold"
)
})
output$histogram_plot <- renderPlotly({
req(input$ano, input$disciplina, input$turma)
dados_plot <- dados %>%
filter(Ano == input$ano,
Disciplina == input$disciplina,
Turma %in% input$turma)
plot_ly(dados_plot, x = ~Notas, type = "histogram",
nbinsx = 20,
marker = list(color = "steelblue",
line = list(color = "white", width = 1))) %>%
layout(
xaxis = list(title = "Notas", range = c(0, 20), dtick = 2),
yaxis = list(title = "Frequência"),
showlegend = FALSE,
margin = list(t = 50, r = 50, b = 50, l = 50)
)
})
}
shinyApp(ui, server)