# eliminateclubs_fixed.R
# Εξάρτηση: install.packages(c("dplyr", "readr", "tidyr"))
library(dplyr)
library(readr)
library(tidyr)
# --- Ρυθμίσεις ---
min_threshold <- 10L
responses_file <- "responses.csv"
clubs_file <- "clubs.csv"
output_prefix <- "responses_round"
# --- Διαβάζουμε δεδομένα ---
responses <- read_csv(responses_file, show_col_types = FALSE)
clubs <- read_csv(clubs_file, show_col_types = FALSE)
# --- Καθαρισμός και εναρμόνιση (όπως πριν) ---
colnames(clubs) <- tolower(trimws(colnames(clubs)))
clubs$club_id <- trimws(tolower(clubs$club_id))
if("club_name" %in% colnames(clubs)) {
clubs$club_name <- trimws(clubs$club_name)
} else if("clubname" %in% colnames(clubs)) {
clubs$club_name <- trimws(clubs$clubname)
} else {
clubs$club_name <- clubs$club_id
}
student_col <- colnames(responses)[1]
initial_club_cols <- trimws(tolower(colnames(responses)[-1]))
colnames(responses) <- c(student_col, initial_club_cols)
# Μετατροπή σε 'long' μορφή για ευκολότερη διαχείριση προτιμήσεων
responses_long_master <- responses |>
pivot_longer(cols = -all_of(student_col),
names_to = "ClubID", values_to = "Rank") |>
mutate(
ClubID = trimws(tolower(ClubID)),
# Εξασφάλιση ότι το Rank είναι ακέραιος
Rank = as.integer(Rank)
) |>
# Φιλτράρουμε μόνο τις καταγεγραμμένες προτιμήσεις
filter(!is.na(Rank))
round <- 1L
elimination_log <- data.frame(Round=integer(), ClubID=character(), ClubName=character(), FirstPrefCount=integer(), stringsAsFactors = FALSE)
active_clubs <- initial_club_cols
repeat {
# --- Βήμα 1: Υπολογισμός Πρώτων Προτιμήσεων (Τρέχουσα κατάσταση) ---
# Βρίσκουμε την τρέχουσα υψηλότερη προτίμηση (το χαμηλότερο Rank) για κάθε μαθητή
current_prefs <- responses_long_master |>
# Φιλτράρουμε μόνο τους ενεργούς Ομίλους
filter(ClubID %in% active_clubs) |>
# Βρίσκουμε το ελάχιστο Rank (την πρώτη προτίμηση)
group_by(across(all_of(student_col))) |>
filter(Rank == min(Rank, na.rm = TRUE)) |>
ungroup()
# Μετράμε τις πρώτες προτιμήσεις
counts <- current_prefs |>
count(ClubID, name = "FirstPrefCount") |>
right_join(data.frame(ClubID = active_clubs), by = "ClubID") |>
replace_na(list(FirstPrefCount = 0)) |>
# Μετατροπή σε named vector για εύκολη χρήση
tibble::deframe()
cat(sprintf("\n--- Γύρος %d ---\n", round))
cat("Όμιλοι και πρώτες προτιμήσεις:\n")
print(data.frame(ClubID=names(counts), FirstPref=as.integer(counts)), row.names = FALSE)
# --- Βήμα 2: Έλεγχος Τερματισμού ---
if(length(active_clubs) == 0) {
cat("\nΤερματισμός: Δεν έμεινε κανένας Όμιλος.\n")
break
}
if(all(counts >= min_threshold)) {
cat("\nΤερματισμός: όλοι οι ενεργοί Όμιλοι έχουν τουλάχιστον ", min_threshold, " πρώτες προτιμήσεις.\n", sep = "")
# Αναδημιουργία του τελικού responses_wide για αποθήκευση
responses_final_wide <- responses_long_master |>
filter(ClubID %in% active_clubs) |>
pivot_wider(id_cols = all_of(student_col),
names_from = ClubID, values_from = Rank)
write_csv(responses_final_wide, "responses_final.csv")
break
}
# --- Βήμα 3: Εξάλειψη ---
counts_df <- data.frame(ClubID=names(counts), FirstPrefCount=as.integer(counts)) |>
filter(FirstPrefCount < min_threshold)
if(nrow(counts_df) == 0) {
# Αυτό δεν θα έπρεπε να συμβεί λόγω του προηγούμενου ελέγχου,
# αλλά εξασφαλίζει τη συνέχεια αν το all(counts >= min_threshold) δεν πιάσει την περίπτωση.
cat("\nΤερματισμός: Όλοι οι Όμιλοι άνω του ορίου.\n")
next
}
min_count <- min(counts_df$FirstPrefCount)
elim_candidates <- counts_df |> filter(FirstPrefCount == min_count)
# Λογική Ισοπαλίας: επιλέγουμε αλφαβητικά
club_to_elim <- sort(elim_candidates$ClubID)[1]
match_idx <- match(club_to_elim, clubs$club_id)
club_name <- if(!is.na(match_idx)) clubs$club_name[match_idx] else club_to_elim
cat(sprintf("Εξαλείφεται ο Όμιλος: %s (%s) με %d πρώτες προτιμήσεις.\n",
club_to_elim, club_name, min_count))
elimination_log <- rbind(elimination_log,
data.frame(Round=round,
ClubID=club_to_elim,
ClubName=club_name,
FirstPrefCount=min_count,
stringsAsFactors = FALSE))
# --- Βήμα 4: Μεταφορά Ψήφων (Αναβάθμιση Προτιμήσεων) ---
# 1. Απενεργοποίηση του εξαλειφθέντος Ομίλου
active_clubs <- setdiff(active_clubs, club_to_elim)
# 2. Ακύρωση του club_to_elim (θέτουμε το Rank του σε NA)
responses_long_master <- responses_long_master |>
mutate(Rank = ifelse(ClubID == club_to_elim, NA_integer_, Rank)) |>
# Αφαιρούμε πλέον τα NA rank, καθώς ο Όμιλος δεν μετράει
filter(!is.na(Rank))
# 3. Αναβαθμίζουμε τα Ranks για κάθε μαθητή
responses_long_master <- responses_long_master |>
group_by(across(all_of(student_col))) |>
# Αναβαθμίζουμε τα Ranks (1, 3, 4, 5... γίνεται 1, 2, 3, 4...)
# Επανα-αριθμούμε τις προτιμήσεις με βάση την τρέχουσα σειρά τους
mutate(Rank = rank(Rank, ties.method = "min")) |>
ungroup()
# --- Αποθήκευση Ενδιάμεσου Αρχείου (responses_wide) ---
round_file <- sprintf("%s%d.csv", output_prefix, round)
responses_round_wide <- responses_long_master |>
pivot_wider(id_cols = all_of(student_col),
names_from = ClubID, values_from = Rank) |>
# Συμπληρώνουμε με NA τους Όμιλους που έχουν ήδη εξαλειφθεί (αν υπάρχουν)
select(all_of(student_col), all_of(active_clubs))
write_csv(responses_round_wide, round_file)
cat("Αποθηκεύτηκε ενδιάμεσο αρχείο: ", round_file, "\n")
round <- round + 1L
}
if(nrow(elimination_log)>0) write_csv(elimination_log, "elimination_log.csv")
cat("\nΟλοκληρώθηκε η διαδικασία εξάλειψης. Δες το elimination_log.csv.\n")