INTRODUCTION
OBJECTIF: Extraction des patrons morphosyntaxiques dans les étiquetages produits avec Talismane/Treetagger
Données
-
Entrée :
les sorties au format XML de l'étiquetage (via Treetagger) issues de la Boîte à Outils Série 2
les sorties "brutes" de l'étiquetage (via Talismane) issues de la Boîte à Outils Série 2
- des listes des patrons morphosyntaxiques triés par ordre de fréquence d'occurrence (1 fichier txt par motif et par rubrique)
Méthodes et Outils
- Perl : utiliser un script Perl pour l'extraction des patrons.
- Python : adapter le script pour l'extraction des patrons.
- Xquery : fouiller les données des fichiers XML par des requêtes XQuery, en passant par le logiciel BaseX.
- Xslt : en choisissant la méthode de sortie texte et en utilisant Cygwin ou Firefox, on obtient des pages ou des fichiers de résultats.
Scripts et resultats
script sur cygwin
- On a utilisé cygwin pour de divers tâches, comme l'extractions de patron, la classification de sorties, et le comptage des patrons, etc.
(extraire les patrons avec perl):
perl5.28.0 extract-terminologie-ter.pl 3208-2018-talismane.txt termino.txt
(classer et compter les items dans un fichier):
less all_patrons.txt | sort | uniq -c | sort -gr >item_count_patron.txt
(extraire les items avec xslt):
xsltproc extractionPatrons.xsl 3208_2018.xml > patronN-PREP-N.txt
(extraire,classer et compter les items avec xslt):
xsltproc extractionPatrons.xsl 3208_2018.xml | sort | uniq -c | sort -gr >item_count_patron.txt
Perl
Quand on rencontre une ligne qui correspond à la fin d'une phrase, on ouvre le fichier contenant le patron. Chaque ligne correspond à un patron à retrouver. On transforme le patron en expression régulière et on stocke le tout dans une variable $listePatron. Ensuite, on recherche ce motif dans notre phrase recomposée : s'il y a une correspondance, on la récupère grâce à la variable par défaut, puis on enlève l'étiquette de POS pour ne garder que la forme. Le script renvoie cette correspondance nettoyée. Voilà le script fourni par M. Serge FLEURY et J.-M.D.:
#!/usr/bin/perl <) { chomp($patron); push(@{$listePatron},[split(/ +/,$patron)]) } close($fileTer); my %dicoPatron=(); my $nbTerme=0; my @WORDS; my @POS; open my $fileT,"<:encoding(UTF-8)",$ARGV[0]; while (my $ligne=<$fileT>) { if (($ligne!~/^1\t££/) and ($ligne!~/^\#\#/) and ($ligne!~/^$/)){ my @TMPLIGNE=split(/\t/,$ligne); push(@WORDS,$TMPLIGNE[1]); push(@POS,$TMPLIGNE[3]); } } close($fileT); my $lg=0; while (my $pos=$POS[$lg]) { foreach my $patron (@{$listePatron}) { if ($pos eq $patron->[0] ) { my $indice=1; my $longueur=1; my $stop=1; while (($indice <= scalar @$patron) and ($stop == 1)) { if ($POS[$indice+$lg] eq $patron->[$indice]) { $longueur++; $indice++; } else { $stop=0; } } if ($longueur == scalar @$patron) { $dicoPatron{join(" ",@{$patron})}->{join(" ",@WORDS[$indice+$lg-scalar @$patron..$indice+$lg-1])}++; $nbTerme++; } } } $lg++; } open my $fileResu,">:encoding(UTF-8)","perlIsTheBigOneBis.txt"; print $fileResu "$nbTerme éléments trouvés\n"; foreach my $patron (keys %dicoPatron) { print $fileResu "\nType de pattern: ".$patron." \n\n"; foreach my $terme (sort {$dicoPatron{$patron}->{$b} <=> $dicoPatron{$patron}->{$a} } keys %{$dicoPatron{$patron}}) { print $fileResu $dicoPatron{$patron}->{$terme}."\t".$terme."\n"; } } print $fileResu "\nScript execution time: " . tv_interval($timepg) . " seconds."; close($fileResu); exit;
On a comparé les résultats de trois rubriques en utilisant Perl et Python pour extraire les patrons. Pour la rubrique 3208 et 3214, ils ont rendu le même résultat, alors que pour la rubrique 3246, Perl a trouvé deux éléments de plus que Python. On n'a pas encore trouvé l'explication pour le problème.
Python
- sauter des lignes qui sont inutiles : words = list(filter(lambda x: x and x.startswith("#") == False, lines))
- utiliser \t(space) pour séparer chaque partie d'étiqutage : champs = word.split('\t')
- chercher les patrons dans la liste de tags du texte : for i, tag in enumerate(tags):
- comparer la liste de patron la liste de catégorie des mots dans le texte : if tags[i:i+pat_leng] == ele:
- le patron est la clé de dicationnaire et il correpond à plusieurs valeurs, qui sont la forme de chaque mot : patrons_dico[key].append(n_gram)
- parcourir toute la dictionnaire : for pattern, patrons in patrons_dico.items():
- compter le nombre des chaque item qui correspond à chaque patron : tmplist = sorted(tmp.items(), key = lambda x:x[1], reverse = True)
Dans cette phase, nous utilisons le script fourni par Danrun et Pierre, et voici quelques étapes importantes dans ce processus.
sentence_list.pop()
patterns_list = list(map(lambda x: findall('\w+', x)
if tag in patterns_dico.keys():
#!/usr/bin/env python3 # -*- coding: utf-8 -*- from time import monotonic from re import findall filepath_sequence = input('Saisissez un fichier talismane:\n> ') patterns = input('Saisissez les patrons (séparés par un /):\n> ') start = monotonic() with open(filepath_sequence, 'r', encoding = 'utf-8', newline = '\n') as fichier: contenu = fichier.read() sentence_list = contenu.split('\n\n') sentence_list.pop() patterns_list_input = patterns.split('/') patterns_list = list(map(lambda x: findall('\w+', x), patterns_list_input)) patterns_dico = {} patrons_dico = {} for pattern in patterns_list: patrons_dico[' '.join(pattern)] = [] try: patterns_dico[pattern[0]] except: patterns_dico[pattern[0]] = [] patterns_dico[pattern[0]].append(pattern) for sentence in sentence_list: lines = sentence.split('\n') words = list(filter(lambda x: x and x.startswith("#") == False, lines)) tokens = [] tags = [] for word in words: champs = word.split('\t') tokens.append(champs[1]) tags.append(champs[3]) if len(tokens) == 1: continue for i, tag in enumerate(tags): if tag in patterns_dico.keys(): pat_list = patterns_dico[tag] print(pat_list) for ele in pat_list: pat_leng = len(ele) if tags[i:i+pat_leng] == ele: n_gram = ' '.join(tokens[i:i+pat_leng]) key = ' '.join(ele) patrons_dico[key].append(n_gram) countresult = 0 for pattern, patrons in patrons_dico.items(): tmp = {} for patron in patrons: try: tmp[patron] except: tmp[patron] = 0 tmp[patron] += 1 tmplist = sorted(tmp.items(), key = lambda x:x[1], reverse = True) countresult += len(patrons) patrons_dico[pattern] = tmplist with open('sortie_talismane.txt', 'w', encoding = 'utf-8', newline = '\n') as sortie: sortie.write('{} éléments trouvés\n'.format(countresult)) for pattern, patrons in patrons_dico.items(): sortie.write("Type de pattern: {}\n\n".format(pattern)) tmp = list(map(lambda x: sortie.write("{0}\t{1}\n".format(x[1], x[0])), patrons)) sortie.write('\n\n') end = monotonic() print('Temps de traitement: {:.2f}s'.format(end - start))
- resultat_quatre_patrons
Xquery
- treetagger_quatre_patrons
for $art in collection("sortie-3214-regexp")//article for $elt in $art/element let $conc2 := if (($elt/data[1]="NOM") and ($elt/following-sibling::element[1]/data[1]="PRP") and ($elt/following-sibling::element[2]/data[1]="NOM") and ($elt/following-sibling::element[3]/data[1]="PRP")) then ( concat($elt/data[3]/text()," ",$elt/following-sibling::element[1]/data[3]/text()," ",$elt/following-sibling::element[2]/data[3]/text()," ",$elt/following-sibling::element[3]/data[3]/text()," ") ) else if (($elt/data[1]="NOM") and ($elt/following-sibling::element[1]/data[1]/text()="ADJ")) then ( concat($elt/data[3]/text()," ",$elt/following-sibling::element[1]/data[3]/text()) ) else if (($elt/data[1]="ADJ") and ($elt/following-sibling::element[1]/data[1]/text()="NOM")) then ( concat($elt/data[3]/text()," ",$elt/following-sibling::element[1]/data[3]/text()) ) else if (($elt/data[1][contains(text(),"VER")]) and ($elt/following-sibling::element[1]/data[1][contains(text(),"DET")]) and ($elt/following-sibling::element[2]/data[1]/text()="NOM")) then ( concat($elt/data[3]/text()," ",$elt/following-sibling::element[1]/data[3]/text()," ",$elt/following-sibling::element[2]/data[3]/text()) ) else ( " " ) where $conc2 != " " return $conc2
- resultat_quatre_patrons
- script_xquery_chaque_patron
- resultat_chaque_patron
- script_talismane_quatre_patrons
for $art in collection("sortie-3214-regexp")//article for $elt in $art/element let $conc2 := if (($elt/data[1]="NOM") and ($elt/following-sibling::element[1]/data[1]="PRP") and ($elt/following-sibling::element[2]/data[1]="NOM") and ($elt/following-sibling::element[3]/data[1]="PRP")) then ( concat($elt/data[3]/text()," ",$elt/following-sibling::element[1]/data[3]/text()," ",$elt/following-sibling::element[2]/data[3]/text()," ",$elt/following-sibling::element[3]/data[3]/text()," ") ) else if (($elt/data[1]="NOM") and ($elt/following-sibling::element[1]/data[1]/text()="ADJ")) then ( concat($elt/data[3]/text()," ",$elt/following-sibling::element[1]/data[3]/text()) ) else if (($elt/data[1]="ADJ") and ($elt/following-sibling::element[1]/data[1]/text()="NOM")) then ( concat($elt/data[3]/text()," ",$elt/following-sibling::element[1]/data[3]/text()) ) else if (($elt/data[1][contains(text(),"VER")]) and ($elt/following-sibling::element[1]/data[1][contains(text(),"DET")]) and ($elt/following-sibling::element[2]/data[1]/text()="NOM")) then ( concat($elt/data[3]/text()," ",$elt/following-sibling::element[1]/data[3]/text()," ",$elt/following-sibling::element[2]/data[3]/text()) ) else ( " " ) where $conc2 != " " return $conc2
- resultat_quatre_patrons
- Ici, on peut observer que le nombre de result de xquery est le même avec python! (13,899 results)
Xslt
- script_xslt_chaque_patron_treetagger
- resultat_html_patron_treetagger
- ATTENTION: il faut ouvrir ces pages avec le navigateur Firefox qui possède un convertisseur xslt-xml
- Si vous en avez pas, voici la photo de résultat
- script_xslt_chaque_patron_treetagger
- resultat_texte_patron_treetagger(on a omis quelques résultats pas très intéressants ici)
- script_talismane_quatre_patrons
- resultat_quatre_patrons
- ATTENTION: il faut ouvrir ces pages avec le navigateur Firefox qui possède un convertisseur xslt-xml
- Si vous en avez pas, voici la photo de résultat
- script_xslt_chaque_patron_talismane
- resultat_texte_patron_talismane(le résultat est le même avec xquery et python)