# -*- coding: utf-8 -*- """ @author: lejocelyn @licence:GPL3 + CeCill 2.1 """ from __future__ import print_function import oursql import argparse import sys import graphviz as gv import functools import csv import re from collections import OrderedDict graph = functools.partial(gv.Graph, format='svg') digraph = functools.partial(gv.Digraph, format='svg') parser = argparse.ArgumentParser(description='Get the data from a CSV file to input into the graph.') parser.add_argument("--data", "-d", dest='data', type=str, default=False, help="Spécifie le fichier duquel les données sont extraites.") parser.add_argument("--fichier", "-f", dest='fichier', type=str, default="arbre_corpus", help="Spécifie le nom du fichier dans lequel est écrit le graphique.") parser.add_argument("--lettre", "-l", dest='lettre', type=str, default=False, help="Restreint aux formes contenant la ou les lettres spécifiées.") parser.add_argument("--syllabe", "-s", dest='syllabe', type=str, default=False, help="Faire des stats et arbre pour la syllabe") parser.add_argument("--contexte", "-c", dest='contexte', type=str, default=False, help="""Définit un contexte pour limiter la recherche. Le formalisme de définition du contexte est celui des expressions régulières.""") parser.add_argument("--couleurs", "-o", dest='couleurs', type=str, default=False, help="""Active ou non l'usage de couleurs pour marquer les lettres recherchées.""") #parser.add_argument("--mots", "-m", dest='mots', type=str, default=False, # help="""Si l'option est précisée, la requête est effectuée sur les unités d'intonations # et représente les morphèmes à la place des lettres.""") args = parser.parse_args() #### #### #### Fonctions récurrentes proposées par la doc de graphviz #### def add_nodes(graph, nodes): for n in nodes: if isinstance(n, tuple): graph.node(n[0], **n[1]) else: graph.node(n) return graph def add_edges(graph, edges): for e in edges: if isinstance(e[0], tuple): graph.edge(*e[0], **e[1]) else: graph.edge(*e) return graph def log_stats(info): """ append the stats information to the end of the stat file """ with open("stats_" + args.fichier + ".txt", "a") as myfile: print(info, file=myfile) myfile.write("\n") def find_lettre_in_list(letter, liste): """ a letter being defined by a form and a position, find if a same label and a same position can be already match in the list of node """ for element in liste: # si le caractère et la positon sont identiques if element[1]["label"] == letter[1]["label"] and element[1]["position"] == letter[1]["position"] and element[1]["id_lettre_precedente"] == letter[1]["id_lettre_precedente"]: return element[0] return False def get_lettre_in_list(id_lettre, liste_noeuds): """ retourne le nœud/lettre correspondant à l'id """ for lettre in liste_noeuds: if lettre[0] == str(id_lettre): return lettre def find_liens_in_list(liste_liens, id_A=False, id_B=False): """ chercher un lien dans la liste des liens. Si les deux ID, A et B, sont spécifiés, retourne le lien, Si seul un ID, A ou B, est spécifié, retourne la liste des liens satisfaisants l'ID demandé """ if id_A != False and id_B != False: for lien in liste_liens: # Lorsque l'on trouve le bon lien, on le renvoie if lien[0][0] == id_A and lien[0][1] == id_B: return lien return False elif id_A != False: liste_id_A = [] for lien in liste_liens: if lien[0][0] == id_A : liste_id_A.append(lien) return liste_id_A elif id_B != False: liste_id_B = [] for lien in liste_liens: if lien[0][1] == id_B: liste_id_B.append(lien) return liste_id_B def incremente_occurrence(id_lettre, liste_node, nom): ''' incremente d'une occurrence la lettre dans la liste et retourne la liste ''' # exemple id_lettre : '1', # exemple liste_node : [('1', {'style': 'filled', 'occurrence': '397', # 'label': u'v', 'fillcolor': 'grey', 'position': '0', 'id_lettre_precedente': ' '}), # ('2', {'style': 'filled', 'occurrence': '397', # 'label': u'a', 'fillcolor': 'grey', 'position': '1', # 'id_lettre_precedente': '1'}), # ('3', {'style': 'filled', 'occurrence': '397', # 'label': u'n', 'fillcolor': 'grey', 'position': '2', 'id_lettre_precedente': '2'})], # exemple nom : (u'va', 194L)) i_list = 0 for element in liste_node: if element[0] == str(id_lettre): tmp_occurrence = int(liste_node[i_list][1]["occurrence"]) tmp_occurrence += nom[1] liste_node[i_list][1]["occurrence"] = str(tmp_occurrence) i_list += 1 return liste_node def incremente_lien(id_A, id_B, liste_liens, nom): """ Incrémente d'une occurrence du lien """ i_list = 0 for element in liste_liens: if element[0][0] == str(id_A) and element[0][1] == str(id_B): tmp_occurrence = int(liste_liens[i_list][1]["label"]) tmp_occurrence += nom[1] liste_liens[i_list][1]["label"] = str(tmp_occurrence) i_list += 1 #print(liste_liens) return liste_liens def ordonner_node(liste_noeuds): """ ordonne la liste des nœuds par ordre décroissant """ liste_ordonnee = [] #print(len(liste)) index = 0 for element in liste_noeuds: i = 0 if len(liste_ordonnee) == 0: liste_ordonnee.append(element) else: while True: if len(liste_ordonnee) - i == 0: liste_ordonnee.insert(i, element) break elif int(element[1]["occurrence"]) <= int(liste_ordonnee[i][1]["occurrence"]): i += 1 else: liste_ordonnee.insert(i, element) break index += 1 return liste_ordonnee def get_total(liste_nodes): """ Retourne le nombre total d'occurrences de la requête. Ce nombre correspond à la somme des occurrences des lettres en première position """ total = 0 for element in liste_nodes: if int(element[1]["position"]) == 0: total += int(element[1]["occurrence"]) return total def couleur_noeud(label): """ Définit la couleur d'un noeud dans le schema SVG """ ## Colorisation du schéma SVG ## couleur_lettre = "white" if args.couleurs: # if label in args.contexte: # couleur_lettre = "green" if isinstance(args.lettre,str): if label in args.lettre: couleur_lettre = "red" return couleur_lettre def get_total_position(liste_nodes): """ retourne une liste de totaux suivant la position de la lettre À quoi ça sert ? """ liste_total = [] for element in liste_nodes: if len(liste_total) <= int(element[1]["position"]): liste_total.append(int(element[1]["occurrence"])) else: liste_total[int(element[1]["position"])] += int(element[1]["occurrence"]) return liste_total def compte_total_occurrence_lettres(liste_nodes): """ retourne un dictionnaire du nombre total d'occurrences de chacune des lettres et en fonction de leur position """ dico_compte_occurrence_lettres = {} # Le dictionnaire aura la forme suivante : # label : total, "position" : nb_occurrence # {"s" : 13} # pour rappel, un noeud à la forme suivante : # str(id_lettre), {"label": label , "position": str(position_node), # "id_lettre_precedente": " ", "occurrence":str(nom[1]), "fillcolor":couleur_noeud(label), "style":"filled"}) for noeud in liste_nodes: lettre = noeud[1]["label"] label_position = lettre + "_position_" + noeud[1]["position"] if lettre in dico_compte_occurrence_lettres.keys(): dico_compte_occurrence_lettres[lettre] += int(noeud[1]["occurrence"]) else : dico_compte_occurrence_lettres[lettre] = 0 dico_compte_occurrence_lettres[lettre] += int(noeud[1]["occurrence"]) if label_position in dico_compte_occurrence_lettres.keys(): dico_compte_occurrence_lettres[label_position] += int(noeud[1]["occurrence"]) else : dico_compte_occurrence_lettres[label_position] = 0 dico_compte_occurrence_lettres[label_position] += int(noeud[1]["occurrence"]) dico_compte_occurrence_lettres = OrderedDict(sorted(dico_compte_occurrence_lettres.items(), key=lambda t: t[0])) return dico_compte_occurrence_lettres def comptage_formes(listes_liens, listes_nodes): """ Calcul du nombre de formes différentes dans l'arbre résultant de l'analyse du corpus. """ ## Une lettre égale (str(id unique), dico{"label": "a", "position":"1", id_lettre_precedente = 23, "occurrence":str(nom[1])}) ## lien = ((id_lettre_A, id_lettre_B), {"label": str(nom[1])}) nb_formes = 0 # Afin de déterminer le nombre de formes, le principe est de comparer le nombre # d'occurrences. Si le nombre d'occurrences d'une lettre est différent du # nombre d'occurrences de la lettre précédentes, cela signifie qu'il y a # une forme langagière. # Une forme est : # Si l'on prend la liste des morphèmes, cela correspond à un morphème # Si l'on prend la liste des syllables, cela correpond à une syllabe, # si l'on prend la liste des squellettes, cela correspond à un squelette # Initialisation des variables de comparaison à false occurrence_lettre_A = False occurrence_lettre_B = False # Calcul du nombre de branches, puis on ajoutera les moments où # la somme des occurrences des lettres "branches" n'est pas égale au nombre d'occurrences # du nœud précédent nb_branches = 0 nb_arbres = 0 liste_A_deja_compte = {} # la clef, un str, indique le nombre de lettres du morphème, et la valeur son nombre d'occurrences dico_forme_nb_lettres = {} # il faut compter les lettres isolées : # si l'id de la lettre n'est pas dans la liste des liens # alors l'ajouter comme une lettre isolée liste_id_lettre = [] liste_id_liens = [] for lettre in listes_nodes: liste_id_lettre.append(int(lettre[0])) for element in listes_liens: liste_id_liens.append(int(element[0][0])) liste_id_liens.append(int(element[0][1])) for id_lettre in liste_id_lettre: lettre_temp = get_lettre_in_list(id_lettre,listes_nodes) dico_forme_nb_lettres[lettre_temp[1]["position"]] = 0 if id_lettre not in liste_id_liens: nb_formes += 1 # la lettre ne peut pas avoir déjà été ajouté, vu qu'elle n'est pas dans la liste dico_forme_nb_lettres[lettre_temp[1]["position"]] += 1 copie_listes_liens = listes_liens # pas clair la raison de ce hack, une question de globalspace for lien in listes_liens: lettre_A_id = lien[0][0] lettre_B_id = lien[0][1] lettre_A = get_lettre_in_list(lettre_A_id,listes_nodes) lettre_B = get_lettre_in_list(lettre_B_id,listes_nodes) # initialisation du comptage de nombre de formes par position : if dico_forme_nb_lettres.get(lettre_A[1]["position"]) == None : dico_forme_nb_lettres[lettre_A[1]["position"]] = 0 if dico_forme_nb_lettres.get(lettre_B[1]["position"]) == None : dico_forme_nb_lettres[lettre_B[1]["position"]] = 0 ## Si la lettre est la première de la forme, alors il s'agit d'une nouvelle ## forme if int(lettre_A[1]["position"]) == 0: if lettre_A_id not in liste_A_deja_compte: # pas la peine de les compter car cela ferait doublon avec le compte des branches terminales #nb_formes += 1 nb_arbres += 1 #print("nouvel arbre") occurrence_lettre_A = int(lettre_A[1]["occurrence"]) occurrence_lettre_B = int(lettre_B[1]["occurrence"]) liste_lien_lettre_A = find_liens_in_list(copie_listes_liens, id_A=lettre_A_id) liste_lien_lettre_B = find_liens_in_list(copie_listes_liens, id_A=lettre_B_id) somme_occurrence_lien = 0 for lien in liste_lien_lettre_A: somme_occurrence_lien += int(lien[1]["label"]) if somme_occurrence_lien != occurrence_lettre_A: if lettre_A_id in liste_A_deja_compte: pass else: # Les branches intermédiaires sont en fait des formes # existantes mais qui possèdent également des sous-branches nb_formes += 1 dico_forme_nb_lettres[lettre_A[1]["position"]] += 1 #print("branche intermédiaire") if len(liste_lien_lettre_B) == 0: nb_formes += 1 dico_forme_nb_lettres[lettre_B[1]["position"]] += 1 #print("fin de la branche") # On conserve la somme des occurrences des branches pour les prochaines itérations liste_A_deja_compte[lettre_A_id] = somme_occurrence_lien log_stats("Nombre total d'arbres:\n") log_stats(nb_arbres) log_stats("Nombre de formes existantes en fonction du nombre de lettres :\n") tri_dico_forme_nb_lettres = OrderedDict(sorted(dico_forme_nb_lettres.items())) log_stats(tri_dico_forme_nb_lettres) return nb_formes def navigation_arbre_lettre(liste_des_noms): """à définir """ # dans ce dico, on stocke les noms correspondants à la requete dico_correspond = {} noms_total = 0 for element in liste_formes: noms_total += int(element[1]) liste_nodes = [] liste_liens = [] # Une lettre égale (str(id unique), dico{"label": "a", "position":"1", id_lettre_precedente = 23, "occurrence":str(nom[1])}) i_lien = 0 i_node = 0 #id_lettre est un identifiant unique pour chaque lettre id_lettre = 0 # nombre de morphèmes existants dans le corpus nb_formes = 0 liste_lettres_premiere_position = [] # nom est un tuple : (forme, nb_occurrences) for nom in liste_formes: # Si l'utilisateur a spécifié qu'il souhaité concentré la recherche uniquement # sur les mots contenant une ou plusieurs lettres, les autres mots sont enlevé # de la recherche. if args.lettre != False: for lettre in args.lettre: if lettre not in nom[0]: cont = True else : cont = False if args.lettre != True: cont = False if cont == True: continue # Si l'utilisateur définit un contexte, il faut voir si le nom # contient le contexte rechercher par l'utilisateur, sinon, on passe if args.contexte != False: match = re.search(args.contexte, nom[0]) if match is None: continue # Si l'utilisateur spécifie qu'il étudie la structure syllabique, les lettres # sont alors remplacées par leur symbole correspondant S = Demivoyelle; V = voyelle, C = consonne if args.syllabe != False: nouveau_nom = u"" i_index_lettre = 0 for lettre in nom[0]: if lettre in consonnes: nouveau_nom += "C" elif lettre in voyelles: # Gestion des semiconsonnes if len(nom[0]) > 1 and len(nom[0]) == i_index_lettre + 1 and nom[0][i_index_lettre -1] in voyelles: if lettre == "i" or lettre == "o": nouveau_nom += "S" else: nouveau_nom += "V" else: nouveau_nom += "V" else: nouveau_nom += lettre i_index_lettre += 1 nom = (nouveau_nom, nom[1]) # dictionnaire des noms correspondant dico_correspond[nom[0]] = nom[1] # Le premier cas de la liste est particulier, étant donné qu'il ne peut être comparé # avec la lettre précédente.. position_node = 0 label = nom[0][0] lettre_A = (str(id_lettre), {"label": label , "position": str(position_node), "id_lettre_precedente": " ", "occurrence":str(nom[1]), "fillcolor":couleur_noeud(label), "style":"filled"}) # si la lettre est en première position du mot if position_node == 0: liste_lettres_premiere_position.append(lettre_A[1]["label"]) liste_lettres_premiere_position = list(dict.fromkeys(liste_lettres_premiere_position)) # qu'est qu'on vérifie déjà ? if len(nom[0]) == 1: id_lettre_A = find_lettre_in_list(lettre_A, liste_nodes) if id_lettre_A: # déjà dans la liste des lettres existantes liste_nodes = incremente_occurrence(id_lettre_A, liste_nodes, nom) lettre_A = get_lettre_in_list(id_lettre_A, liste_nodes) else: # ajout de la lettre A dans la liste des nodes id_lettre += 1 lettre_A = (str(id_lettre), {"label": label , "position": str(position_node), "id_lettre_precedente": " ", "occurrence":str(nom[1]), "fillcolor":couleur_noeud(label), "style":"filled"}) liste_nodes.append(lettre_A) continue # On commence par le deuxième élément de la liste, le premier ayant déjà été étudié for lettre in nom[0][1:]: # la lettre de référence, lettre A, est la première lettre du mot id_lettre_A = find_lettre_in_list(lettre_A, liste_nodes) if id_lettre_A: # déjà dans la liste des lettres existantes if position_node == 0: liste_nodes = incremente_occurrence(id_lettre_A, liste_nodes, nom) lettre_A = get_lettre_in_list(id_lettre_A, liste_nodes) else: # ajout de la lettre A dans la liste des nodes id_lettre += 1 lettre_A = (str(id_lettre), {"label": label , "position": str(position_node), "id_lettre_precedente": " ", "occurrence":str(nom[1]), "fillcolor":couleur_noeud(label), "style":"filled"}) liste_nodes.append(lettre_A) id_lettre_A = lettre_A[0] # La première lettre étant A, on incrémente pour la lettre B position_node += 1 # la lettre B est la lettre suivante du mot label_b = nom[0][position_node] lettre_B = (str(id_lettre), {"label":label_b, "position":str(position_node), "id_lettre_precedente":lettre_A[0], "occurrence":str(nom[1]), "fillcolor":couleur_noeud(label_b), "style":"filled"}) id_lettre_B = find_lettre_in_list(lettre_B, liste_nodes) if(id_lettre_B): # déjà dans la liste des lettres existantes lettre_B = get_lettre_in_list(id_lettre_B, liste_nodes) liste_nodes = incremente_occurrence(id_lettre_B, liste_nodes, nom) #print("find B") #raw_input() if lettre_A[0] == lettre_B[1]["id_lettre_precedente"]: liste_liens = incremente_lien(id_lettre_A, id_lettre_B, liste_liens, nom) else: # nouveau lien liste_liens.append(((id_lettre_A, id_lettre_B), {"label": str(nom[1])})) else: # ajout de la lettre B dans la liste des nodes id_lettre += 1 lettre_B = (str(id_lettre), {"label":nom[0][position_node], "position":str(position_node), "id_lettre_precedente":lettre_A[0], "occurrence":str(nom[1]), "fillcolor":couleur_noeud(label_b), "style":"filled"}) liste_nodes.append(lettre_B) liste_liens.append(((id_lettre_A, lettre_B[0]), {"label": str(nom[1])})) # Les occurrences ayant déjà été comptabilisé une fois, il ne faut pas les ajouter à nouveau. lettre_A = lettre_B # la lettre de référence devient la lettre B, la lettre suivante #raw_input() log_stats("Liste des lettres en première position de mot :") log_stats(liste_lettres_premiere_position) total = get_total(liste_nodes) log_stats("Nombre total d'occurrences des nœuds:") log_stats(total) position_totale = get_total_position(liste_nodes) log_stats("Nombre total d'occurrences des nœuds par position:") log_stats(position_totale) occurrences_lettres = compte_total_occurrence_lettres(liste_nodes) log_stats("compte lettres noeuds :") log_stats(occurrences_lettres) log_stats("Liste des mots, ordonnée par taille :\n") dico_tri = OrderedDict(sorted(dico_correspond.items(), key=lambda x:len(x[0]))) log_stats(dico_tri) log_stats("Nombre de formes:\n") nb_formes_total = 0 for element in dico_tri.items() : nb_formes_total += element[1] log_stats(str(nb_formes_total)) #print(len(liste_nodes)) liste_nodes = ordonner_node(liste_nodes) formes = comptage_formes(liste_liens, liste_nodes) log_stats("Nombre totales de formes existantes :\n") log_stats(formes) #print(liste_liens) add_edges( add_nodes(digraph(), liste_nodes), liste_liens ).render("img/" + args.fichier) def navigation_arbre_mots(liste_des_enonces): return "hé hé, pas encore" ##### extrait les noms du fichiers CSV pour en faire une liste [] log_stats("args.fichier") log_stats(args.fichier) liste_formes = [] if args.data: with open(args.data, 'rb') as csvfile: spamreader = csv.reader(csvfile, delimiter=',', quotechar='|') for row in spamreader: liste_formes.append((unicode(row[0]), int(row[1]))) else : db = oursql.connect(host="localhost", # your host, usually localhost user="lejocelyn", # your username passwd='Lejocelyn1', # your password db="lexique_nisvai") # name of the data base cursor = db.cursor() if args.mots: cursor.execute("""SELECT nisvais FROM Enonces;""" ) else: ### COUNT permet d'ordonner les résultats de la recherche ### par ordre décroissant de gauche à droite de la forêt ### d'arbres cursor.execute("""SELECT Termes.termes, COUNT(Termes.id_termes) FROM Termes_has_Enonces INNER JOIN Termes ON Termes.id_termes = Termes_has_Enonces.id_termes WHERE Termes.id_categories=1 GROUP BY Termes.id_termes ORDER BY COUNT(Termes.id_termes) DESC;""" ) liste_formes = cursor.fetchall() #### Les lettres du nisvais consonnes = ["b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w"] voyelles = ["a", "e", "u", "i", "o", "y"] #### Possibilité de définir d'autres classes #### if args.mots: navigation_arbre_mots(liste_formes) else : navigation_arbre_lettre(liste_formes) ########### MOYENNE DURÉE GROUPE DE SOUFFLE # select Textes.id_textes, SUM(t2-t1)/COUNT(Textes.id_textes) AS moyenne From Enonces INNER JOIN Textes on Enonces.id_textes=Textes.id_textes WHERE Textes.id_corpus="T9" OR Textes.id_corpus="T8";