Quelle approche pour création de barème

Bonjour,

Quelle approche me conseillez-vous dans l’optique de créer des app qui permettent d’appliquer un résultat en fonction d’une valeur entrée?
Typiquement: un coureur a couru en 00:00:12,30 et ça renvoi la note de barème 11/20.

Merci de votre aide.

Salut Jean CHarles,

La clé de ton problème est la conversion du temp en valeur “calculcable” concrétement il faut que tu convertisse ton temp en timeInterval (seconde).

Une foi fait il suffit de déterminer quel temps il faut faire pour avoir 20 est quel temps il faut faire pour avoir 0.

Nous obtenons 2 points que l’on peut representé sur un graphique, on opbient donc une droite (la note sur l’axe des y et le temp sur l’axe des x). Il suffit maintenant simplement de déterminer l’equation de cette droite qui est de type y = ax + b

Un peu de math allé hop:

Reste à trouvé a et b.
Donc :
Nous avons ici 2 points. T(11,20) et Z(19,0), je considère ici que 11 secondes est un 20/20 et que 19 secondes et un zero pointé :slight_smile:
a = Zy - Ty / Zx - Tx = 0 - 20 / 19 - 11 = -20/8 = -2.5
bon vu qu’on conais maintenant a, b c’est très facile
y = -2,5x + b
on prend le point T par exemple ce qui nous donne
Ty = -2,5Tx + b
20 = -2.5x11 + b
20 = -27.5 + b
b = 27.5 + 20 = 47.5
Ouf enfin nous avons trouvé notre équation qui est
y = -2,5x + 47.5

Maintenant si un elève fait 15 seconde , il aura donc la note de y = -2.5x15 + 47.5 = 10 (10/20)

Maintenant il suffit d’ecrire une petite fonction qui recupère le temps en seconde et te retourne la note sur 20

En espérant avoir répondu a ta question

1 « J'aime »

Salut Samir et merci de ta réponse…

… qui m’a fait revenir 25 ans en terminale S!

Approche intéressante mais il s’agit d’un cas d’un barème perso et proportionnel. Or, à l’école, les barèmes sont imposés et pas forcément linéaires de façon algébrique.

La référence que j’ai, c’est celle que j’ai développé comme sur Excel: un tableau à 2 colonnes avec les temps et la note qui correspond. De l’autre côté une fonction qui récupère dans un label la valeur effectuée par le coureur et va rechercher via l’index la note. Sur excel sur l’imbrication des des fonctions INDEX et EQUIV.
D’autant que les temps effectués par les gamins ne tomberont jamais sur une valeur du tableau donc il faudra arrondir à la valeur supérieure.
Dans l’exemple si dessous, un garçon qui fait 54.5 aura la note 8.

00

Et oui, la conversion d’une valeur de type 00:00:54,500 en valeur numérique va me poser problème!
,-)

Ce n’est pas bien compliqué. C’est juste un tableau avec une liste de valeurs.

Tu fais une boucle pour parcourir le tableau, à la recherche de la valeur la plus proche du chronométrage du gamin. Et tu regardes la note correspondante. Essaye de le faire. Si tu n’arrives pas, je te montrerais comment faire.

1 « J'aime »

Effectivement si le barème ne repose pas sur un model mathématique je pense que la méthode de @Draken et la plus simple.

Et oui, la conversion d’une valeur de type 00:00:54,500 en valeur numérique va me poser problème!
,-)

Quand tu sera à cette étape fait moi signe je te montrerai comment faire :slight_smile:

Ok. Je reprends les cours de maxime et dès que j’ai avancé je reviens!
Je garde ta méthode sous le coude Samir, je pense que ça va me servir pour créer des barèmes proportionnels. Merci!

1 « J'aime »

Bonjour DRAKEN,

