# 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")