Aller au contenu principal


Une moyenne pondérée

Dans les 3 versions du code, le nom des fonctions à utiliser sont en anglais :

  • SUM() pour SOMME()
  • IF() pour SI()
  • ISNUMBER(() pour ESTNUM()
  • OFFSET() pour DECALER

Pour MS Excel et LO Calc, le langage de programmation est VBA, pour G Sheet, c'est Javascript.

Les objets utilisés, les propriétés et méthodes qui leurs sont appliquées, sont décrites dans les billets respectifs :

Billet créé le :
04 fév 2022

1. Google Sheet

 

Remarque : Contrairement à MS Excel ou LO Calc, il n'est pas possible d'appeler une fonction interne (COUNT ou OFFSET par exemple) directement dans le code de la macro. Il faut passer par l'intermédiaire d'une cellule de la feuille de calcul en utilisant les propriétés "value" et "formula" de la cellule.

Aucune difficulté n'est à signaler dans ce code.
La 1ère boucle calcule le nombre de devoirs NbDevoirs (pour la suite je suppose que ce calcul rend la valeur 4).

Les 2 variables de type "chaînes de caractères (string) "lesCoef"  et "lesNotes" sont utilisées pour simplifier l'écriture et éviter les fautes de frappe lors de la saisie des variables de type chaîne "leNum" (le numérateur) et "leDeno" (le dénominateur) de la formule finale (variable "laFormule").

  1. lesCoef  qui a pour valeur  "OFFSET(D3;0;0;1;4)" lorsque NbDevoirs vaut 4 ;

  2. lesNotes qui est calculée à chaque tour de la 2ème boucle et qui vaudra par exemple  "OFFSET(D6;0;0;1;4)"  au 3ème tour.

A chaque tour t de la 2ème boucle,  la variable chaîne "laFormule" est calculée puis est passée en paramètre de la méthode formulaArray de la cellule At pour produire l'affichage cherché.

 

2. Microsoft Excel (v2010)

Remarquons l'utilisation des méthodes COUNTA()  et COUNT() sur l'objet racine WorksheetFunction pour calculer respectivement :

  • nbEleves : le nombre de tour de boucles à effectuer ;

  • nbDevoirs : la longueur des plages de cellules.

 

Dans une formule, si une fonction a plusieurs paramètres, ceux-ci doivent être séparés par une virgule alors que c'est toujours des points virgules comme dans les feuilles de calcul pour LO Calc et G Sheet.

L'objet racine WorksheetFunction ne dispose pas de la méthode OFFSET. Le calcul du nombre de devoir (1ère boucle du code) qui est le résultat de :

COUNT(OFFSET($" + CStr(i) + ":$" + CStr(i) + ";0;3;1;COUNTA($" + CStr(i) + ":$" + CStr(i) + ")))"

nécessite donc d'adapter le calcul de la plage sur laquelle porte l'opération : OFFSET().

Par exemple, pour obtenir la plage "C3:C6" correspondant à OFFSET(C3;0;0;1;4), il faut appliquer successivement les méthodes Offset() et Resize() sur l'objet de type Range "C3" (la cellule C3) par :

var laPlage
set laPlage = ActiveSheet.Range("C3").Offset(0, 0).Resize(1,4)

Le 2nd paramètre de resize() se calcule par la méthode COUNTA() de l'objet système WorksheetFunction.
Cela explique le code de la 1ère boucle :

        Set laPlage = ActiveSheet.Range("$" + CStr(i) + ":$" + CStr(i))
        Set laPlage = ActiveSheet.Range("C" + CStr(i)).Offset(0, 0).Resize(1, WorksheetFunction.CountA(laPlage))

Il ne reste plus qu'à compter le nombre de nombres de la plage résultat en appliquant la méthode COUNT() de l'objet système WorksheetFunction sur l'objet "laPlage" obtenu.

         nbNoteI = WorksheetFunction.Count(laPlage)

A l'intérieur de la boucle 2 qui calcule la moyenne pondérée apparait une difficulté d'un autre ordre.

En effet, en supposant qu'il y a 4 devoirs à compter, le code suivant qui calcule la moyenne pondérée produit l'erreur d'exécution 1004 due dans mon cas à une chaîne de caractère dont la taille dépasse les 200 caractères. Ce programme (expliqué ICI) fonctionne cependant parfaitement sous Google Sheet (onglet 1 ci-dessous) et sous LO Calc (onglet 3 ci-dessous).

  
         lesCoef = "OFFSET(D2,0,0,1,4)"
         lesNotes = "OFFSET(D3,0,0,1,4)"

         'la moyenne
         
         leNum = "(SUM(IF(ISNUMBER(" + lesCoef + "),IF(ISNUMBER(" + lesNotes + ")," + lesNotes + "*" + lesCoef + ",0)," + lesNotes + ")))"
         leDeno = "(SUM(IF(ISNUMBER(" + lesNotes + "),IF(ISNUMBER(" + lesCoef + ")," + lesCoef + ",1),0)))"  
      
         MsgBox Len(leNum) + Len(leDeno)
        
         'Cette ligne fonctionne
         ActiveSheet.Cells(20, 1).FormulaArray = "=" + leNum

         'Cette ligne fonctionne
         ActiveSheet.Cells(20, 1).FormulaArray = "=" + leDeno

        'Cette ligne produit l'erreur d'exécution
         ActiveSheet.Cells(20, 1).FormulaArray = "=" + leNum + "/ " + leDeno
       

Pour outrepasser cette erreur, j'ai modifié le code pour raccourcir les chaînes "leNum" et  "leDeno" en calculant le décalage en dehors de la formule grâce aux méthodes OFFSET et RESIZE utilisées comme indiqué au début de ce chapitre pour obtenir les plages "lesCoef" et "lesNotes". Attention, maintenant les variables "lesCoef" et "lesNotes" sont des objets "range" et non des chaînes de caractères.

Enfin, grâce à la propriété address de ces objets ("lesCoef.address et "lesNotes.address), nous construisons les chaînes "leNum" et "leDeno".

Remarquez enfin :

  • les 3 arguments de la fonction IF qui sont séparés par des virgules selon la syntaxe imposée ;

  • l'utilisation de la méthode formulaArray sur la cellule qui affichera la moyenne.

 

3. Libre Office Calc (v7.14)

Remarquons l'utilisation de la méthode "callFunction" d'une instance du service "com.sun.star.sheet.FunctionAccess" pour utiliser les fonctions de calcul interne COUNTA  et COUNT  pour calculer respectivement :

  • nbEleves : le nombre de tour de boucles à effectuer ;
  • nbDevoirs : la longueur des plages de cellules

Aucune difficulté n'est à signaler dans ce code.
La 1ère boucle calcule le nombre de devoirs NbDevoirs (pour la suite je suppose que ce calcul rend la valeur 4).

Les 2 variables de type "chaînes de caractères (string) " lesCoef"  et "lesNotes" sont utilisées pour simplifier l'écriture et éviter les fautes de frappe lors de la saisie des variables de type chaîne "leNum" (le numérateur) et "leDeno" (le dénominateur) de la formule finale (variable "laFormule").

  1. 1esCoef  qui a pour valeur  "OFFSET(D3;0;0;1;4)" lorsque NbDevoirs vaut 4
  2. lesNotes qui est calculée à chaque tour de la 2ème boucle et qui vaudra par exemple  "OFFSET(D6;0;0;1;4)"  au 3ème tour

A chaque tour  t de la 2ème boucle  la variable chaîne "laFormule" est calculée puis est passé en paramètre de la méthode formulaArray de la cellule At pour produire l'affichage cherché.

4. Le code dans les 3 versions

Cliquez sur le bouton pour copier le code dans le presse papier
    
 function calculerMoyenne(){
    var laCelluleBcle
    var laZone
    var laFeuille = SpreadsheetApp.getActiveSheet();
    var laCellule = laFeuille.getRange("A2");
    laCellule.setValue ="Moyenne pond.";
    //la cellule A3 contiendra le nb de ligne contenant des noms d'élèves donc le nombre d'eleves
    //=NBVAL($C:$C)
    LaCellule = laFeuille.getRange("A3");
    laFormule = "=COUNTA($C:$C)";
    laCellule.setFormula(laFormule);
    nbEleves = laCellule.getValue();
    //Calcul du nb de devoirs donnés
    //Bcler car peut être absence à un devoir
    var nbDevoirs = 0;
    LaCellule = laFeuille.getRange("A"+ (nbEleves + 2));
    for ( i = 4; i < (nbEleves +3) ; i++) {   
           laFormule = "=COUNT(OFFSET($"+i+":$"+ i+";0;3;1;COUNTA($"+i+ ":$"+i+")))";
           laCellule.setFormula(laFormule);
           var nbNoteI=laCellule.getValue()
           if (nbDevoirs < nbNoteI) {
            nbDevoirs = nbNoteI
           }
    }
    // lesCoef est la plage D3:G3 si 4 devoirs
    var lesCoef = "OFFSET(D3;0;0;1;"+nbDevoirs+")";
    for ( i = 4; i < (nbEleves +3) ; i++) {
       // lesNotes est par exemple la plage D7:G7 (eleve n° 5)
       var lesNotes = "OFFSET(D"+ i + ";0;0;1;"+nbDevoirs+")";
           //la moyenne
       var leNum ="SUM(IF(ISNUMBER("+lesCoef+");IF(ISNUMBER("+lesNotes+");"+lesNotes+"*"+lesCoef+";0);"+lesNotes+"))";
       var leDeno = "SUM(IF(ISNUMBER("+lesNotes+");IF(ISNUMBER("+lesCoef+");"+lesCoef+";1);0))";
       laFormule = "=ARRAYFORMULA(" + leNum + "/" + leDeno + ")"
           var laCelluleBcle = SpreadsheetApp.getActiveSheet().getRange( i , 1 );
           laCelluleBcle.setFormula(laFormule);
    }
}
Cliquez sur le bouton pour copier le code dans le presse papier
 Sub calculerMoyenne()
   Dim laCellule As Object, laCelluleBcle As Object, laPlage As Object
   Dim laNote As Double, nbNoteI As Double
   Dim nbEleves As Integer, nbDevoirs As Integer
   Dim i As Integer
   Dim nomCel As String, laFormule As String
   Dim lesCoef As Object, lesNotes As Object
   Dim leNum As String, leDeno As String
   '
   Set laCellule = ActiveSheet.Range("A2")
   laCellule.Value = "Moyenne pond."
   '
   'le nombre d'élèves
   '=NBVAL($C:$C)- 1
   Set laPlage = ActiveSheet.Range("$C:$C")
   nbEleves = WorksheetFunction.CountA(laPlage) - 1
   '
   Set laCellule = ActiveSheet.Range("A1")
   nbDevoirs = 0
   For i = 3 To nbEleves + 2
       'COUNT(OFFSET($" + CStr(i) + ";0;0;1;COUNTA($" + CStr(i) + ":$" + CStr(i) + ")))"
       ' la fonction OFFSET pas définie dans worksheetFunction d'ou adaptation
       Set laPlage = ActiveSheet.Range("$" + CStr(i) + ":$" + CStr(i))
       Set laPlage = ActiveSheet.Range("C" + CStr(i)).Offset(0, 0).Resize(1, WorksheetFunction.CountA(laPlage))   
       nbNoteI = WorksheetFunction.Count(laPlage)       
       If nbDevoirs < nbNoteI Then
        nbDevoirs = nbNoteI
        End If
    Next i
   '
   'lesCoef est la plage D2:2 si 4 devoirs
   'lesCoef = "OFFSET(D2,0,0,1," + CStr(nbDevoirs) + ")"
   Set lesCoef = ActiveSheet.Range("D2").Offset(0, 0).Resize(1, nbDevoirs)
'
   For i = 3 To nbEleves + 2   
       'lesNotes est par exemple la plage D7:G7 (eleve n° 5)
       'lesNotes = "OFFSET(D" + CStr(i) + ",0,0,1," + CStr(nbDevoirs) + ")"
       Set lesNotes = ActiveSheet.Range("D" + CStr(i)).Offset(0, 0).Resize(1, nbDevoirs)    
       'la moyenne
       leNum = "SUM(IF(ISNUMBER(" + lesCoef.Address + "),IF(ISNUMBER(" + lesNotes.Address + ")," + lesNotes.Address + "*" + lesCoef.Address + ",0)," + lesNotes.Address + "))"
       leDeno = "SUM(IF(ISNUMBER(" + lesNotes.Address + "),IF(ISNUMBER(" + lesCoef.Address + ")," + lesCoef.Address + ",1),0))"
       laFormule = "=" + leNum + "/" + leDeno
       Set laCelluleBcle = ActiveSheet.Cells(i, 1)
       laCelluleBcle.FormulaArray = laFormule    
    Next i
End Sub
Cliquez sur le bouton pour copier le code dans le presse papier
 function calculerMoyenne ()
   Dim FAS as Object
   Dim laFeuille as object, laCellule as object, laCelluleBcle as Object
   Dim leForm as Object
   Dim laZone as object
   Dim laPlage as object
   Dim info as object
   Dim laNote as double
   Dim nbEleves as integer, nbNoteI as integer, nbDevoirs as Integer
   dim i as integer
   Dim lesCoef as string, lesNotes as string, leNum as string, leDeno as string
   FAS = CreateUnoService("com.sun.star.sheet.FunctionAccess")
   laFeuille =thisComponent.currentController.activeSheet
   LaCellule = laFeuille.getCellRangeByName("A2")
   laCellule.string ="Moyenne pond."
   'la cellule A1 contient le nb de ligne contenant des noms d'élèves
   '=NBVAL($C:$C)- 2
   LaCellule = laFeuille.getCellRangeByName("A1")
   laZone = thisComponent.currentController.activeSheet.getCellRangeByName("$C:$C")
   nbEleves = FAS.callFunction("COUNTA", Array(laZone))
   nbDevoirs=0
   for i = 4 to nbEleves +1
       laFormule ="=COUNT(OFFSET($"+i+":$"+ i+";0;3;1;COUNTA($"+i+ ":$"+i+")))"
       laCellule.Formula = laFormule
       nbNoteI = laCellule.Value
       if nbDevoirs < nbNoteI then
           nbDevoirs = nbNoteI
       end if
   next i
   'lesCoef est la plage D3:G3 si 4 devoirs
   lesCoef = "OFFSET(D2;0;0;1;"+Cstr(nbDevoirs)+")"
   for i = 3 to nbEleves
       'lesNotes est par exemple la plage D7:G7 (eleve n° 5)
       lesNotes = "OFFSET(D"+ CStr(i) + ";0;0;1;"+CStr(nbDevoirs) +")"
       'la moyenne
       leNum ="SUM(IF(ISNUMBER("+lesCoef+");IF(ISNUMBER("+lesNotes+");"+lesNotes+"*"+lesCoef+";0);"+lesNotes+"))"
       leDeno = "SUM(IF(ISNUMBER("+lesNotes+");IF(ISNUMBER("+lesCoef+");"+lesCoef+";1);0))"
       laFormule = "=" + leNum + "/" + leDeno
       laCelluleBcle = laFeuille.getCellByPosition( 0 , i -1 )
       laCelluleBcle.ArrayFormula = laFormule
   next i
end function