je relance ce sujet et en particulier cette question sur les barèmes. Je n’arrive pas à le faire et ce, d’autant que je ne maîtrise pas très bien les boucles et la manipulation des tableaux. Voici typiquement un tableau de barème que j’utilise tout le temps dans sa structure:

L’idée est donc de récupérer la valeur temps et de trouver la valeur la plus proche pour affecter une note sachant que ça dépend de son sexe. Si tu peux me mettre le pied à l’étrier, je suis preneur.

Bonne année et merci d’avance pour tes précieux conseils.

Hello @jeancharles.bidault,

Comment as-tu stocké ces valeurs? Un tableau filles, un tableau garçon, et un tableau note?

Voici une façon de le faire sachant que j’ai toujours une préférence pour le code lisible, quitte à ce qu’il soit un peu moins performant :

struct PalierNote {
    let tempsMaxiFilles:Double
    let tempsMaxiGarcons:Double
    let note:Double
}

var bareme:[PalierNote] = []
bareme.append(PalierNote(tempsMaxiFilles: 62.8, tempsMaxiGarcons: 52.4, note: 10))
bareme.append(PalierNote(tempsMaxiFilles: 63.4, tempsMaxiGarcons: 53.0, note: 9.5))
// ...
bareme.append(PalierNote(tempsMaxiFilles: 67.3, tempsMaxiGarcons: 56.6, note: 6.5))

func trouverNote(tempsEleve:Double, estUneFille:Bool) -> Double? {
    var noteTrouvee:Double?
    var indexPalier = 0
    while (noteTrouvee == nil && indexPalier < bareme.count) {
        let notePalier = bareme[indexPalier]
        if (estUneFille == true && notePalier.tempsMaxiFilles >= tempsEleve)
            || (estUneFille == false && notePalier.tempsMaxiGarcons >= tempsEleve) {
            noteTrouvee = notePalier.note
        } else {
            indexPalier = indexPalier + 1
        }
    }
    return noteTrouvee
}
//Exemples filles
trouverNote(tempsEleve: 62.0, estUneFille: true)
trouverNote(tempsEleve: 63.0, estUneFille: true)
trouverNote(tempsEleve: 64.0, estUneFille: true)
trouverNote(tempsEleve: 68.0, estUneFille: true)

//Exemples garcons
trouverNote(tempsEleve: 52.0, estUneFille: false)
trouverNote(tempsEleve: 53.0, estUneFille: false)
trouverNote(tempsEleve: 54.0, estUneFille: false)
trouverNote(tempsEleve: 48.0, estUneFille: false)

Je l’ai fait dans un Playground donc c’est à adapter à une vrai classe : déplacer la variable barème au bon endroit, la remplir au bon endroit, etc.

1 « J'aime »

Bonjour Maxime et merci pour ta réponse rapide!
si je comprends bien ta proposition, la notePalier correspond à l’indexPath dans une tableView?
Car l’idéal serait que ce barème soit dans une tableView modifiable et “triable” (au minimum manuellement). Je n’ai encore vu les structures mais je crois avoir compris l’esprit.

Comme tu me l’as demandé en MP, j’ai réfléchi à ton problème. Voici une solution n’utilisant pas les boucles. C’est moins optimisé qu’une boucle classique, mais le principe est facile à comprendre.

Voici une fonction explorant un tableau pour trouver l’indice de la valeur la plus proche d’une valeur précise :

    func chercherIndexValeurProche(valeur:Double, tableau:[Double]) -> Int {
    let ecarts = tableau.map {abs($0 - valeur)}
    let ecartMinimal = ecarts.min()!
    let indice = ecarts.index(of: ecartMinimal)!
    return indice
}

Cela fonctionne en créant un nouveau tableau, contenant l’écart entre la valeur recherchée et les valeurs du tableau.

L’opérateur .map permet de créer une copie d’un tableau contenant le résultat d’un calcul. Je l’utilise pour fabriquer un nouveau tableau renfermant la valeur absolue du décalage entre les valeurs du tableau et une valeur donnée.

