momente şi schiţe de informatică şi matematică
To attain knowledge, write. To attain wisdom, rewrite.

Distribuţia pe zile a orelor dintr-o şcoală cu un singur schimb (II)

limbajul R | orar şcolar
2021 feb

Din [1] avem fişierul chosen_distr.rds, conţinând o distribuţie pe zilele de lucru a orelor dintr-o şcoală cu un singur schimb – mai precis, un obiect "tibble" (putem să-i zicem şi „tabel”) cu 1202 înregistrări pe trei variabile, reprezentând câte un profesor (dintre cei 76 existenţi), o clasă (dintre cele 41 – nu 32, cum notasem greşit în [1]) şi ziua alocată orei la acea clasă pentru profesorul respectiv. Această distribuţie a fost aleasă ca fiind cea mai „promiţătoare”, dintr-o listă de vreo 30 de distribuţii generate în [1].

Subseturi de ore; mutăm ore, sau redistribuim orele?

Vrem să îndreptăm cumva, distribuţiile individuale rezultate profesorilor cu mai puţin de 15 ore pe săptămână – fiindcă pentru aceştia nu am impus nicio condiţie la generarea distribuţiei (v. "distribute.R" din [1]); dar vom considera şi cele câteva cazuri cu „coeficientul de omogenitate” peste 0.5, existente între distribuţiile individuale cu un total de cel puţin 15 ore – mărind astfel posibilităţile de schimbare dintr-o zi în alta a orelor la anumite clase (cu şansa de a îmbunătăţi şi aceste cazuri).

Prin programul următor separăm înregistrările corespunzătoare celor două categorii de cazuri menţionate mai sus. Întâi, recuperăm în D distribuţia dată şi constituim (similar matricei Zore din [1]) matricea Z conţinând distribuţiile individuale, împreună cu totalurile şi coeficienţii de omogenitate aferenţi; integrăm (transformând matricea în "data.frame") şi coloana numelor profesorilor de care ţin distribuţiile individuale respective. Apoi, folosim Z pentru a obţine prin funcţia sgr_dis(), lista înregistrărilor din D corespunzătoare uneia sau alteia dintre cele două categorii; prin funcţia make_df_sgr() vom putea obţine şi o formă lizibilă, a datelor respective:

# subdis.R  (separă subgrupul orelor de redistribuit pe zile)
library(tidyverse)
D <- readRDS("chosen_distr.rds")  # 1202 ore (prof/cls) distribuite pe zile

cf_omg <- function(ore_zi) {  # coeficient de omogenitate (v. [1])
    ore <- ore_zi[ore_zi != 0]
    round(sd(ore)/mean(ore)*diff(range(ore)), 2)
}
Z <- as.matrix(table(D[c("prof", "zl")])) %>%
     cbind(., apply(., 1, cf_omg)) %>%  # coloana primeşte numele "V6"
     cbind(., rowSums(.[, 1:5]))  # coloana primeşte numele "V7"
Z[, c(6, 7)] <- Z[, c(7, 6)]  # schimbă între ele ultimele două coloane
nume <- rownames(Z)  # profesorii de care aparţin distribuţiile din Z
Z <- as.data.frame(Z) %>%
     mutate(prof = nume) %>%  # adaugă coloana profesorilor
     relocate(., prof, before=1)

Z1 <- Z %>% filter(V6 < 15)  # subgrupul distribuţiilor cu sub 15 ore pe săptămână
# > print(Z1)  ## distribuţiile 2, 3, 16 şi 20 sunt bune, încât ignorăm liniile:  
Z1 <- Z1[-c(2,3,16,20), ]  # (3 3 3 3 0), (2 2 0 0 0), (2 3 3 2 2) şi (0 0 2 2 2)

Z2 <- Z %>% filter(V6 >= 15 & V7 > 0.5)  # şi acestea ar fi eventual, de îndreptat

sgr_dis <- function(sZ) {  # produce lista înregistrărilor din D pentru Z1 sau Z2
    D %>% filter(prof %in% sZ$prof) %>%
          split(.$zl) %>%
          map(., function(Z) Z[, 1:2] %>% arrange(prof))
}

make_df_sgr <- function(sZ) {  # pentru tipărit/afişat listele date de sgr_dis()
    hZ <- sgr_dis(sZ)
    patt <- data.frame(prof="", Lu="", Ma="", Mi="", Jo="", Vi="")
    r = 1
    for(P in sZ$prof) {
        patt[r, 1] <- P
        for(q in 2:6) {
            ql <- hZ[[q-1]] %>% filter(prof == P)
            patt[r, q] <- paste(c(ql$cls), collapse=" ") 
        }
        r <- r + 1
    }
    patt    
}

reD <- D %>%  # comasează cele două liste (subgrupul orelor de re-distribuit pe zile)
       filter(prof %in% c(Z1$prof, Z2$prof))
saveRDS(reD, file="ore_de_redist.rds")

Bineînţeles că în final, am salvat (într-un fişier ".rds") grupul de înregistrări din distribuţia iniţială D, corespunzător distribuţiilor individuale din Z1 şi Z2 (urmând să re-distribuim orele din acest grup, iar după modificare, să le montăm înapoi în D).

Acum, prin următoarea secvenţă de comenzi sintetizăm orele respective într-o formă pe care putem încerca chiar şi manual, să le redistribuim pe zile:

> df1 <- make_df_sgr(Z1)
> df2 <- make_df_sgr(Z2)
> df12 <- rbind(df1, df2)
> sink("sgred.txt"); print(df12, row.names=FALSE, width=100); sink()
# distribuţia iniţială a orelor de re-distribuit pe zile
 prof             Lu              Ma                Mi             Jo              Vi
  P56          6D 8D    10B 5B 6D 8D  10B 12B 5B 5C 6D         12B 5C                
  P59       6D 7D 8B        5E 7D 8B                5E          5D 5E           6D 7D
  P60 11C 12D 12E 9E         12D 12E                              11C  11C 12D 12E 9E
  P61   10C 5C 7A 7E        5B 5E 7C                5D             7D              5A
  P62             9C  10C 10D 12E 9C                       10A 10B 9D          10E 9D
  P63            12A         12A 12C        11A 12C 9A     11A 11B 9A             11B
  P64            11D             11D     11D 11E 9C 9D      11E 9C 9D             11E
  P65          6E 9A           5A 8C             5B 7D             7E           6C 8E
  P66                       5A 5B 9E             9D 9E         11E 9D              9D
  P67             7E           7E 8C             7E 8C          7E 8C                
  P68             5C          12B 5C               12B             8C           5C 8C
  P69          9B 9D       11C 5C 9C                5C                            11A
  P70         11A 9D             12A                              11B          12B 9A
  P72        10A 10C         10D 11E               10B            10E                
  P73 11E 12C 12E 9E          12E 9E                                                 
  P74            10E                           10C 10D                            10E
  P76             7B                                                               7B
  P39  11A 12D 8C 8F     7E 8C 8D 8F      12E 7D 8D 8E      12E 7C 8E         11A 12A
  P40  11B 11D 8F 9A   11C 11D 8A 9A             8A 8B      11D 8B 9C   10E 11D 8F 9C
  P41 11E 12A 12D 7E       12D 7E 8D      12D 7A 8D 8E   12D 7A 8E 9E         11A 12C
  P42   10B 7B 9B 9E     5B 7B 9B 9E      10B 5B 9B 9E         10B 9B       10B 7B 9E
  P47   11A 7E 8A 9B   11A 12C 8A 8E            12C 8E       7E 8A 8E        7E 8A 9B
  P48    6E 8C 8D 9E     6C 6D 8B 9B             6A 8A   10B 6B 7B 8F           7B 8E
  P54      12E 6E 8F          12B 6E            12B 8F  12B 12E 6E 8F   12B 12E 6E 8F
## 5C se poate muta între P56 şi P61
## 7E are 4, 3, 1, 3, 1 ore pe zi

Dăm un exemplu de re-distribuire manuală (folosind un editor de text, pentru a evidenţia sintactic apariţiile în text a uneia sau alteia dintre clase). P56 are distribuţia (2 4 5 2 0), de coeficient 1.38; evidenţiind în tot textul "5C", observăm că 5C apare miercuri la P56 şi apare luni (dar nu şi miercuri) la P61; mutând 5C din ziua Mi în ziua Lu la P56 şi din ziua Lu în ziua Mi la P61 – ambele distribuţii se îmbunătăţesc (a lui P56 devine (3 4 4 2 0), cu omogenitatea 0.59; cea a lui P61 devine (3 3 2 1 1), ceva mai bună ca aceea iniţială).
Cu alte cuvinte, în distribuţia iniţială D aveam înregistrările "P56 5C Mi" şi "P61 5C Lu" – iar după modificarea arătată, am avea "P56 5C Lu" şi respectiv "P61 5C Mi".

Mecanismul exemplificat se poate aplica manual relativ uşor, putând conduce la îmbunătăţirea distribuţiilor individuale respective (fără a afecta numărul de ore pe zi la clase) – dar pare dificil de automatizat printr-un program… În schimb, nu este prea greu de realizat un "widget jQuery" (analog celui din Ajustarea orarului) prin care să putem sesiza mai uşor ce interschimbări de ore ar fi mai bine de făcut şi totodată, să le putem face interactiv (cu posibilităţi de revenire), mai în siguranţă şi mai comod decât dacă am proceda manual (şi utilizabil pe orice subset de ore).

Pe de altă parte, cel mai bine ar fi (în loc să mutăm ore dintr-o zi în alta) să adaptăm cumva programul de generare distribute.R din [1], încât să-l putem folosi şi pe un subset al încadrării pe clase iniţiale. De exemplu, vedem în tabelul redat mai sus că unele clase apar în fiecare zi (5C, 7D, 10B, 11A şi altele); deci orele corespunzătoare acestora vor putea fi etichetate prin orice permutare de Zile (satisfăcând anumite condiţii) – încât se întrevede posibilitatea de a folosi iarăşi algoritmul de etichetare din distribute.R; dificultatea ar consta în faptul că unele clase apar numai în unele zile (de exemplu, 8D apare numai în trei coloane), deci orele aferente acestora trebuie etichetate cu un anumit subset din Zile (pentru a nu afecta numărul de ore pe zi la clase).

O altă dificultate vine din faptul că acum, clasele implicate au un număr prestabilit de ore pe zi; de exemplu, clasa 7E apare în tabelul de mai sus de 4 ori în prima coloană şi de 3, 1, 3, 1 ori respectiv, în celelalte zile – ori prin etichetarea din [1] orele clasei ar fi repartizate omogen (de exemplu, (2 3 3 2 2)).

vezi Cărţile mele (de programare)

docerpro | Prev | Next