Liste de liens :
Liste de liens :
CQi est l'API Java et Groovy permettant d'accéder au moteur de recherche, ou de “corpus”, de TXM. Ce tutoriel s'appuie sur des exemples de lignes de code Groovy que l'on peut exécuter facilement depuis TXM. L'API est entièrement définie par la classe Java MemCqiClient (ou AbstractCqiClient - javadoc) de TXM. Chaque méthode de cette classe donne accès à un aspect du moteur de corpus (connexion au moteur, propriétés du moteur, corpus disponibles, appel de requêtes CQL sur un corpus, gestion des résultats de requêtes, index et lexiques du moteur de recherche).
Ce tutoriel reprend de nombreux éléments du document Stefan Evert, "CQi tutorial −− how to run a CQP query", 2000, IMS Stuttgart.
Comme la Javadoc de la classe AbstractCqiClient est TRÈS perfectible, il faut se référer au code C MemCqiServer.c qui implémente ces méthodes pour savoir quelle fonction ou macro de l'API C de CQi est appelée. L'API CQi étant strictement définie dans cqi.h.
Une documentation de l'API CQi est en cours de rédaction à l'occasion de ce tutoriel (en espérant qu'elle alimente la Javadoc un jour).
La documentation de l'API Python CWB.CL du projet cwb-python est intéressante à consulter en complément, ainsi que l'API Perl CWB::CL du package CPAN de même nom. Ces APIs sont construites à partir des mêmes APIs de base CQP avec des variantes de services intéressantes.
Nous allons d'abord vérifier que le corpus DISCOURS est présent dans votre TXM.
C:\Documents and Settings\<identifiant de l'utilisateur>\TXM\scripts\user
” sous Windows 7) et le nom du fichier “cqi.groovy” (vous pouvez choisir un nom différent, mais l'extension doit être '.groovy')import org.txm.Toolbox def corpusEngine = Toolbox.getCqiClient() println corpusEngine.listCorpora().sort()
Voici à quoi votre interface de TXM devrait ressembler après l'exécution de ce script :
import org.txm.Toolbox
déclare la boite à outils donnant accès à toutes les fonctionnalités de TXM aux scripts Groovy (dont l'API CQi)def corpusEngine = Toolbox.getCqiClient();
associe un accès à l'API CQi (une instance de la classe AbstractCqiClient) à une variable nommée corpusEngine
println corpusEngine.listCorpora().sort()
affiche la liste des noms de corpus retournés par la méthode listCorpora de la classe AbstractCqiClient.sort()
permet de trier la liste des noms de corpus alphabétiquementprintln Toolbox.getCqiClient().listCorpora().sort()
, ce qui ne nécessite pas de variable intermédiaire (mais est moins lisible pédagogiquement)Nous allons maintenant vérifier que le corpus DISCOURS dispose bien des propriétés de mot 'pos' et 'lemma' ainsi que de la structure 's'.
import org.txm.Toolbox def corpusEngine = Toolbox.getCqiClient() println "Positional Attributes : "+corpusEngine.corpusPositionalAttributes("DISCOURS") println "Structural Attributes : "+corpusEngine.corpusStructuralAttributes("DISCOURS")
Exécution de cqi.groovy Positional Attributes : [word, id, sid, pid, pos, func, lemma] Structural Attributes : [text, text_id, text_loc, text_type, text_date, text_numero, text_base, text_project, s, s_id, p, p_id, txmcorpus, txmcorpus_lang] Terminé: 32 ms
Maintenant que nous connaissons les noms des propriétés de mots du corpus DISCOURS, nous pouvons afficher leurs valeurs au fil du corpus.
import org.txm.Toolbox def corpusEngine = Toolbox.getCqiClient() def cpos = [0, 1, 2, 3, 4] as int[] println corpusEngine.cpos2Str("DISCOURS.word", cpos) println corpusEngine.cpos2Str("DISCOURS.lemma", cpos) println corpusEngine.cpos2Str("DISCOURS.pos", cpos)
Exécution de cqi.groovy [Les, affaires, de, la, France] [le, affaire, de, le, France] [Da-.p-d, Ncfp, Sp, Da-fs-d, Np.s] Terminé: 36 ms
def cpos = [0, 1, 2, 3, 4] as int[]
déclare la variable 'cpos' (pour 'c[orpus] pos[ition]') comme contenant un tableau d'entiers de 0 à 4. Comme la position des mots d'un corpus CQP commence à '0', ce tableau contient les adresses des 5 premiers mots du corpuscorpusEngine.cpos2Str("DISCOURS.word", cpos)
retourne les valeurs de la propriété 'word' des 5 premiers mots du corpuscorpusEngine.cpos2Id("DISCOURS.word", cpos)
pour obtenir les codes numériques des valeurs situées à ces positionscorpusEngine.id2Str("DISCOURS.word", id)
(voir ci-dessous)corpusEngine.cpos2Str("DISCOURS.lemma", cpos)
retourne les valeurs de la propriété 'lemma' des 5 premiers mots du corpuscorpusEngine.cpos2Str("DISCOURS.pos", cpos)
retourne les valeurs de la propriété 'pos' des 5 premiers mots du corpusLes lexiques de propriétés de mots ('lexicon' dans la terminologie CQP) encodent numériquement leurs différentes valeurs.
Nous allons maintenant afficher le code numérique de la forme “France” dans le corpus DISCOURS.
import org.txm.Toolbox def corpusEngine = Toolbox.getCqiClient() println corpusEngine.str2Id("DISCOURS.word", "France")[0]
Exécution de cqi.groovy 4 Terminé: 24 ms
corpusEngine.str2Id("DISCOURS.word", "France")[0]
retourne le code numérique de la valeur “France” telle qu'encodée dans le lexique de la propriété 'word' du corpus DISCOURSLes lexiques de propriétés de mots encodent la fréquence de leurs différentes valeurs.
Nous allons maintenant afficher la fréquence de la forme “France” dans le corpus DISCOURS.
import org.txm.Toolbox def corpusEngine = Toolbox.getCqiClient() println corpusEngine.id2Freq("DISCOURS.word", corpusEngine.str2Id("DISCOURS.word", "France")[0])[0]
Exécution de cqi.groovy 428 Terminé: 26 ms
corpusEngine.id2Freq("DISCOURS.word", ...)", "France")[0]
retourne la fréquence (le nombre total d'occurrences) de la valeur “France” de la propriété 'word' du corpus DISCOURS. La partie ...)[0]
extrait le premier élément du tableau de résultats retourné par la méthode id2Freq (cette méthode est conçue pour interroger plusieurs valeurs de propriétés à la fois).Les index de propriétés de mots encodent les positions de leurs différentes valeurs au fil du corpus.
Nous allons maintenant afficher les positions des dix premières occurrences de la forme “France” dans le corpus DISCOURS.
import org.txm.Toolbox def corpusEngine = Toolbox.getCqiClient() println corpusEngine.id2Cpos("DISCOURS.word", corpusEngine.str2Id("DISCOURS.word", "France")[0])[0..9]
Exécution de cqi.groovy [4, 89, 291, 583, 832, 1526, 2230, 2366, 2378, 2666] Terminé: 29 ms
corpusEngine.id2Cpos("DISCOURS.word", ...)
retourne les positions des occurrences du code numérique de l'index de la propriété 'word' dans le corpus DISCOURScorpusEngine.id2Cpos(...)[0..9]
retourne les 10 premières positionsIl est possible d'interroger un lexique pour obtenir les codes numériques d'un ensemble de formes correspondant à un expression régulière.
Et il est bien sûr possible d'obtenir ensuite les valeurs de chaque code.
Nous allons maintenant afficher les formes du corpus DISCOURS correspondant à l'expression régulière “[Ff]ran[cç][aeio].*”.
import org.txm.Toolbox def corpusEngine = Toolbox.getCqiClient() println corpusEngine.id2Str("DISCOURS.word", corpusEngine.regex2Id("DISCOURS.word", "[Ff]ran[cç][aeio].*"))
Exécution de cqi.groovy [France, Français, Françaises, française, français, françaises, franco-allemand, franco-américaines, franco-américaine, franco-allemande, franco-marocains, franco-allemands, franco-britannique] Terminé: 28 ms
corpusEngine.regex2Id("DISCOURS.word", "[Ff]ran[cç][aeio].*")
retourne les codes numériques des entrées correspondant à l'expression régulière “[Ff]ran[cç][aeio].*” dans le lexique des formes (word) du corpus DISCOURScorpusEngine.id2Str("DISCOURS.word"...)
retourne les valeurs correspondant aux codes numériques du lexique des formes (word) du corpus DISCOURSAprès avoir listé toutes les structures disponibles et leurs propriétés dans le corpus DISCOURS au début de ce tutoriel, nous allons maintenant lister les différentes valeurs possibles de la propriété 'type' de la structure 'text' (la structure 'text' codant les limites des discours de ce corpus, il s'agit donc d'une métadonnée).
import org.txm.Toolbox def corpusEngine = Toolbox.getCqiClient() println ((0..corpusEngine.attributeSize("DISCOURS.text_type")-1).collect { it -> corpusEngine.struc2Str("DISCOURS.text_type", [it] as int[])[0] }.sort().unique())
Exécution de cqi.groovy [Allocution radiotélévisée, Conférence de presse, Entretien radiotélévisé] Terminé: 35 ms
0..corpusEngine.attributeSize("DISCOURS.text_type")-1
déclare l'intervalle des entiers entre 0 et le nombre total (-1) de valeurs prises par la propriété 'type' de cette structure dans le corpus DISCOURS.collect { it -> ... }
retourne la liste de tous les résultats de l'application du calcul suivant pour chaque entier de cet intervallecorpusEngine.struc2Str("DISCOURS.text_type", [it] as int[])[0]
calcule la valeur prise par la propriété 'type' pour une entrée donnée.sort().unique()
retourne la liste alphabétiquement triée des différentes valeurs possiblesNous allons maintenant utiliser le moteur de recherche CQP pour rechercher toutes les séquences de la forme <lemme “je” suivi d'un verbe> et lister les 10 lemmes de verbes les plus fréquents dans cette position. Pour pouvoir ne lister que les verbes, nous les ciblons au sein de la requête avec l'opérateur '@'.
import org.txm.Toolbox byte CQI_CONST_FIELD_TARGET = (byte) 0x00 def corpusEngine = Toolbox.getCqiClient() corpusEngine.cqpQuery("DISCOURS", "RES1", "'je' @[pos='V.*']") def matches_target_lemma = corpusEngine.cpos2Str("DISCOURS.lemma", corpusEngine.dumpSubCorpus("DISCOURS:RES1", CQI_CONST_FIELD_TARGET, 0, corpusEngine.subCorpusSize("DISCOURS:RES1")-1)) println matches_target_lemma.countBy { it }.sort { -it.value }.take(10)
Exécution de cqi.groovy [croire:36, vouloir:25, être:22, pouvoir:18, dire:13, aller:11, parler:8, devoir:7, faire:5, penser:5] Terminé: 61 ms
byte CQI_CONST_FIELD_TARGET = (byte) 0x00
déclare un nouveau type de donnée CQi pour accéder aux positions d'occurrences ciblées (elle ne sera plus utile à partir de TXM 0.7.8)corpusEngine.cqpQuery("DISCOURS", "RES1", "'je' @[pos='V.*']")
appelle le moteur de recherche CQP pour rechercher la requête <'je' @[pos='V.*']> dans le corpus DISCOURS en sauvegardant le résultat dans le sous-corpus RES1def matches_target_lemma = corpusEngine.cpos2Str("DISCOURS.lemma", corpusEngine.dumpSubCorpus("DISCOURS:RES1", CQI_CONST_FIELD_TARGET, 0, corpusEngine.subCorpusSize("DISCOURS:RES1")-1))
calcule la liste des lemmes correspondant aux positions ciblées dans les résultatscorpusEngine.dumpSubCorpus("DISCOURS:RES1", CQI_CONST_FIELD_TARGET, 0, corpusEngine.subCorpusSize("DISCOURS:RES1")-1))
récupère les positions ciblées dans le résultat RES1corpusEngine.cpos2Str("DISCOURS.lemma", ...)
récupère les lemmes des positions retournéesprintln matches_target_lemma.countBy { it }.sort { -it.value }.take(10)
compte les lemmes, les trie par fréquence décroissante et ne garde que les 10 premiers.countBy { it }
compte les lemmes.sort { -it.value }
trie les lemmes par fréquence décroissante.take(10)
ne garde que les 10 premiersNous allons d'abord vérifier le nombre de phrases du corpus DISCOURS, c'est à dire le nombre de structures 's'.
import org.txm.Toolbox def corpusEngine = Toolbox.getCqiClient() printf("Nombre de phrases du corpus DISCOURS = %d\n", corpusEngine.attributeSize("DISCOURS.s"))
Exécution de cqi.groovy Nombre de phrases du corpus DISCOURS = 3427 Terminé: 23 ms
DISCOURS.s
avec la méthode attributeSizeNous allons maintenant afficher le nombre de mots par phrase.
import org.txm.Toolbox def corpusEngine = Toolbox.getCqiClient() printf("start\tend\tlength\n") for (i in 0..corpusEngine.attributeSize("DISCOURS.s")-1) { (start, end) = corpusEngine.struc2Cpos("DISCOURS.s", i) printf("%d\t%d\t%d\n", start, end, end-start+1) }
Exécution de cqi.groovy start end length 0 13 14 14 16 3 17 23 7 24 35 12 36 50 15 51 58 8 59 78 20 79 97 19 98 105 8 ... 104983 104988 6 104989 105001 13 105002 105106 105 105107 105118 12 105119 105156 38 105157 105182 26 105183 105190 8 Terminé: 176 ms
printf("start\tend\tlength\n")
affiche la ligne suivante (le code '\t' correspond à la tabulation, voir le Formateur Java) :start end length
for (i in 0..corpusEngine.attributeSize("DISCOURS.s")-1) {
commence une boucle de 0 au nombre de structures 's' du corpus DISCOURS moins 1. c'est-à-dire qu'elle va itérer autant de fois qu'il y a de phrases. Le numéro d'itération sera stocké dans la variable i
, qui prendra donc successivement les valeurs 0, 1, 2, etc.start
et end
end-start+1
)