Exemple j’ai un tableau contenant les valeurs suivantes :

let tableau = [12.0, 15.0, 18.0]

Et une valeur 14.0

 let tableau = [12.0, 15.0, 18.0]
 let valeur = 14.0
 let ecarts = tableau.map {abs($0 - valeur)}
 print ("Tableau : ", tableau)
 print ("Ecarts  : ", ecarts)

Tableau : [12.0, 15.0, 18.0]
Ecarts : [2.0, 1.0, 4.0]

Le décalage le plus faible est 1.0, qui correspond à la valeur 15.
Pour connaître la valeur la plus faible dans un tableau, il faut utiliser l’opérateur min().

let ecartMinimal = ecarts.min()!

Le point d’exclamation te surprend peut-être. C’est dus au fait que l’opérateur .min() retourne une valeur optionnelle. Swift considère que l’opération d’extraction de la valeur minimale d’un tableau peut échouer (par exemple si le tableau est vide). Il retourne donc une valeur de type Double?.

L’opérateur ! transforme l’information optionnelle en une valeur normale. Techniquement parlant c’est une erreur de convertir brutalement l’optionnelle en valeur normale avec un !, sans aucune vérification. Si le tableau est vide, c’est le plantage assurée. Mais bon, tu connais la structure de ton tableau, il ne devrais pas avoir d’erreur.

Une fois la valeur minimale du tableau connu, on peut connaître sa position (son index) avec l’opérateur .index().

let indice = ecarts.index(of: ecartMinimal)!

De la même manière que précédemment, l’opérateur .index() retourne une valeur optionnelle que je convertit en valeur normale, sans précautions à la hussarde.


Exemple d’utilisation :

let tempsGarcon = [0.0,  52.400, 53.000, 53.600, 54.200, 54.800, 55.400]
let tempsFille  = [0.0,  62.800, 63.400, 64.000, 64.600, 65.200, 65.900]
let listeNotes  = [10.0, 10.0,   9.5,     9,      8.5,    8,      7.5]

let temps = 54.654
let position = chercherIndexValeurProche(valeur: temps, tableau: tempsGarcon)
print ("temps : ", temps)
print ("Position dans le tableau : ", position)

temps : 54.654
Position dans le tableau : 5
Temps : 54.654 Note : 8.0

L’application indique que la valeur la plus proche de 54.654 est à la position 5 du tableau (valeur 54.800).

Pour connaître la note correspondante, il suffit de lire la position 5 du tableau des Notes.

let note = listeNotes[position]


Pour faciliter l’identification des filles et garçons, tu peux utiliser une énumération :

enum TypeEleve {
    case garcon
    case fille
}

Cela augmente la lisibilité du code.

// Calcul Note d'un élève
func calculNote(temps:Double, typeEleve:TypeEleve) -> Double {
    var index = 0
    switch typeEleve {
    case .garcon:
        index = chercherIndexValeurProche(valeur: temps,
                                                                  tableau: tempsGarcon)
    case .fille:
        index = chercherIndexValeurProche(valeur: temps,
                                                                  tableau: tempsFille)
        }
        let note = listeNotes[index]
        return note
    }

Mise en pratique :

   let temps = 54.654
   let sexe = TypeEleve.garcon
   let note = calculNote(temps: temps, typeEleve: sexe)
   print ("Temps : ", temps, " Note : ", note)

Temps : 54.654 Note : 8.0

Voilà, c’est juste une base de départ. Et une petite introduction aux opérateurs de tableau de Swift, très souples et très puissants. On peut par exemple trier un tableau en une ligne de code, quelque soit son contenu (nombres ou objets complexes). Ou extraire des informations utiles au sein d’une masse d’informations stockées dans des tableaux.


Voici la version « évoluée » de la méthode de recherche, retournant une valeur optionnelle en cas d’erreur.

