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

library(dplyr)
## 
## Присоединяю пакет: 'dplyr'
## Следующие объекты скрыты от 'package:stats':
## 
##     filter, lag
## Следующие объекты скрыты от 'package:base':
## 
##     intersect, setdiff, setequal, union
library(ggplot2)
library(reshape2)
library(kableExtra)
## 
## Присоединяю пакет: 'kableExtra'
## Следующий объект скрыт от 'package:dplyr':
## 
##     group_rows
library(ggfortify)

# Чтение данных
data <- read.csv("var05.csv")
str(data)
## 'data.frame':    156 obs. of  4 variables:
##  $ stid  : int  1528 1519 1528 1528 1529 1525 1529 2369 1527 1529 ...
##  $ discid: int  162 139 150 176 163 139 159 150 3 112 ...
##  $ semnum: int  5 5 5 5 5 5 5 5 5 5 ...
##  $ stmark: int  60 60 88 88 90 85 60 14 96 92 ...
table(data$semnum)
## 
##   5 
## 156
data_simple <- data %>% select(-semnum)

# Уникальные студенты
cat("Количество уникальных студентов:", length(unique(data_simple$stid)), "\n")
## Количество уникальных студентов: 18
shapiro_test <- shapiro.test(data_simple$stmark)
shapiro_test
## 
##  Shapiro-Wilk normality test
## 
## data:  data_simple$stmark
## W = 0.71191, p-value = 3.973e-16
# Собираем топ студентов
top_students <- data_simple %>% 
  group_by(stid) %>% 
  filter(stmark >= 90) %>% 
  summarise(n = n()) %>% 
  arrange(desc(n))
head(top_students, 10)
## # A tibble: 10 × 2
##     stid     n
##    <int> <int>
##  1  1536     8
##  2  1525     7
##  3  1520     6
##  4  1527     6
##  5  1529     6
##  6  1533     6
##  7  1523     5
##  8  1526     5
##  9  1530     4
## 10  1534     4
summary(data_simple$stmark)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    0.00   65.00   85.00   71.44   90.00   98.00
# Гистрограмма распределения оценок
ggplot(data_simple, aes(x = stmark)) +
  geom_histogram(binwidth = 5, fill = 'cyan', color = 'black') +
  labs(title = "Распределение оценок студентов",
       x = "Оценка",
       y = "Количество") +
  theme_minimal()

# Здесь оценки по студентам - boxplot
# Да, по сути у нас ID кривые (было бы круто по именам, но что есть), но зная, что у нас всего 18 студентов, то впринципе показательно. 
ggplot(data_simple, aes(y = stmark, x = factor(stid))) +
  geom_boxplot() +
  labs(title = "Распределение оценок по студентам",
       x = "ID студента",
       y = "Оценка") +
  theme(axis.text.x = element_blank())

# Преобразование в широкий формат
wide_data <- dcast(data_simple, stid ~ discid, value.var = "stmark")
wide_data[is.na(wide_data)] <- 0
head(wide_data)
##   stid  3 28 57 112 139 150 159 162 163 176
## 1 1519 91 80 65  90  60  85  75  85  90  23
## 2 1520 94 79 80  92  79  94  85  90  90  96
## 3 1523 93 80 85  91  81  98  85  90  90  80
## 4 1525 93 92 65  94  85  94  70  90  90  97
## 5 1526 90 76 65  91  73  94  60  90  90  83
## 6 1527 96 92 65  92  84  96  85  95  90  78
# KMeans
# Здесь был очень долгий затуп. Из-за ID предметов.
set.seed(123)
kmeans_result <- kmeans(wide_data[-1], centers = 3)
wide_data$cluster <- as.factor(kmeans_result$cluster)

# Рассчитываем IQR (межквартальный размах) для каждого предмета (ищем предметы, где оценки студентов сильно отличаются — это поможет выделить кластеры четче)
iqr_values <- apply(wide_data[-1], 2, IQR)  # Нужно убрать stid
# Выбираем топ-2 предмета с максимальным разбросом
top_subjects <- names(sort(iqr_values, decreasing = TRUE))[1:2]
top_subjects
## [1] "162" "176"
autoplot(kmeans_result, data = wide_data[-1], frame = TRUE)

ggplot(wide_data, aes(x = `162`, y = `176`, color = cluster)) +
  geom_point(size = 3) +
  labs(title = "Кластеризация студентов по успеваемости (по ID предметов)",
       x = "Оценка по предмету 162",
       y = "Оценка по предмету 176") +
  theme_minimal()

# Иерархическая кластеризация
# Здесь цифры от 1-18 разумеется уникальные студенты и чем выше его "строка", тем больше отличие. 
dist_matrix <- dist(wide_data[-1])
hclust_result <- hclust(dist_matrix)

plot(hclust_result, main = "Дендрограмма иерархической кластеризации",
     xlab = "Студенты", ylab = "Расстояние")

mean_marks <- wide_data %>% 
  select(-stid) %>% 
  summarise(across(everything(), ~ mean(., na.rm = TRUE))) %>% 
  t() %>% 
  as.data.frame() %>% 
  rename("Средний_балл" = "V1") %>% 
  mutate(Предмет = rownames(.)) %>% 
  select(Предмет, Средний_балл) %>% 
  arrange(desc(Средний_балл))
## Warning: There was 1 warning in `summarise()`.
## ℹ In argument: `across(everything(), ~mean(., na.rm = TRUE))`.
## Caused by warning in `mean.default()`:
## ! аргумент не является числовым или логическим: возвращаю NA
mean_marks %>% 
  kable(digits = 1, caption = "Средний балл по предметам") %>% 
  kable_styling(bootstrap_options = "striped", full_width = FALSE)
Средний балл по предметам
Предмет Средний_балл
3 3 70.5
150 150 67.3
112 112 66.2
163 163 65.0
28 28 63.2
162 162 63.1
159 159 62.9
139 139 55.7
57 57 54.2
176 176 51.0
cluster cluster NA