<jupyter_start><jupyter_text>Tutoriel "Calculate_SRF.py"Auteurs: [<NAME>](mailto:<EMAIL>), [<NAME>](mailto:<EMAIL>)[INSA Lyon](https://www.insa-lyon.fr), France, 08/10/2021 IntroductionDans le but de pondérer les critères par la méthode SRF (Simos-Roy-Figueira), le code Python [**Calculate_SRF.py**](Calculate_SRF.py) présenté ci-dessous permet d'automatiser les calculs à partir d'un fichier Excel, où l'utilisateur a préalablement classer ses critères.   Présentation de la méthode SRFLa méthode Simos-Roy-Figueira (SRF) est une amélioration de la méthode de Simos, dites la "méthode des cartes".La méthode des cartes permet de pondérer les critères. Le décideur choisit la position relative des cartes, représentant les critères, en les classant sous forme pyramidale. Il peut positionner des cartes blanches pour indiquer une importance supplémentaire entre deux rangs. Visuelle et concrète, cette technique est souvent utilisée comme moyen de communication et de pondération des critères. Un traitement mathématique des rangs permet la détermination du poids de chaque critère. Cependant, cette méthode a des limites, invisibles pour le décideur. Tout d'abord, la pondération n'est pas forcément normalisée (la somme des poids doit valoir 1). Ensuite, la position visuelle des critères est modifiée par le traitement mathématique. Deux cartes sur le dernier rang (numéro 1) ont une position évaluée à 1.5. Cette déformation mathématique réduit fortement la liste des poids pouvant être obtenus. Enfin, l'importance relative entre le premier et le dernier critère est imposée, ce qui induit des fortes disparités dans la pondération lorsque le nombre de critères est élevé. La méthode SRF améliore le traitement mathématique des critères, avec l'ajout d'un ratio Z représentant l'importance relative mentionnée précédemment.   Présentation du code Les différentes étapes du code de la méthode SRF sont présentées ici.  Les modules panda et openpyxl seront utilisés pour extraire les données de la feuille de calcul.<jupyter_code>import pandas as pd<jupyter_output><empty_output><jupyter_text>1. Première étape : Définition des fonctions 1.1 Récupération du fichier La première étape est de récupérer les données d'un tableau Excel donné. Ce dernier se décompose en cinq feuilles :     1. Critères : présentation des critères, de leurs indices, leurs descriptions, leurs intérêts et les moyens d'évaluation    2. Pondération Critères Systèmes : l'utilisateur classe les critères du système et donne un ratio Z     3. Pondération Critères Enveloppe : l'utilisateur classe les critères de l'enveloppe et donne un ratio Z    4. Calculs : les étapes de calcul, ici automatisés par un code python, sont réalisées.     5. Résultats : les résultats sont présentés.         Le ratio Z correspond à l'importance relative entre le premier et le dernier critère. Dans la fonction suivante, 'recuperate_file', le nom 'name' du fichier Excel est indiqué, ainsi que le numéro de la feuille à extraire. Les données sont ensuite renvoyées sous forme de liste pour faciliter le traitement des données.<jupyter_code>def recuperate_file(name, index_sheet):
    """Récupère une feuille du fichier Excel pour la transformer en objet liste """
    feuille_recup = pd.read_excel(name, sheet_name=index_sheet)
    return feuille_recup.values.tolist()<jupyter_output><empty_output><jupyter_text>1.2 Définition des dictionnaires La correspondance entre le nom des critères et leur indice est réalisé par les fonctions dico_. Une fonction est écrite pour chaque thème. Attention ! Les indices utilisés ne sont valables uniquement pour le fichier Excel "Pondération Critères.xlsm"Cette limite à la méthode de Simos pourrait être améliorée par l'encodage du système de choix, réalisé pour l'instant sur Excel.<jupyter_code>def fdico_economie(feuille):
    """ Définition du dictionnaire Economie contenant la correspondance entre l'index et son critère """
    dico_economie = {}
    for x in range(7,15):
        dico_economie[(feuille[x][2])] = (feuille[x][1])
    return dico_economie

def fdico_technique(feuille):
    """ Définition du dictionnaire Technique contenant la correspondance entre l'index et son critère """
    dico_technique = {}
    for x in range(18, 22):
        dico_technique[(feuille[x][2])] = (feuille[x][1])
    return dico_technique

def fdico_social(feuille):
    """ Définition du dictionnaire Social contenant la correspondance entre l'index et son critère """
    dico_social = {}
    for x in range(25,30):
        dico_social[(feuille[x][2])] = (feuille[x][1])
    return dico_social

def fdico_environnement(feuille):
    """ Définition du dictionnaire Environnement contenant la correspondance entre l'index et son critère """
    dico_environnement = {}
    for x in range(33,40):
        dico_environnement[(feuille[x][2])] = (feuille[x][1])
    return dico_environnement<jupyter_output><empty_output><jupyter_text>1.3 Récupération des choix de l'utilisateur Les choix de l'utilisateur sont récupérés sous forme de liste. Pour cela, la fonction tableau_choix reçoit la 'feuille' où se situent les données à récupérer, et les indices des lignes du tableau de choix.<jupyter_code>def tableau_choix(feuille,dico,index):
    """Récupération des choix de l'utilisateur sous forme d'une liste. Retourne également le ratio Z"""
    tableau_choix_environnement = [[0 for j in range(4)] for i in range(15)]
    a = index[0]
    b = index[1]
    for x in range(a,b):
        tableau_choix_environnement[x - a][0]= x - a+1
        for y in range(2,5):
            tableau_choix_environnement[x - a][y - 1]=str(feuille[x][y])
            if (dico.get(tableau_choix_environnement[x - a][y - 1], 1) != 1) :
                tableau_choix_environnement[x - a][y - 1] = dico.get(tableau_choix_environnement[x - a][y - 1])
    return tableau_choix_environnement, feuille[index[1]+1][2]<jupyter_output><empty_output><jupyter_text>1.4 Définition des fonctions de calcul Les lignes ci-dessous présentent les fonctions de calcul utilisées pour déterminer les paramètres de la méthode SRF. Premièrement, nous recevons une matrice contenant les critères rangés par l'utilisateur. Une premire fonction permet de supprimer les lignes ne servant pas au classement des critères.<jupyter_code>def traitement(l):
   """Permet de retirer les cartes blanches inutiles"""
   while l[-1][1] == 'nan':
       del l[-1]
   return l<jupyter_output><empty_output><jupyter_text>Une seconde méthode permet d'affecter la numérotation des rangs, à l'inverse du classement. Le dernier classement se voit donc affecter le numéro 1.<jupyter_code>def calcul_rang(l):
    listnouveaurang = [None]*len(l)
    for i in range(len(l)):
        listnouveaurang[i] = l[len(l)-i-1]
        listnouveaurang[i][0]=i+1
    return listnouveaurang<jupyter_output><empty_output><jupyter_text>La méthode calcul_n permet de calculer le nombre de rang dans le classement donné 'l'. A chaque ligne non vide, le paramètre n est incrémenté.<jupyter_code>def calcul_n(l):
    n = 0
    for i in range(len(l)):
        for j in range(1, len(l[i])):
            if l[i][j] != 'nan':
                n +=1
    return n<jupyter_output><empty_output><jupyter_text>Cette fonction 'calcul_er_prime' permet de déterminer le nombre de cartes blanches entre deux rangs successifs. Si la ligne est vide (remplie de 'nan'), la matrice 'l' ajoute un 1. Sinon, on ajoute 0 carte blanche. Le n correspond au nombre de cartes blanches au rang précédent.<jupyter_code>def calcul_er_prime(l,n):
    for i in range(len(l)-1):
        if l[i+1][1] == 'nan':
            l[i].append(1)
        else:
            l[i].append(0)
    if n == 0:
        l[len(l)-1].append(1)
    else:
        l[len(l)-1].append(0)

    for i in range(1,len(l)):
        if l[len(l)-i-1][-1] != 0:
            l[len(l) - i - 1][-1] = l[len(l)-i-1][-1] + l[len(l)-i][-1]
    for i in range(len(l)):
        if l[i][1] == 'nan' and l[i][2] == 'nan' and l[i][3] == 'nan':
            l[i][-1] = 0
    return l<jupyter_output><empty_output><jupyter_text>La méthode calcul_er permet de calculer le paramètre 'er' à partir de la liste des er' calculée précédemment.<jupyter_code>def calcul_er(l):
    e = 0
    for i in range(len(l)):
        l[i].append(l[i][-1]+1)
        e += l[i][-1]
    return l,e<jupyter_output><empty_output><jupyter_text>La méthode calcul_u permet de calculer le paramètre 'u' à partir de la liste des er calculée précédemment.<jupyter_code>def calcul_u(z,e):
    u = (z-1)/e
    return u<jupyter_output><empty_output><jupyter_text>La méthode calcul_k_i_prime permet de calculer le paramètre ki'.<jupyter_code>def calcul_k_i_prime(u,l):
    somme_er = 0
    for i in range(len(l)):
        if l[i][1] != 'nan':
            l[i].append(1+u*somme_er)
            somme_er += l[i][-2]
        else:
            l[i].append('nan')
            somme_er += l[i][-2]
    return l<jupyter_output><empty_output><jupyter_text>Enfin, la méthode traitement2 permet de créer un tableau associant le critère et le ki' correspondant.<jupyter_code>def traitement2(l):
    """Permet d'obtenir un tableau avec le critère et son ki' associé"""
    liste_traitee = []
    for i in range(len(l)):
        for j in range(1 , 4):
            if l[i][j] != 'nan':
                liste_traitee.append([l[i][j],l[i][-1]])
    return liste_traitee<jupyter_output><empty_output><jupyter_text>Une fois le tableau [critère, ki'] obtenu, la somme des ki' est calculée.<jupyter_code>def calcul_K_prime(l):
  K_prime = 0
  for i in range(len(l)):
      K_prime += l[i][1]
  return K_prime<jupyter_output><empty_output><jupyter_text>Le ki*, correspondant à la pondération finale non arrondie, est calculé.<jupyter_code>def calcul_ki_etoile(l,K_prime):
  for i in range(len(l)):
      l[i].append((100/K_prime)*l[i][1])
  return l<jupyter_output><empty_output><jupyter_text>Un traitement mathématique est effectué pour normaliser les ki* obtenus. Pour cela, un ki" est calculé en tronquant le ki*.<jupyter_code>def calcul_ki_2prime(l):
  for i in range(len(l)):
      l[i].append(int(l[i][2]*100)/100)
  return l<jupyter_output><empty_output><jupyter_text>La somme des ki" est calculée.<jupyter_code>def calcul_K_2prime(l):
  K_2prime = 0
  for i in range(len(l)):
      K_2prime += l[i][3]
  return K_2prime<jupyter_output><empty_output><jupyter_text>La pondération finale est ensuite calculée. Pour cela, des opérateurs [di, M, L, F] sont utilisés. Un paramètre w est défini pour choisir le nombre de décimales. Enfin, les matrices F+ et F- indiquent si le poids doit être arrondi au supérieur ou à l'inférieur.<jupyter_code>def calcul_ki(l,K_2prime,n):
  w = 2
  for i in range(len(l)):
      l[i].append((10 ** (-w) - (l[i][2] - l[i][3])) / l[i][2])  # Calcul di
      l[i].append((l[i][2] - l[i][3]) / l[i][2])  # Calcul di_barre
      if l[i][4] > l[i][5]: #Calcul M
          l[i].append(1)
      else:
          l[i].append(0)
      if l[i][6] == 1: #Calcul di_M
          l[i].append(l[i][4])
      else:
          l[i].append('-')

  v = int(10 ** (w) * (100 - K_2prime))  # Calcul v
  m = 0  # Calcul m
  for i in range(len(l)):
      m += l[i][6]

  L = [] # Calcul L
  for i in range(len(l)):
      if l[i][7] != '-':
          L.append(l[i][7])
  L.sort()
  if L == []:
      L = [0] * n

  vmn = v + m - n
  if m+v > n:
      for i in range(vmn,len(L)):
          L[i] = 0
  else:
      L = [0]*n

  #Calcul flèche L
  for i in range(len(L)):
      compteur_di = 0
      if L[i] == l[0][4]:
          compteur_di +=1
  if compteur_di != 0:
          l[0].append(1)
  else:
          l[0].append(0)
  vmn =  v + m -n
  for i in range(1,len(l)):
      compteur_di = 0
      for j in range(len(L)):
          if L[j] == l[i][4]:
              compteur_di +=1
      if compteur_di != 0:
          s=0
          j = 0
          while j != i and i < len(L):
              s+=L[j]
              j +=1
          if s>vmn :
              l[i].append(0)
          else:
              l[i].append(1)

      else:
          l[i].append(0)


  for i in range(len(l)): #Calcul dibarre_différent_M
      if l[i][6] == 0:
          l[i].append(l[i][5])
      else:
          l[i].append('-')

  Lbarre = []  # Calcul Lbarre
  nvm = n - v -m
  for i in range(len(l)):
      if l[i][9] != '-':
          Lbarre.append(l[i][9])
  Lbarre.sort()
  if Lbarre == []:
      Lbarre = [0] * n

  if m + v < n:
      for i in range(nvm, len(Lbarre)):
          Lbarre[i] = 0
  else:
      Lbarre = [0] * n


  #Calcul flèche Lbarre

  for i in range(len(Lbarre)):
      compteur_dibarre = 0
      if Lbarre[i] == l[0][5]:
          compteur_di += 1
  if compteur_di != 0:
      l[0].append(1)
  else:
      l[0].append(0)

  for i in range(1, len(l)):
      compteur_dibarre = 0
      for j in range(len(Lbarre)):
          if Lbarre[j] == l[i][5]:
              compteur_dibarre += 1

      if compteur_dibarre != 0:
          s = 0
          j = 0
          while j != i and i < len(Lbarre):
              s += Lbarre[j]
              j +=1
          if s > nvm:
              l[i].append(0)
          else:
              l[i].append(1)
      else:
          l[i].append(0)
  #Calcul F+ / F-
  for i in range(len(l)):  # F+
      if m + v < n:
          conditionF = n - v - m
          if l[i][6] == 0:
              l[i].append(1)
          else:
              l[i].append(0)
      else:
          conditionF = n - v
  fbis = [0] * len(l)
  verif = 0
  j = n-1
  while verif < conditionF and j!=-1:
      if l[j][10]==1 and l[j][6]==0:
          fbis[j] = 0
          if verif <= nvm:
              l[j][11] = fbis[j]
              verif = verif +1
      j = j-1

  # Calcul F- puis ki
  for i in range(len(l)):
      if l[i][11] == 0: #F-
          l[i].append(1)
      else:
          l[i].append(0)
      if l[i][12] == 1: #ki
          l[i].append(int(l[i][2]*(10**w))/10**w)
      else:
          l[i].append(int(l[i][2]*(10**w)+1)/(10**w))
  veri = 0
  for i in range(len(l)):
      veri += l[i][13]
  return l,veri<jupyter_output><empty_output><jupyter_text>2. Deuxième étape : Méthode de Simos-Roy-Figueira 2.1 Récupération des donnéesLa première étape est de récupérer les choix de l'utilisateur à partir du tableau excel 'Pondération Critères. xlsm'. Pour cela, les fonctions de récupération de données (présentées parties 1.1 et 1.2) sont utilisées. Les feuilles 1, 2 et 3 sont récupérées. Un message d'avertissement apparaît pour indiquer que le module Openpyxl ne supporte pas les données 'Data Validation' dans Excel. Nos données ne sont pas concernées.<jupyter_code>feuille1 = recuperate_file('Pondération critères.xlsm', 0)
feuille2 = recuperate_file('Pondération critères.xlsm', 1)
feuille3 = recuperate_file('Pondération critères.xlsm', 2)<jupyter_output>C:\Users\flori\anaconda3\lib\site-packages\openpyxl\worksheet\_reader.py:312: UserWarning: Data Validation extension is not supported and will be removed
  warn(msg)<jupyter_text>Les dictionnaires associant critères et indices sont créés.<jupyter_code>dico_economie = fdico_economie(feuille1)
dico_environnement = fdico_environnement(feuille1)
dico_social = fdico_social(feuille1)
dico_technique = fdico_technique(feuille1)<jupyter_output><empty_output><jupyter_text>Les index permettant d'identifier la plage de données dans la feuille de calcul sont définis<jupyter_code>index_environnement = [92,107]
index_economie = [21,36]
index_social = [69,84]
index_technique = [45,60]<jupyter_output><empty_output><jupyter_text>Les tableaux contenant le classement des critères sont créés.<jupyter_code>tableau_choix_environnement, z_environnement = tableau_choix(feuille2,dico_environnement,index_environnement)
tableau_choix_economie, z_economie = tableau_choix(feuille2,dico_economie,index_economie)
tableau_choix_technique, z_technique = tableau_choix(feuille2,dico_technique,index_technique)
tableau_choix_social, z_social = tableau_choix(feuille2,dico_social,index_social)<jupyter_output><empty_output><jupyter_text>2.2 Traitement des donnéesLa deuxième étape permet de traiter les données pour déterminer la pondération des critères avec les fonctions définies partie 1.3<jupyter_code>tableau = traitement(tableau_choix_economie)

tableau = calcul_rang(tableau)

n = calcul_n(tableau)

tableau = calcul_er_prime(tableau,n)

tableau,e = calcul_er(tableau)

u = calcul_u(z_economie,e)


tableau = calcul_k_i_prime(u,tableau)

tableau = traitement2(tableau)


K_prime = calcul_K_prime(tableau)

tableau = calcul_ki_etoile(tableau,K_prime)

tableau = calcul_ki_2prime(tableau)

K_2prime = calcul_K_2prime(tableau)

tableau,total = calcul_ki(tableau,K_2prime,n)<jupyter_output><empty_output><jupyter_text>Finalement, le critère associé à sa pondération est affiché.<jupyter_code>for i in range(0,len(tableau)):
    print(str(tableau[i][0])+" "+str(tableau[i][13]))
print("Somme des poids = "+str(total))<jupyter_output>g1.4 2.37
g1.3 9.21
g1.2 10.18
g1.5 11.16
g1.8 14.09
g1.7 17.01
g1.1 17.99
g1.6 17.99
Somme des poids = 100.0
{'fraction_non_alphanumeric': 0.11396430885211144, 'fraction_numerical': 0.02149714353024324, 'mean_word_length': 3.5003975616220515, 'pattern_counts': {'":': 0, '<': 101, '<?xml version=': 0, '>': 99, 'https://': 1, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '23', 'dir': 'jupyter-structured-clean-dedup', 'id': '490059', 'license': 'Apache-2.0', 'n_tokens_mistral': 6431, 'n_tokens_neox': 6372, 'n_words': 1742, 'path': 'Tutoriel Calculate_SRF .ipynb', 'repo_name': 'FlorianM1907/PIRD_Decision_Making', 'size': '17037'}