func chercherIndexValeurProche(valeur:Double, tableau:[Double]) -> Int? {
    // Création d'un tableau contenant les écarts de temps
    let ecarts = tableau.map {abs($0 - valeur)}
    // Recherche du plus petit écart du tableau
    if let ecartMinimal = ecarts.min() {
        // Recherche de l'index du petit écart
        if let indice = ecarts.index(of: ecartMinimal) {
            // On retourne l'index de l'écart
            return indice
        }
    }
    // Une erreur s'est produite quelque part
    // On retourne la valeur nil
    return nil
}

C’est celle que j’utiliserais, en ajoutant un système de gestion d’erreur dans le code. Quelque chose comme :

if let position = chercherIndexValeurProche(..) {
   // calculs divers
   .....
} else {
  print (« ERREUR de recherche dans le tableau .. »)
}

:clap: jolie explication ! et très intéressant comme sujet. Ca me servira peu être un jour

Merci, vraiment merci pour ce cours très pédagogique. Je vais tenter de mettre à profit tout ça.
Pour l’instant je suis bloqué à la création du tableau et Realm. Sans tableau, pas de barèmes…:sob:

Super proposition @Draken, la solution est élégante car elle est courte et ça marche très bien pour un petit tableau comme celui-ci.
Par contre, comme tu l’as dit, c’est très peu performant car tu crées un autre tableau et tu le parcours quand même au final avec la fonction min. C’est donc à réserver pour les petits tableaux et à ne surtout pas utiliser sur des tableaux volumineux.

Je vois par contre un soucis : tu recherches la borne la plus proche mais ça ne tient pas compte des paliers de notes. C’est bien ce que tu voulais @jeancharles.bidault ? Car j’avais cru comprendre que le temps 54.654 devrait donner 8.5 et non 8.0 pour un garçon ?

Super proposition @Draken, la solution est élégante car elle est courte et ça marche très bien pour un petit tableau comme celui-ci.
Par contre, comme tu l’as dit, c’est très peu performant car tu crées un autre tableau et tu le parcours quand même au final avec la fonction min. C’est donc à réserver pour les petits tableaux et à ne surtout pas utiliser sur des tableaux volumineux.

Je vois par contre un soucis : tu recherches la borne la plus proche mais ça ne tient pas compte des paliers de notes. C’est bien ce que tu voulais @jeancharles.bidault ? Car j’avais cru comprendre que le temps 54.654 devrait donner 8.5 et non 8.0 pour un garçon ?
[/quote]

Exact! @mbritto. J’avais pas fait attention tellement je suis englué dans mon problème de tableView sur "ça passe pas! "

J’ai codé exactement ce que jeancharles a demandé :

L’idée est donc de récupérer la valeur temps et de trouver la valeur la plus proche pour affecter une note sachant que ça dépend de son sexe.

N’ayant pas compris le besoin d’un seuil, j’ai cherché la valeur mathématiquement la plus proche, qu’elle soit au-dessus ou au dessous du temps de l’élève.

Ce n’est pas difficile à corriger. Je n’ai pas le temps de taper le code maintenant et de vérifier si cela fonctionne. je le ferait ce soir à tête reposé.

Bonjour Draken,

effectivement, je n’ai pas été très explicite dans ma demande. Déformation pro car pour un prof d’EPS c’est évident, sur des données de temps, on arrondi au temps réellement fait et non pas au plus proche. Ma formulation de question n’était vraiment pas explicite et tu as répondu de façon logique!

Voici une solution.

Le principe est toujours le même : créer une liste des écarts entre le temps de l’élève et les valeurs du barème, pour ne garder que l’écart le plus petit. Mais au lieu de calculer l’écart de manière absolue (en ne prenant que la valeur absolue), je ne conserve que les écarts au dessus de 0 (valeur positive).

Exemple :

Le temps joueur est 54.654. Il se trouve entre les seuils 54.200 (note de 8,5) et 54.800 (note de 8).

