- Pour plus de détails, voir le projet sur GitHub.
- Auteur(s) : Lilian Besson et Bastien Trotobas.
- Date : Juillet 2018.
- Licence : MIT
On utilise Python 3 dans ce document. Je vous conseille de le lire interactivement, en cliquant sur ce bouton là :
%load_ext watermark
%watermark -a "Lilian Besson" -v
On va donner ici une implémentation simplifiée de la fonction du calcul d'ordre d'appel, avec plusieurs exemples.
Un candidat ou une candidate peut être boursier-ère ou non, et résident-e ou non. On a besoin de 4 types de candidats, qu'on représente simplement par un entier.
# Les différents types de candidats
BoursierResident = 1
BoursierNonResident = 2
NonBoursierResident = 3
NonBoursierNonResident = 4
# Liste des différents types
TypesCandidats = [
BoursierResident,
BoursierNonResident,
NonBoursierResident,
NonBoursierNonResident
]
On définie également une fonction pour pouvoir afficher le type d'un candidat.
def afficheTypeCandidat(typeCandidat):
if typeCandidat == BoursierResident:
return "BoursierResident"
elif typeCandidat == BoursierNonResident:
return "BoursierNonResident"
elif typeCandidat == NonBoursierResident:
return "NonBoursierResident"
elif typeCandidat == NonBoursierNonResident:
return "NonBoursierNonResident"
Un vœu désigne la candidature d'un candidat à une formation donnée. Ici on représente un vœu comme la donnée du type de candidat et du rang que l'établissement lui a atribué.
On pourrait utiliser un tuple ou une liste pour représenter un vœu, par exemple [BoursierResident, 1, "Harry Potter"]
pour un boursier résident classé 1er et appelé Harry Potter, mais par lisibilité on préfère utiliser un dictionnaire qui contient ces informations :
POUR NOUS ne même pas parler de nom mais juste d'identifiant ?
exemple_voeu = {
"type": BoursierResident,
"rang": 1,
"nom": "Harry Potter",
"id": 1235
}
En fait, on n'utilisera jamais le nom des vœux, parce que la plateforme ParcourSup ne tient compte d'aucune information sur les vœux ou les candidat-s, à part leur identifiant unique et anonymisé.
On ajoute un nom dans les premiers exemples simplement pour être un peu plus visuel.
Les seules chosent que l'algorithme de ParcourSup utilise à propos des vœux sont leur type et leur rang.
Dans le code ci-dessous, ils seront lu comme ça : voeu["type"]
et voeu["rang"]
.
print("VÅ“u de type", afficheTypeCandidat(exemple_voeu["type"]), "et de rang", exemple_voeu["rang"])
Ensuite, une liste de vœux est une liste de dictionnaires de cette forme. Prenons un exemple avec 8 vœux, qui reprend l'exemple "A1" donné dans le document de référence.
Note : les prénoms sont choisis pour être dans l'ordre alphabétique.
voeu_alice = { "type": NonBoursierNonResident, "rang": 1, "nom": "Alice" , "id": 1911}
voeu_bob = { "type": NonBoursierNonResident, "rang": 2, "nom": "Bob", "id": 2211}
voeu_christophe = { "type": NonBoursierNonResident, "rang": 3, "nom": "Christophe", "id": 1061}
voeu_dora = { "type": NonBoursierNonResident, "rang": 4, "nom": "Dora", "id": 7843}
voeu_emilie = { "type": NonBoursierNonResident, "rang": 5, "nom": "Emilie", "id": 3388}
voeu_florian = { "type": BoursierNonResident, "rang": 6, "nom": "Florian", "id": 4073}
voeu_guillaume = { "type": BoursierNonResident, "rang": 7, "nom": "Guillaume", "id": 8020}
voeu_helene = { "type": NonBoursierNonResident, "rang": 8, "nom": "Hélène", "id": 4219}
On a juste à les mettre dans une liste ensuite (en Python, une liste commence par [
, chaque valeur est séparée par ,
et termine par ]
).
voeuxClasses = [
voeu_alice, voeu_bob, voeu_christophe, voeu_dora,
voeu_emilie, voeu_florian, voeu_guillaume, voeu_helene
# le retour à la ligne n'est là que pour une meilleure visibilité
]
On aura besoin de savoir si un type de candidat est boursier ou non (respectivement, est résident ou non). On ecrit deux fonctions pour faire ce test qui renvoie True si le candidat est du type proposé et False si ce n'est pas le cas.
def estBoursier(voeu):
return voeu["type"] == BoursierResident or voeu["type"] == BoursierNonResident
def estResident(voeu):
return voeu["type"] == BoursierResident or voeu["type"] == NonBoursierResident
print("Le vœu exemple est-il boursier ?", estBoursier(exemple_voeu))
print("Le vœu exemple est-il résident ?", estResident(exemple_voeu))
L'algorithme ParcoursSup permet aux établissements de fixer des quotas minimaux sur le nombre de boursiers-ères et de résidents-es admis. On a besoin de connaître ces deux contraintes, exprimées en pourcentage donné comme un entier entre $0$ et $100$.
tauxMinBoursiersPourcents = 20
tauxMinResidentsPourcents = 0
On devrait avoir tout ce qu'il faut.
L'algorithme est présenté comme suit dans le document de référence :
Nous avons essayé de rendre le code suivant le plus clair possible mais il est nécessairement un peu long. Forcez vous à le lire jusqu'au bout !
La fonction suivante passe par plusieurs étapes.
Je trouve le terme voeuxClasses ambigue, voeuxFormation pour l'ensemble des voeux d'une formation serait plus clair. Ton avis ?
Données d'entrée :
Grosses étapes :
def calculerOrdreAppel(
voeuxClasses,
tauxMinBoursiersPourcents,
tauxMinResidentsPourcents,
afficheTout=True
):
# On définit la fonction d'affichage que si on demande d'afficher tout.
if afficheTout == False:
def affiche(*args): pass
else: affiche = print
# 1. On tri la liste voeuxClasses par ordre de rang
affiche("\n1. On trie les vœux par rang")
affiche(" Avant le tri :", voeuxClasses)
voeuxClasses.sort(key=lambda voeu: -voeu["rang"])
affiche(" Après le tri :", voeuxClasses)
# 2. On crée des listes de vœux pour chaque types de candidats
affiche("\n2. On crée des listes triées pour chaque types de candidats")
filesAttente = {
BoursierResident: [ ], # liste vide associée à chaque type
BoursierNonResident: [ ], # liste vide associée à chaque type
NonBoursierResident: [ ], # liste vide associée à chaque type
NonBoursierNonResident: [ ], # liste vide associée à chaque type
}
# Chaque voeu classé est placé dans la liste correspondante,
# en fonction du type du candidat.
# Les quatre listes obtenues sont ordonnées par rang de classement,
# comme l'est la liste voeuxClasses.
nbBoursiersTotal = 0
nbResidentsTotal = 0
for voeu in voeuxClasses:
# on ajoute le voeu à la fin de la file (FIFO) correspondante
filesAttente[voeu["type"]].append(voeu)
if estBoursier(voeu):
nbBoursiersTotal += 1
if estResident(voeu):
nbResidentsTotal += 1
# Affichage des listes d'attentes
affiche(" Liste des boursiers résident :\n\t", filesAttente[1])
affiche(" Liste des boursiers non résident :\n\t", filesAttente[2])
affiche(" Liste des boursiers non résident :\n\t", filesAttente[3])
affiche(" Liste des non boursiers non résident :\n\t", filesAttente[4])
nbVoeuxClasses = len(voeuxClasses)
nbAppeles = 0
nbBoursiersAppeles = 0
nbResidentsAppeles = 0
# la boucle ajoute les candidats un par un à la liste suivante, dans l'ordre d'appel.
# On commence par un ordre d'appel vide (liste vide).
ordreAppel = [ ]
affiche("\n3. Début de la boucle while, on remplit l'ordre d'appel pour respecter les critères")
while len(ordreAppel) < nbVoeuxClasses:
# on calcule lequel ou lesquels des critères boursiers et résidents
# contraignent le choix du prochain candidat dans l'ordre d'appel
contrainteTauxBoursier = (nbBoursiersAppeles < nbBoursiersTotal) and ((nbBoursiersAppeles * 100) < tauxMinBoursiersPourcents * (nbAppeles + 1))
affiche("\tLes boursiers représentent : ", (nbBoursiersAppeles * 100), "% et on en veut ", tauxMinBoursiersPourcents * (nbAppeles + 1), "%")
affiche("\tIl reste des boursiers à appeler : ", (nbBoursiersAppeles < nbBoursiersTotal))
affiche("\t\t=> Du coup on va appeler un boursier : ", contrainteTauxBoursier)
contrainteTauxResident = (nbResidentsAppeles < nbResidentsTotal) and ((nbResidentsAppeles * 100) < tauxMinResidentsPourcents * (nbAppeles + 1))
affiche("\tLes résident représentent : ", (nbResidentsAppeles * 100), "% et on en veut ", tauxMinResidentsPourcents * (nbAppeles + 1), "%")
affiche("\tIl reste des résidents à appeler : ", (nbResidentsAppeles < nbResidentsTotal))
affiche("\t\t=> Du coup on va appeler un résident : ", contrainteTauxResident)
# on fait la liste des voeux satisfaisant
# les deux contraintes à la fois, ordonnée par rang de classement
eligibles = [ ]
for queue in filesAttente.values():
if queue:
voeu = queue[-1] # le meilleur a été ajouté en dernier
if ((estBoursier(voeu) or not contrainteTauxBoursier)
and (estResident(voeu) or not contrainteTauxResident)):
eligibles.append(voeu)
affiche(" Les vœux satisfaisant les deux contraintes à la fois, ordonnés par rang de classement sont :\n", eligibles)
# stocke le meilleur candidat à appeler tout en respectant
# les deux contraintes si possible
# ou à défaut seulement la contrainte sur le taux boursier
meilleur = None
if len(eligibles) > 0:
# on prend le meilleur de cette liste
meilleur = max(eligibles, key=lambda voeu: -voeu["rang"])
affiche(" La liste des éligibles n'est pas vide, donc le-la meilleur-e est le-la meilleur-e de cette liste =", meilleur)
else:
# la liste peut être vide dans le cas où les deux contraintes
# ne peuvent être satisfaites à la fois.
# Dans ce cas nécessairement il y a une contrainte sur chacun des deux taux
# (donc au moins un boursier non encore sélectionné)
# et il ne reste plus de boursier résident,
# donc il reste au moins un boursier non résident
CandidatsBoursierNonResident = filesAttente[BoursierNonResident]
meilleur = max(CandidatsBoursierNonResident, key=lambda voeu: -voeu["rang"])
affiche(" La liste des éligibles est pas vide, donc le-la meilleur-e est le-la meilleur-e de la liste des boursier-e-s non résident-e-s =", meilleur)
# suppression du candidat choisi de sa file d'attente
saFileAttente = filesAttente[meilleur["type"]]
affiche(" On vérifie si le-la meilleur", meilleur, "est aussi le-la meilleur-e de sa liste contenant", len(saFileAttente), "candidat-e-s du type", meilleur['type'])
meilleur_de_sa_liste = saFileAttente.pop()
# ajout du meilleur candidat à l'ordre d'appel
ordreAppel.append(meilleur)
nbAppeles += 1
affiche(" On ajoute le meilleur", meilleur, "à l'ordre d'appel, c'est le-la", nbAppeles, "ème à être appelé-e.")
if estBoursier(meilleur):
nbBoursiersAppeles += 1
affiche(" En plus, c'est le-la", nbBoursiersAppeles, "ème boursier-e à être appelé-e.")
if estResident(meilleur):
nbResidentsAppeles += 1
affiche(" En plus, c'est le-la", nbResidentsAppeles, "ème résident-e à être appelé-e.")
# fin de la boucle while
affiche("\n3. On a terminé la boucle, on a rempli l'ordre d'appel.")
return ordreAppel
Avec les valeurs prisent ci-dessus comme exemple :
calculerOrdreAppel(
voeuxClasses,
tauxMinBoursiersPourcents,
tauxMinResidentsPourcents
)
Sur cet exemple, on constate que la contrainte sur les boursiers a pu aider le candidat Florian, qui étais classé 6ème (le meilleur parmi les boursiers) à remonter devant certains candidats non boursiers.
La suite est logique, entre Alice, Bob, Christophe et Dora, puis on voit que Guillaume est aussi passé devant d'autres candidats avec la contrainte de 20% de boursiers (après les 5 premiers candidats, dont un boursier, il faut ajouter Guillaume pour continuer à satisfaire la contrainte de minimum 20% de boursiers).
Les morceaux suivants sont hors programme, ne regardez pas le détail du code.
Ils vont permettre d'explorer interactivement l'influence des deux taux minimums (taux de boursiers-ères et résidents-entes) sur le résultat de l'ordre d'appel.
from IPython.display import display
from ipywidgets import interact
On définit la fonction que l'on souhaite explorer :
def fait_fonctionATester(
voeuxClasses,
tauxMinBoursiersPourcents=20,
tauxMinResidentsPourcents=0
):
def fonctionATester(
tauxMinBoursiersPourcents=tauxMinBoursiersPourcents,
tauxMinResidentsPourcents=tauxMinResidentsPourcents
):
ordreAppel = calculerOrdreAppel(
voeuxClasses,
tauxMinBoursiersPourcents,
tauxMinResidentsPourcents,
afficheTout=False # on cache la sortie
)
res = [voeu["rang"] for voeu in ordreAppel]
return res
return fonctionATester
Par exemple, si on demande $90%$ de boursiers, on voit que les candidats classés 6ème (Florian) et 7ème (Guillaume) sont mis en avant.
fait_fonctionATester(voeuxClasses)(90, 0)
Mais la visualisation interactive suivante permet de suivre l'influence des deux paramètres sur la liste des rangs finaux beaucoup plus facilement.
interact(
fait_fonctionATester(voeuxClasses, tauxMinBoursiersPourcents=20, tauxMinResidentsPourcents=0),
tauxMinBoursiersPourcents=(0, 100, 1),
tauxMinResidentsPourcents=(0, 100, 1)
);
Ca ne marche pas sur ma machine, mais sur une installation neuve ou sur MyBinder, tout fonctionne bien…
Le document de référence propose deux autres exemples de petite taille, notés A2 et A4.
Comme dans le document de référence : C1 C2 C3 C4 C5 B6 C7 C8, où B6 est boursier, et tous sont non résidents. (Plus besoin des noms dans cet exemple là , on les enlève)
C1 = { "type": NonBoursierNonResident, "rang": 1 }
C2 = { "type": NonBoursierNonResident, "rang": 2 }
C3 = { "type": NonBoursierNonResident, "rang": 3 }
C4 = { "type": NonBoursierNonResident, "rang": 4 }
C5 = { "type": NonBoursierNonResident, "rang": 5 }
B6 = { "type": BoursierNonResident, "rang": 6 }
C7 = { "type": NonBoursierNonResident, "rang": 7 }
C8 = { "type": NonBoursierNonResident, "rang": 8 }
voeuxClasses_A2 = [C1, C2, C3, C4, C5, B6, C7, C8]
On reproduit l'exemple du document de référence, avant de vous laisser explorer l'influence des deux taux.
fait_fonctionATester(voeuxClasses_A2)(2, 0)
interact(
fait_fonctionATester(
voeuxClasses_A2, tauxMinBoursiersPourcents=2, tauxMinResidentsPourcents=0
),
tauxMinBoursiersPourcents=(0, 100, 1),
tauxMinResidentsPourcents=(0, 100, 1)
);
On remarque que même un minimum de $2%$ de boursiers-ères suffit à faire remonter le candidat B6 en tête.
L'ordre des rangs est respecté seulement si on a aucune contrainte sur le taux de boursiers.
fait_fonctionATester(voeuxClasses_A2)(0, 0)
Comme dans le document de référence : C1 B2 B3 C4 C5 C6 C7 B8 C9 C10, où B2 B3 et B8 sont boursiers et tous sont non résidents.
C1 = { "type": NonBoursierNonResident, "rang": 1 }
B2 = { "type": BoursierNonResident, "rang": 2 }
B3 = { "type": BoursierNonResident, "rang": 3 }
C4 = { "type": NonBoursierNonResident, "rang": 4 }
C5 = { "type": NonBoursierNonResident, "rang": 5 }
C6 = { "type": NonBoursierNonResident, "rang": 6 }
C7 = { "type": NonBoursierNonResident, "rang": 7 }
B8 = { "type": BoursierNonResident, "rang": 8 }
C9 = { "type": NonBoursierNonResident, "rang": 9 }
C10 = { "type": NonBoursierNonResident, "rang": 10 }
voeuxClasses_A4 = [C1, B2, B3, C4, C5, C6, C7, B8, C9, C10]
On reproduit l'exemple du document de référence, avant de vous laisser explorer l'influence des deux taux.
fait_fonctionATester(voeuxClasses_A4)(10, 0)
interact(
fait_fonctionATester(
voeuxClasses_A4, tauxMinBoursiersPourcents=10, tauxMinResidentsPourcents=0
),
tauxMinBoursiersPourcents=(0, 100, 1),
tauxMinResidentsPourcents=(0, 100, 1)
);
On va utiliser ipythonblocks.
from ipythonblocks import BlockGrid
couleursTypeCandidat = {
NonBoursierNonResident: (200, 200, 200), # gris
NonBoursierResident: (0, 0, 200), # bleu
BoursierNonResident: (200, 0, 0), # rouge
BoursierResident: (200, 0, 200), # violet
}
def voirListeVoeu(voeuxClasses):
nbVoeux = len(voeuxClasses)
grille = BlockGrid(nbVoeux, 1, fill=(200, 200, 200))
for i, voeu in enumerate(voeuxClasses):
typeCandidat = voeu["type"]
couleur = couleursTypeCandidat[typeCandidat]
grille[0, i].set_colors(*couleur)
return grille
voeuxClasses_A4
voirListeVoeu(voeuxClasses_A2)
voirListeVoeu(voeuxClasses_A4)
Les candidats-ates boursiers-ères sont en rouges. On peut visualiser que le calcul de l'ordre d'appel va faire remonter un-e candidat-e boursier-ère :
voirListeVoeu(calculerOrdreAppel(voeuxClasses_A2, 20, 20, False))
voirListeVoeu(calculerOrdreAppel(voeuxClasses_A4, 20, 20, False))
Pour des exemples plus complets, on génère ici une liste de vœux de taille fixée, où chaque vœu sera aléatoirement boursier ou non, résident ou non. Cela va permettre de mieux comprendre l'algorithme sur des entrées de taille plus grande.
import random
import math
def fait_voeuxClasses_aleatoire(
taille=100,
tauxBoursiers=20,
tauxResidents=20,
random_seed=0,
):
random.seed(random_seed)
# on commence par avoir une liste triée de non boursier non résident
voeuxClasses = [
{'type': NonBoursierNonResident, 'rang': i}
for i in range(1, taille + 1)
]
# pour certains candidats aléatoires on les transforme en boursier et/ou en résident
nbBoursiers = int(math.ceil(taille * tauxBoursiers / 100.0))
for voeu in random.sample(voeuxClasses, nbBoursiers):
voeu['type'] = BoursierNonResident
nbResidents = int(math.ceil(taille * tauxResidents / 100.0))
for voeu in random.sample(voeuxClasses, nbResidents):
if estBoursier(voeu): voeu['type'] = BoursierResident
else: voeu['type'] = NonBoursierResident
# on la mélange enfin et on la renvoie
random.shuffle(voeuxClasses)
return voeuxClasses
voeuxClasses_exemple = fait_voeuxClasses_aleatoire(
70,
tauxBoursiers=20, # ~14 boursiers
tauxResidents=20 # ~14 résidents
)
On va avoir en moyenne 14 boursiers-ères et 14 résidents-entes, avec quelques intersections.
voirListeVoeu(voeuxClasses_exemple)
voeuxClasses_exemple_sortie = calculerOrdreAppel(
voeuxClasses_exemple,
tauxMinBoursiersPourcents=100,
tauxMinResidentsPourcents=100,
afficheTout=False
)
voirListeVoeu(voeuxClasses_exemple_sortie)
On visualise assez bien : avec une contrainte un peu folle qui impose de prendre 100% des boursiers-ères (en rouge ou violet) et 100% des résidents (en bleu ou violet), l'algorithme d'appel a d'abord pris les candidats de types BoursierResident
(violet), puis BoursierNonResident
(rouge) car le décret officiel demande de favoriser les boursiers, puis les NonBoursierResident
(bleu) et enfin les autres.
On peut combiner la visualisation interactive et la visualisation avec des couleurs. On va d'abord avoir une
def fait_fonctionATester_couleurs(
taille=70,
tauxBoursiers=20,
tauxResidents=20,
random_seed=0,
tauxMinBoursiersPourcents=10,
tauxMinResidentsPourcents=0
):
def fonctionATester(
taille=taille,
tauxBoursiers=tauxBoursiers,
tauxResidents=tauxResidents,
random_seed=random_seed,
tauxMinBoursiersPourcents=tauxMinBoursiersPourcents,
tauxMinResidentsPourcents=tauxMinResidentsPourcents
):
voeuxClasses = fait_voeuxClasses_aleatoire(
taille,
tauxBoursiers=tauxBoursiers,
tauxResidents=tauxResidents,
random_seed=random_seed
)
print("Visualisation de l'algorithme de calcul de l'ordre d'appel.")
print("1. Vœux non triés par rang :")
display(voirListeVoeu(voeuxClasses))
print("2. Vœux triés par rang, mais pas par l'algorithme :")
voeuxClasses_tries = sorted(voeuxClasses, key=lambda voeu: -voeu["rang"])
display(voirListeVoeu(voeuxClasses_tries))
print("3. Vœux triés par l'algorithme :")
ordreAppel = calculerOrdreAppel(
voeuxClasses,
tauxMinBoursiersPourcents,
tauxMinResidentsPourcents,
afficheTout=False # on cache la sortie
)
display(voirListeVoeu(ordreAppel))
return fonctionATester
interact(
fait_fonctionATester_couleurs(
taille=70,
tauxBoursiers=20,
tauxResidents=20,
random_seed=0,
tauxMinBoursiersPourcents=10,
tauxMinResidentsPourcents=0
),
taille=(1, 500, 1),
tauxBoursiers=(0, 100, 1),
tauxResidents=(0, 100, 1),
random_seed=(0, 1000, 1),
tauxMinBoursiersPourcents=(0, 100, 1),
tauxMinResidentsPourcents=(0, 100, 1)
);
Jusqu'ici on a visualisé l'ordre d'appel de toute la liste. On va se focaliser sur un seul candidat pour voir l'influence du comportement de tous les paramètres sur son ordre d'appel final.
def voirListeVoeu_avecFocus(voeuxClasses, randDuFocus):
grille = voirListeVoeu(voeuxClasses)
for i, voeu in enumerate(voeuxClasses):
if voeu['rang'] != randDuFocus:
r, g, b = grille[0, i].rgb
grille[0, i].rgb = (max(0, int(r*0.65)), max(0, int(g*0.65)), max(0, int(b*0.65)))
return grille
def fait_fonctionATester_unSeulFocus(
taille=70,
tauxBoursiers=20,
tauxResidents=20,
random_seed=0,
rangDuFocus=1,
focusEstBoursier=False,
focusEstResident=False,
tauxMinBoursiersPourcents=10,
tauxMinResidentsPourcents=0
):
def fonctionATester(
taille=taille,
tauxBoursiers=tauxBoursiers,
tauxResidents=tauxResidents,
random_seed=random_seed,
rangDuFocus=1,
focusEstBoursier=False,
focusEstResident=False,
tauxMinBoursiersPourcents=tauxMinBoursiersPourcents,
tauxMinResidentsPourcents=tauxMinResidentsPourcents
):
voeuxClasses = fait_voeuxClasses_aleatoire(
taille,
tauxBoursiers=tauxBoursiers,
tauxResidents=tauxResidents,
random_seed=random_seed
)
for voeu in voeuxClasses:
if voeu['rang'] == rangDuFocus:
if focusEstBoursier and focusEstResident:
voeu['type'] = BoursierResident
elif focusEstBoursier and not focusEstResident:
voeu['type'] = BoursierNonResident
elif not focusEstBoursier and focusEstResident:
voeu['type'] = NonBoursierResident
else:
voeu['type'] = NonBoursierNonResident
print("Visualisation de l'algorithme de calcul de l'ordre d'appel.")
print("1. Vœux non triés par rang :")
display(voirListeVoeu_avecFocus(voeuxClasses, rangDuFocus))
print("2. Vœux triés par rang, mais pas par l'algorithme :")
voeuxClasses_tries = sorted(voeuxClasses, key=lambda voeu: voeu["rang"])
display(voirListeVoeu_avecFocus(voeuxClasses_tries, rangDuFocus))
print("3. Vœux triés par l'algorithme :")
ordreAppel = calculerOrdreAppel(
voeuxClasses,
tauxMinBoursiersPourcents,
tauxMinResidentsPourcents,
afficheTout=False # on cache la sortie
)
display(voirListeVoeu_avecFocus(ordreAppel, rangDuFocus))
return fonctionATester
# https://ipywidgets.readthedocs.io/en/latest/examples/Using%20Interact.html#Arguments-that-are-dependent-on-each-other
import ipywidgets
taille_widget = ipywidgets.IntSlider(min=1, max=200, step=1, value=10)
rang_widget = ipywidgets.IntSlider(min=1, max=200, step=1, value=1)
def update_taille_max(*args):
rang_widget.max = taille_widget.value
rang_widget.observe(update_taille_max, 'value')
interact(
fait_fonctionATester_unSeulFocus(
taille=70,
tauxBoursiers=20,
tauxResidents=20,
random_seed=0,
rangDuFocus=1,
focusEstBoursier=False,
focusEstResident=False,
tauxMinBoursiersPourcents=10,
tauxMinResidentsPourcents=0
),
taille=taille_widget,
tauxBoursiers=(0, 100, 1),
tauxResidents=(0, 100, 1),
random_seed=(0, 1000, 1),
rangDuFocus=rang_widget,
focusEstBoursier=False,
focusEstResident=False,
tauxMinBoursiersPourcents=(0, 100, 1),
tauxMinResidentsPourcents=(0, 100, 1)
);
from IPython.display import HTML
HTML("<center><span style='color:red; font-size: xx-large;'>{}</span></center>".format('TODO '*54))
Ce petit notebook n'est pas terminé, c'est un test en cours de rédaction.