Boîte à outil 3 : extraction de patrons (II)

1. Préparation d'environnement


In [ ]:
filename = input('Saisissez un fichier conll:\n> ')
patterns = input("Saisissez les motifs (séparés par une virgule): ")

# on récupère les patrons saisis et on initialise le dictionnaire
patterns_list = patterns.split(',')
pattern_dico = {}

# on crée une entrée pour chaque patron
for pattern in patterns_list:
    pattern_dico[pattern] = []

Nous demandons à l'utilisateur le nom du fichier à traiter et les patrons à extraire. Nous créons un dictionnaire pattern_dico avec chaque patron comme clé et une liste vide comme valeur pour stocker le résultat. La liste patterns_list est également conservée pour la recherche de patrons.

Variables générées : pattern_dico (dictionnaire patron-résultat), patterns_list (liste des patrons saisis)

2. Extraction de patrons


In [ ]:
# on lit le fichier et on sépare les phrases
with open(filename, 'r', encoding = 'utf-8', newline = '\n') as fichier:
    contenu = fichier.read()
    
phrase_liste = contenu.split('\n\n')
phrase_liste.pop()

Nous lisons le contenu du fichier et nous le séparons en phrase. Dans un fichier conll, les phrases sont séparées avec une suite de deux sauts de ligne. Par contre il y a aussi un double sauts de ligne à la fin du fichier, il faut donc l'enlever pour pas qu'il gêne les traitements suivants. A l'issu de cette étape, nous avons une liste des phrases.

Variables générées : phrase_liste (liste de phrases)

In [ ]:
# on traite toujours phrase par phrase
for phrase in phrase_liste:
    lignes = phrase.split('\n')
    # on filtre les commentaires
    mots = list(filter(lambda x: x and x.startswith("#") is False, lignes))
    
    # on initialise les listes tokens et tags
    tokens = []
    tags = []
    
    # on analyse chaque ligne de tableau et on met à jour les listes
    for mot in mots:
        champs = mot.split('\t')
        tokens.append(champs[1])
        tags.append(champs[3])
        
    # on transforme la liste tags en une chaîne de caractères
    tags_str = " ".join(tags)
    
    # pour chaque patron enregistré dans patterns_list
    for pattern in patterns_list:
        # si on trouve une suite identique au patron
        if pattern in tags_str:
            # on calcule la longueur du patron
            num = len(pattern.split(" "))
            
            # on génère les listes de n-gram token (n_grams) et tag (n_pos) en fonction de la longueur du patron
            n_grams = []
            n_pos = []
            for i in range(0, len(tags) - num + 1):
                n_grams.append(" ".join(tokens[i:i+num]))
                n_pos.append(" ".join(tags[i:i+num]))

            # on initialise un compteur comme ancre de recherche
            start_i = 0
            
            while True:
                try:
                    # on récupère l'index du patron dans n_pos et on prend le n-gram correspondant dans n_grams
                    pos_i = n_pos.index(pattern, start_i)
                    pattern_dico[pattern].append(n_grams[pos_i])
                    start_i = pos_i + 2
                except:
                    break

Nous traitons phrase par phrase. D'abord nous filtrons les commentaires, les lignes vides et les phrases n'ayant qu'un seul token, puis nous extrayons la liste de tokens et de POS tags, appelées tokens et tags.

Les tags sont ensuite transformés en une chaîne de caractère tags_str. Pour chaque patron cherché, nous regardons s'il existe dans tags_str. Si oui, nous calculons la longueur du patron num et nous générons deux listes de num-gram, l'une des tokens n_grams et l'autre de tags n_pos. Nous cherchons le patron dans n_pos, nous récupérons son index et nous sortons le n-gram correspondant dans n_grams qui est enregistré dans pattern_dico.

3. Ecriture de sortie


In [ ]:
# on écrit le résultat dans le fichier de sortie
with open('sortie_non_trie.txt', 'w', encoding = 'utf-8', newline = '\n') as sortie:
    for pattern, patrons in pattern_dico.items():
        tmp = list(map(lambda x: sortie.write(x + '\n'), patrons))
        sortie.write('\n\n')

Dernière étape, nous écrivons le contenu du pattern_dico dans les fichiers de sortie. Nous commençons par la sortie non triée en parcourant pattern_dico directement et en écrivant toutes les valeurs.

In [ ]:
# on initialise le compteur
# à noter qu'il compte le nombre total des patrons trouvés, sans distinction entre les patterns
countresult = 0

# pour chaque entrée dans patrons_dico
for pattern, patrons in pattern_dico.items():
    # on inisitalise un dictionnaire temporaire
    tmp = {}
    for patron in patrons:
        # on compte le nombre d'occurrences de chaque patron
        try:
            tmp[patron] += 1
        except KeyError:
            tmp[patron] = 1

    # on trie les entrées
    tmplist = sorted(tmp.items(), key=lambda x: x[1], reverse=True)
    # on met à jour le compteur
    countresult += len(patrons)
    # on met à jour le dictionnaire
    pattern_dico[pattern] = tmplist
    
with open('sortie_trie.txt', 'w', encoding = 'utf-8', newline = '\n') as sortie:
    sortie.write('{} éléments trouvés\n'.format(countresult))
    for pattern, patrons in pattern_dico.items():
        sortie.write("Type de pattern: {}\n".format(pattern))
        tmp = list(map(lambda x: sortie.write(x + '\n'), patrons))
        sortie.write('\n\n')

Ensuite nous écrivons le contenu trié dans une autre sortie. Il s'agit de la même procédure que dans BAO3_1. Nous pouvons également paramétrer le format d'écriture pour qu'il ne garde que les informations qui nous intéressent.