54.654 - 54.200 = 0.454
54.654 - 54.800 = -0.146

Mathématiquement parlant, le temps du joueur est plus proche de la valeur 54.800 (note de 8), puisque sa « distance" n’est que de 0.146, alors qu’elle est 4 fois plus grande dans l’autre cas. Pourtant cette valeur n’est pas bonne, puisqu’elle ne respecte pas le système de seuil.

Pour que les calculs se fassent avec l’effet de seuil désiré, il faut ne tenir compte que des écarts positifs.

func chercherIndexValeurAvecSeuil(valeur:Double, tableau:[Double]) -> Int {
        // Calcul liste des écarts
        let ecarts = tableau.map {valeur - $0}
        // Filtrage des valeurs positives
        // On crée un nouveau tableau en ne gardant que
        // les chiffres au dessus de 0
        let listeEcartsPositifs = ecarts.filter {$0>0}
        // On recherche le plus petit écart positif
        let ecartMinimal = listeEcartsPositifs.min()!
        // On recherche la position de l'écart
        let indice = ecarts.index(of: ecartMinimal)!
        return indice
    }

Pour ce faire j’ai utilisé un autre opérateur de tableau : .filter, permettant de créer une copie d’un tableau en ne gardant que les valeurs correspondantes à un « filtre logiciel ».

let listeEcartsPositifs = ecarts.filter {$0>0}

Le filtre est simple : ne conserver que les valeurs au-dessus de 0

Ensuite je cherche la plus petite valeur dans la liste des écarts positifs et basta …

J’ai ajouté une boucle à mon programme, juste pour créer un test sur plusieurs valeurs de temps. Et modifié calculNote() pour utiliser la nouvelle méthode chercherIndexValeurAvecSeuil().

let echantillonsTests = [52.300, 53.254, 54.654, 54.932, 55.987]
        
for temps in echantillonsTests {
      let sexe = TypeEleve.garcon
      let note = calculNote(temps: temps, typeEleve: sexe)
      print ("Temps : ", temps, " Note : ", note)
 }

Résultats :

Temps : 52.3 Note : 10.0
Temps : 53.254 Note : 9.5
Temps : 54.654 Note : 8.5
Temps : 54.932 Note : 8.0
Temps : 55.987 Note : 7.5

Je pense que c’est conforme à ton attente


L’opérateur de filtre est très pratique pour extraire des informations d’un tableau. Par exemple, tu pourrais l’utiliser pour créer un nouveau tableau ne contenant que les élèves avec la note 10, ou tous les élèves au dessous de la note 5, etc … Toutes les conditions sont possibles

1 « J'aime »

J’ai quelques restes de mon époque en tant que prof d’EPS :)) C’est pour ça que j’ai compris ce que tu voulais dire :grin:

1 « J'aime »

Draken, merci pour ton suivi de dossier! J’ai bien compris le fonctionnement et la démarche. Je vais essayer de contextualiser tout ça dans d’autres contextes. Cependant, le résultat attendu n’est pas encore celui-là

Citation

mais plutôt:
Temps : 53.254 Note : 10

Temps : 54.654 Note : 8

Temps : 54.932 Note : 7.5

Temps : 55.987 Note : 7

Bref, un index en dessous. J’ai résolu le problème en ajoutant +1
func chercherIndexValeurAvecSeuil(valeur:Double, tableau:[Double]) → Int {
// Calcul liste des écarts
let ecarts = tableau.map {valeur - $0}
// Filtrage des valeurs positives
// On crée un nouveau tableau en ne gardant que
// les chiffres au dessus de 0
let listeEcartsPositifs = ecarts.filter {$0>0}
// On recherche le plus petit écart positif
let ecartMinimal = listeEcartsPositifs.min()!
// On recherche la position de l’écart
let indice = ecarts.index(of: ecartMinimal)! + 1
return indice
}

Et là, ça fonctionne!