À la base j’aurais pas dû poster ça dans un journal mais dans un forum. Le fait que ce soit un journal m’incite à continuer à poster mon code…
Voici donc la dernière version de Reader.py, script dont le but est de prendre n’importe quel fichier texte en entrée, le découper en « phrases » et stocker la fréquence d’apparition de ces « phrases » dans une base SQLite.
#!/usr/bin/env python3"""Reader.py: Read text for sentences frequencies."""__author__="M4rotte"__copyright__="Copyright 2015, Institut Marotte for Mouling"__license__="GPL"__version__="0.4"importsysimportreimportsqlite3fromcollectionsimportCounterclassReader:def__init__(self):self.sb=''self.characters=Counter()self.sentences=Counter()self.signature=''self.sig96=''self.start=re.compile(r'^(\n|)[\-–—«ÉÀÇÔÎÖÏA-Za-z]$')# If string buffer (self.sb) match, just read next character.self.stop=re.compile(r'(.|\n)+[.!?…:]( |\n)$')# If self.sb match, consider self.sb valid sentence, store it in self.sentences and reset self.sb.self.discard=re.compile(r'^(([0-9]+)([\\:]+)|##)(.*)(\n|)')# If self.sb match, just drop it.self.correct_tiret=re.compile(r'^( |)-')# If match (it's obviously part of a dialog), replace the tiret with the designed Unicode character.self.cancel_stop=re.compile(r'(.*|)( |\n)(M|Me)(\.)( |\n|)$');# If match (means it matched self.stop first…), just don't "stop" and read next character.self.nb_insert=0# Number of inserts off the last read()self.nb_update=0# Number of updates off the last read() self.nb_record=0# Number of records in the updated databasedefread(self,f):"""Read input and populate `sentences`"""whileTrue:c=f.read(1)ifnotc:breakifc=='\xa0':c=' 'self.characters[c]+=1self.sb+=cifself.sbin[' ','\n','»']:self.sb=''continueifself.discard.match(self.sb):self.sb=''continueifself.start.match(self.sb):continueifself.stop.match(self.sb)andnotself.cancel_stop.match(self.sb):ifself.correct_tiret.match(self.sb):self.sb=self.sb.replace('-','–')self.sentences[self.sb.strip()]+=1self.sb=''continueforcinself.characters.most_common():self.signature+=c[0]self.sig96=self.signature[:96].replace('\n',' ')defstore(self,dbfile='reader.sqlite'):""" Update the database from `sentences` """connection=sqlite3.connect(dbfile)cursor=connection.cursor()cursor.execute('create table if not exists sentences (sentence text primary key unique, freq int)')## For futur use :) # cursor.execute('create virtual table if not exists fts_strings using fts4(string text, freq int, tokenize=unicode61 "remove_diacritics=0" "tokenchars= ")')# Store `sentences` into databaseforsinself.sentences.most_common():try:cursor.execute("insert into sentences values (?, ?)",(s[0],s[1]))self.nb_insert+=1exceptsqlite3.IntegrityError:self.nb_update+=1cursor.execute("update sentences set freq=freq+? where sentence = ?",(s[1],s[0]))self.nb_record=cursor.execute('select count(*) from sentences;')sys.stderr.write('Database update: '+str(self.nb_insert)+' inserts '+str(self.nb_update)+' updates '+str(self.nb_record.fetchone()[0])+' records. SIG96 : '+str(self.sig96)+'\n')connection.commit()try:reader=Reader()reader.read(sys.stdin)reader.store()exceptKeyboardInterruptaserror:print('\n'+repr(error),file=sys.stderr)sys.stderr.flush()sys.stdout.flush()exit(1)
La sortie en partant d’une base inexistante et en la renseignant à partir de classiques de la littérature française, posts du bouchot et contenu issu de Wikipédia (histoire d’avoir des entrées assez hétérogènes) :
$ for f in *.txt; do ./Reader.py < "$f"; done; sqlite3 ../linuxfr.org.sqlite 'select message from posts;' | ./Reader.py ; sqlite3 ../Wikipedia.sqlite 'select fact from facts;' | ./Reader.py
Database update: 7498 inserts 0 updates 7498 records. SIG128 : eaistnrulodcmp v,é’fqh.gbàx-jyè;–ALê?«»zkC!SEJMKDI:WPçO…îôâQVNT#œû_ùwBUïRG1FYÀ2()É307H5486][9ÔX
Database update: 7533 inserts 74 updates 15031 records. SIG128 : eaistnrulodcpm v,é’qf.hbgàjx-yè–;AêL?«»kCzE!SM:JIPDKWîçVôNOTâQ_…û#ùœBw0RïU1ÀF2G()3YHÉ5][847°69ü
Database update: 7367 inserts 33 updates 22398 records. SIG128 : easitnrulodcmp ,év’f.qgbhàèxj-êyMIL;EPCS«»Az!RâçJ:DFVûîTô…?ùOGQ–UœNïÀB1É8H{Ç45/kë#0w397XÔ2Z)6—]
Database update: 2496 inserts 2 updates 24894 records. SIG128 : esatinrulodcpmé, v'qfgbh.àxèCy;jê-LAG\:IRzôSBçâTHPîDûVMEù12Q3O«4»N5ï0U687œ?9FÉJ)(_!…ëÀW
Database update: 10914 inserts 71 updates 35808 records. SIG96 : easitnrulodc mp,év’.bfhgqxà»!èj«;-yMAêIECLFDz?P:çSRâOVJîTôQUBùN—…ûHG()ïk1#8X3Ç2w4590Yë6óW7Z\áñú
Database update: 1359 inserts 15 updates 37167 records. SIG96 : esitanrulodpmc '.é,v-qbfghjàxJèIEC!:ML?TyAêçPzQOS»«BVHûRîâùXôÇDNU2ï1F3()69À05Éë8Z°;7
Database update: 10707 inserts 62 updates 47874 records. SIG96 : eaistnrulodmpc év,’q.fhbgàjMx-èJêyL;CIz–!ERSVAP?Dâç:ûôQî…FBTNOœUùÀ#XHGÉï1*()0»«8Kw637k°924YW5ÊZ
Database update: 5734 inserts 79 updates 53608 records. SIG96 : eastinrulodcmp év'.,fqgbh-àjTxèyIêL!çJEC«»A?kMHPSzDOB…Nô*Qûâ:RUîV{ùXœ_ÀÇwGF;1ÉW/()2ï][YZ07438}Î
Database update: 7114 inserts 64 updates 60722 records. SIG96 : easintrulodm,cp vé’qf.hgbàjxè—-yêM:L!zEACJ?IP;çD»…«FôSVùTâOûNBQîR–œUHGÀ1kÉw2X907K5ü84ï#ëäÇY3WÈÊ
Database update: 10690 inserts 123 updates 71412 records. SIG96 : eastinrulodc,mp vé'.fghbq-àjxè!yMEêLC;N…zIPçADSâF?VBT:OôJûùîGURHQZœ_À»«Çk*{Éw1Xïë4Ô05W2Ê73Y
Database update: 24771 inserts 29 updates 96183 records. SIG96 : eastinruol:cpdm1 20fh/vé'5b3.qg4-,j76<>98"wyx?à[]=ç͌!kèêzCA’_;&SET)LPMOINJD(FBRU\VHG%ô…+W̚҉Qû█«
Database update: 67825 inserts 14 updates 164008 records. SIG96 : eainrstoludcmép,. ghvbf1q'()y9L02ACxè-MSjPàBk†8DRJ3IGçF5746T:EzNê°HV’OKUwÉ«W»Xôûî;ïZâYQœ%áù/í"ë
On peut voir que l’intégration du contenu Wikipédia amène à certains biais (et c’est pas Me Soleil qui s’en plaindra…) mais que « Ah ! » et « Oh ! » restent des valeurs sûres :
$ sqlite3 reader.sqlite "select * from sentences order by freq desc limit 30;"
Signe du zodiaque :|358
M.|224
Ah !|194
« Ah !|181
– Ah !|180
Mgr Paul Guérin, Vie des saints ; t.|162
– Oh !|148
Pas de journée internationale répertoriée pour cette date.|134
Oh !|100
« Oh !|87
Les noms de plusieurs voies, places, sites ou édifices, de pays ou régions francophones, contiennent cette date sous diverses graphies :|66
– Eh bien !|59
France :|59
Eh bien !|56
États-Unis :|52
Décembre :|49
Bahaïsme :|47
Février :|46
Juin :|43
– Tiens !|40
Octobre :|40
Hein ?|39
–C'est quoi ça ?|39
Mai :|39
Fêtes religieuses romaines :|39
Mars :|38
« Non !|37
dit Frédéric.|36
Japon :|36
— Ah !|35
Posté par Marotte ⛧ .
En réponse au journal Analyse de texte.
Évalué à 2.
Dernière modification le 21 décembre 2015 à 23:36.
Merci encore pour toutes vos suggestions. Il faut souligner qu’elles étaient toutes plutôt pertinentes… J’ai pu appliquer beaucoup de vos conseils même si j’ai dû abandonner certains (je pense notamment au islice du module itertools, qui est exactement ce que je cherchais à faire…) parce que je suis parti sur d’autres types de variables et une nouvelle approche du problème.
D’une point de vue fonctionnel je désire ne plus saucissoner l’input à l’arrache mais essayer de découper sur les phrases en me basant sur les règles typographiques (une phrase finit par un point, '«' ouvre un dialogue (ou pas…), etc…). Ça fait nettement moins d’enregistrements en base !
D’un point de vue technique j’ai utilisé une classe afin que le code principal soit plus clair.
Donc voici le code, j’ai viré les commentaires inutiles et il est de toute façon plus concis.
#!/usr/bin/env python3"""Reader.py: Read text for sentences frequencies."""__author__="M4rotte"__copyright__="Copyright 2015, Institut Marotte for Mouling"__license__="GPL"__version__="0.3"importsysimportreimportsqlite3fromcollectionsimportCounterclassReader:def__init__(self):self.sb=''self.characters=Counter()self.sentences=Counter()self.start=re.compile(r'^(\n|)[\-–—«ÉÀÇÔÎÖÏA-Za-z]$')self.stop=re.compile(r'(.|\n)+[.!?…:]( |)$')self.nb_insert=0self.nb_update=0self.discard=re.compile(r'^(([0-9]+)([\\]+)|^//).')defread(self,f):"""Read input and populate `sentences`"""whileTrue:c=f.read(1)ifc=='\xa0':c=' 'self.sb+=cself.sb=self.sb.replace(' ',' ')ifnotc:breakifself.start.match(self.sb):continueifself.stop.match(self.sb):self.sentences[self.sb]+=1self.sb=''continueifself.sb==' 'orself.sb=='\n'orself.sb=='«'orself.sb=='»':self.sb=''defstore(self,dbfile):""" Update the database from `sentences` """connection=sqlite3.connect(dbfile)cursor=connection.cursor()cursor.execute('create table if not exists sentences (sentence text primary key unique, freq int)')## For futur use :) # cursor.execute('create virtual table if not exists fts_strings using fts4(string text, freq int, tokenize=unicode61 "remove_diacritics=0" "tokenchars= ")')# Store `sentences` into databaseforsinself.sentences.most_common():ifnotself.discard.match(s[0]):try:cursor.execute("insert into sentences values (?, ?)",(s[0],s[1]))self.nb_insert+=1exceptsqlite3.IntegrityError:self.nb_update+=1cursor.execute("update sentences set freq=freq+? where sentence = ?",(s[1],s[0]))sys.stderr.write('Database update: '+str(self.nb_insert)+' inserts '+str(self.nb_update)+' updates. \n')connection.commit()connection.commit()try:reader=Reader()reader.read(sys.stdin)reader.store('reader.sqlite')exceptKeyboardInterruptaserror:print('\n'+repr(error),file=sys.stderr)sys.stderr.flush()sys.stdout.flush()exit(1)
D’un point de vu performance je trouve que ça tourne pas mal (à partir d’une base inexistante) :
Les fichiers sont des classiques de la littérature issus de sites comme http://www.ebooksgratuits.com/ (principalement), convertis from EPUB par un script choppé sur github. Les fichiers ayant été ensuite légèrement toilettés à la main est au grep avant traitement… Ça représente 6,6M de texte UTF-8.
Voici le résultat de quelques requêtes effectuées sur la base suite au traitement de ces fichiers :
sqlite> select * from sentences order by freq desc limit 10;
M.|213
Ah !|184
« Ah !|181
- Oh !|112
- Ah !|98
Oh !|91
« Oh !|85
– Ah !|67
Eh bien !|54
- Tiens !|38
sqlite> select * from sentences where sentence like '%?' order by freq desc limit 10;
Hein ?|33
- Hein ?|27
– Pourquoi ?|18
« Eh bien ?|13
pourquoi ?|12
quoi ?|11
« Pourquoi ?|11
– Pourquoi cela ?|10
– Comment cela ?|9
« Comment ?|9
sqlite> select * from sentences where sentence like '%moule%' order by freq desc limit 10;
« Il n’y a plus de ténors, disait-il, le moule en est brisé.|1
Mme Arnoux fit exhiber les moules pour les ouvrages plus difficiles.|1
J’ai déjà noté que je devrais appliquer mon expression régulière "discard" dans la fonction read() plutôt qu’au niveau store()…
Ce qu’il dit, je pense, c’est que les gains apportés par le fait de faire le plus possible de chose en SQL (et je dois dire qu’on peut vraiment faire un tas de trucs insoupçonnés dans les différentes variantes SQL…), par le moteur SQL, ça prends plus de sens pour un SGBD client/serveur… ça évite des « aller-retours »…
Tu n'as pas besoin des propriété ACID. Il n'y a qu'un utilisateur à la fois sur ta db, non ?
Pour l’instant.
Entendons nous bien, ce n’est pas pour ce caractère que j’ai pris SQLite. En plus des raisons déjà évoquées c’est que MariaDB je connais pas mal alors que SQLite je découvre un peu.
Peut-être un redis qui travaille au maximum en mémoire avec un système clé / valeur.
Ou encore BerkeleyDB. Mais je ne veux pas me limiter à un système clé-valeur pour les autres fonctionnalités que j’ai en tête. Je compte utiliser les tables virtuelles avec recherche de texte (FTS), par exemple.
Attention avec les dict ça bouffe très vite énormément de mémoire.
Oui… quand on parcourt toutes les combinaisons possibles rien qu’entre caractères lus ça va vite très vite.
Pour la mémoire j’ai abandonné le deque pour stocker mon tampon, le problème étant qu’un deque n’est pas adressable à l’aide de slices ([:4])… contrairement à un string. J’ai bien profité du conseil concernant collections.Counter(), c’est vraiment le type de variable dont j’avais besoin.
J’ai un peu changé d’approche pour conserver les « strings » (sentences), je me base sur des expressions régulières, c’est plus propre. Dès que j’ai un truc correct je le postici.
De plus tu utilise sqlite, je ne connais pas les performance de ce moteur pour les update mais peut être que choisir mysql(ou mariadb)
Alors là attention, que ça puisse fonctionner plus ou ou moins vite sous MariaDB c’est clairement pas un angle d’étude pour le moment… J’utilise sqlite parce que c’est parfaitement adapté à la modélisation (et c’est par ailleurs l’un des meilleurs moteur opensource de base relationnelle). SQLite ça marche partout. sqlite passe le test ACID : https://fr.wikipedia.org/wiki/Propri%C3%A9t%C3%A9s_ACID je n’en suis pas si sûr pour MarioleDB…
C’est volontaire de vouloir prendre les signes de ponctuation…
Pour moi 'mice' et 'mice.' ne sont pas deux chaînes identiques. De la deuxième on peut présager que le mot « mice » a des chances de terminer une phrase. À part le remplacement des retour à la ligne par des espaces puis la réduction des espaces consécutives à une seule espace je travaille en mode suite de caractères.
Merci pour tous ces conseils avisés ! Surtout pour le collections.Counter je pense que ça va me servir…
import fileinput
for line in fileinput.input():
Ma ligne peut avoir une longueur variable. J’aurais toujours besoin de la traiter en morceau de N caractères car je ne vais pas itérer sur tous les caractères de la ligne si je n’ai pas besoin de conserver des chaînes de plus de N caractères. J’ai également besoin d’avoir la fréquence de chaînes telles que "titi\ntoto" je devrais donc conserver un certain nombre de caractères de la ligne précédente à chaque fois…
J’ai trouvé plus simple d’avoir une sorte de « fenêtre » (buf) qui va « glisser » sur mon entrée… Par exemple si je prends un MAXLEN de 2 et que mon entrée est "abcd\n" j’aurais tour à tour :
ab
bc
cd
d\n
Avec MAXLEN à 3 j’aurais, en plus :
abc
bcd
cd\n
si tu crois que tu as besoin de gérer la mémoire toi-même en Python
Non, j’ai bien conscience que ce n’est pas à moi de gérer la mémoire.
As-tu au moins essayé de mettre une trace entre l’analyse du texte et la mise à jour de la base de données pour en être sûr ?
Je le vois simplement grâce au sys.stderr.write(…) qui est fait entre chaque étape, la première étape (chargement) et la deuxième (suppression des chaînes qui coupent des mots en deux, sauf les petites…) s’exécutent en un temps acceptable.
Personnellement, je lirais l’entrée par ligne, je splitterais sur les espaces et signes de ponctuation (ou tout ce qui n’est pas une lettre), comme ça, j’aurais directement les mots et ensuite seulement, pour chaque mot, j’itérerais sur la lettre de départ et la longueur pour avoir les groupes de lettres.
En faisant ça si j’ai une première ligne qui contient "voici une première ligne" et une seconde qui contient "et là une deuxième" je n’aurais pas d’entrée créée contenant le dernier mot de la première et le premier de la deuxième ("ligne et"). Je n’aurais pas non plus 'e e', 'e et', etc… (je remplace systématiquement "\n" par "n"…)
J’utilise donc un tampon de MAXLEN caractères qui se décale de MAXLEN-1 caractères à chaque fois afin que le dernier caractère du précédent tampon se retrouve premier du tampon suivant. Pour ça un deque me semblait plus adapté qu’un string
je chargerais tout le texte en mémoire (mon premier ordinateur avait 8 Ko de mémoire
Comme tu peux le voir le programme lit l’entrée standard, pour l’instant il attend EOF pour traiter les données mais à terme je veux qu’il puisse tourner en continue, en faisant un màj de la base tous les N tampons/caractères.
[edit: vas y efface, c'est ce que tu fais deja j'ai mal lu ton code]
Euh… bah non :)
Utiliser time() je sais faire mais je ne l’ai pas fait ici, je lance simplement le script précédé de la commande time. Vu la sortie d’erreur je sais à peu près combien de temps prend chaque étape. Je remarque nettement que ça va à une vitesse correct quand la base est vide (donc que les INSERT aboutissent).
A priori, ta structure de donnee comptant les syllabes/mots ne devraient pas augmenter lineairement avec la taille de tes textes
Oui. Plus les textes passeront plus il aura de mises à jour et moins d’insertions.
Pourquoi ne pas stocker l'information en memoire (et ne la mettre dans la base de donnee qu'a la fin)
Le genre de code qui va (si je n’y suis pas habitué pour une raison ou une autre) me nécessiter une heure pour rechercher sur le web et assimiler (c’est à dire accepter en son âme de codeur, profondément, que cela puisse pertinemment s’écrire ainsi…) avant de passer à la suite du code…
J’ai dit « concis && élégant », j’aurais du ajouter lisible pour le commun des programmeurs :)
Il faut reconnaître que l’« expansion de variable » en shell c’est une des caractéristiques de ce genre de langage et c’est rudement pratique.
$ if (rm test); then echo ok; fi
rm: cannot remove `test': No such file or directory
$ touch test; if (rm test); then echo ok; fi
ok
Bah c’est justement ce que je dis, dans le premier cas la commande a un code retour ≠ 0 la condition est fausse alors que dans la deuxième le code retour est 0 est la condition est vrai :
≠ 0 implique faux
0 implique vrai
Donc c'est plus logique d'attribuer 0 à ok et !0 à toutes les situations non ok.
Oui mais quand on apprend la logique on note 0 = faux et 1 = vrai, c’est le contraire en shell.
Dans les autres langages de programmation on également plutôt l’habitude qu’une valeur différente de zéro soit évaluée vraie et zéro soit évalué faux.
Ça t’évite de finir par devoir retaper deux fois des commandes quand tu te retrouves sur un AIX qui ne connaît que more, parce que tes doigts tapent less plus vite que ton cerveau ne réfléchit ^^
D’après le man de more:
Cette version est particulièrement primitive. Les utilisateurs doivent se convaincre que
less(1) constitue une excellente émulation de more(1) avec en plus de nombreuses améliorations.
Le fait que ce programme soit toujours disponible ne serait-il pas lié à la norme POSIX ?
Entre le programme true, et le programme [ , je trouve que le shell c'est de la bidouille …
C’est horrible ce [… genre if [ -z $v ]; then… ça ressemble à quoi ? :) On peut même ôter le crochet fermant qui n’est autorisé par le langage que pour la seule bonne raison qu’un crochet ouvert qui reste ouvert c’est très grave… presque autant qu’une parenthèse !
Maintenant j’essaie d’écrire : if test -z $v; then… c’est bien ce qu’il y a de plus logique et clair à lire selon moi.
Oui le shell c’est spécial comme « langage de programmation » vu que ce n’en ai pas vraiment un.
En m’y mettant j’ai eu un peu de mal avec la logique binaire… On a l’habitude de considérer que 1 représente vrai et 0 représente faux, or le code retour des programmes c’est 0 pour OK, par convention. Si je fais un if (rm toto) et que la commande « plante » (parce que le fichier toto n’existe pas par exemple) ma condition sera fausse.
L’utilisation des opérateurs && et || entre plusieurs commandes permet souvent d’être concis et élégant, je trouve.
# Ça avance
Posté par Marotte ⛧ . En réponse au journal Analyse de texte. Évalué à 2.
À la base j’aurais pas dû poster ça dans un journal mais dans un forum. Le fait que ce soit un journal m’incite à continuer à poster mon code…
Voici donc la dernière version de Reader.py, script dont le but est de prendre n’importe quel fichier texte en entrée, le découper en « phrases » et stocker la fréquence d’apparition de ces « phrases » dans une base SQLite.
La sortie en partant d’une base inexistante et en la renseignant à partir de classiques de la littérature française, posts du bouchot et contenu issu de Wikipédia (histoire d’avoir des entrées assez hétérogènes) :
On peut voir que l’intégration du contenu Wikipédia amène à certains biais (et c’est pas Me Soleil qui s’en plaindra…) mais que « Ah ! » et « Oh ! » restent des valeurs sûres :
Vous pouvez reprendre une activité normale.
[^] # Re: Version 0.3
Posté par Marotte ⛧ . En réponse au journal Analyse de texte. Évalué à 2.
En modifiant un peu le code voilà ce que ça donne sur un extrait de bouchot standard :
Ça donne des informations intéressantes sur les domaines des liens POSTé :)
[^] # Re: Version 0.3
Posté par Marotte ⛧ . En réponse au journal Analyse de texte. Évalué à 2. Dernière modification le 22 décembre 2015 à 00:06.
Je devrais je pense créer un re.reset pour celle là…
[^] # Re: Version 0.3
Posté par Marotte ⛧ . En réponse au journal Analyse de texte. Évalué à 2.
J’ai zappé le time :/
# Version 0.3
Posté par Marotte ⛧ . En réponse au journal Analyse de texte. Évalué à 2. Dernière modification le 21 décembre 2015 à 23:36.
Merci encore pour toutes vos suggestions. Il faut souligner qu’elles étaient toutes plutôt pertinentes… J’ai pu appliquer beaucoup de vos conseils même si j’ai dû abandonner certains (je pense notamment au islice du module itertools, qui est exactement ce que je cherchais à faire…) parce que je suis parti sur d’autres types de variables et une nouvelle approche du problème.
D’une point de vue fonctionnel je désire ne plus saucissoner l’input à l’arrache mais essayer de découper sur les phrases en me basant sur les règles typographiques (une phrase finit par un point, '«' ouvre un dialogue (ou pas…), etc…). Ça fait nettement moins d’enregistrements en base !
D’un point de vue technique j’ai utilisé une classe afin que le code principal soit plus clair.
Donc voici le code, j’ai viré les commentaires inutiles et il est de toute façon plus concis.
D’un point de vu performance je trouve que ça tourne pas mal (à partir d’une base inexistante) :
Les fichiers sont des classiques de la littérature issus de sites comme http://www.ebooksgratuits.com/ (principalement), convertis from EPUB par un script choppé sur github. Les fichiers ayant été ensuite légèrement toilettés à la main est au grep avant traitement… Ça représente 6,6M de texte UTF-8.
Voici le résultat de quelques requêtes effectuées sur la base suite au traitement de ces fichiers :
J’ai déjà noté que je devrais appliquer mon expression régulière "discard" dans la fonction read() plutôt qu’au niveau store()…
[^] # Re: Tout faire en base
Posté par Marotte ⛧ . En réponse au journal Analyse de texte. Évalué à 2.
Ce qu’il dit, je pense, c’est que les gains apportés par le fait de faire le plus possible de chose en SQL (et je dois dire qu’on peut vraiment faire un tas de trucs insoupçonnés dans les différentes variantes SQL…), par le moteur SQL, ça prends plus de sens pour un SGBD client/serveur… ça évite des « aller-retours »…
[^] # Re: En memoire
Posté par Marotte ⛧ . En réponse au journal Analyse de texte. Évalué à 2.
Pour l’instant.
Entendons nous bien, ce n’est pas pour ce caractère que j’ai pris SQLite. En plus des raisons déjà évoquées c’est que MariaDB je connais pas mal alors que SQLite je découvre un peu.
Ou encore BerkeleyDB. Mais je ne veux pas me limiter à un système clé-valeur pour les autres fonctionnalités que j’ai en tête. Je compte utiliser les tables virtuelles avec recherche de texte (FTS), par exemple.
[^] # Re: En memoire
Posté par Marotte ⛧ . En réponse au journal Analyse de texte. Évalué à 2.
Oui… quand on parcourt toutes les combinaisons possibles rien qu’entre caractères lus ça va vite très vite.
Pour la mémoire j’ai abandonné le deque pour stocker mon tampon, le problème étant qu’un deque n’est pas adressable à l’aide de slices ([:4])… contrairement à un string. J’ai bien profité du conseil concernant collections.Counter(), c’est vraiment le type de variable dont j’avais besoin.
J’ai un peu changé d’approche pour conserver les « strings » (sentences), je me base sur des expressions régulières, c’est plus propre. Dès que j’ai un truc correct je le postici.
Alors là attention, que ça puisse fonctionner plus ou ou moins vite sous MariaDB c’est clairement pas un angle d’étude pour le moment… J’utilise sqlite parce que c’est parfaitement adapté à la modélisation (et c’est par ailleurs l’un des meilleurs moteur opensource de base relationnelle). SQLite ça marche partout. sqlite passe le test ACID : https://fr.wikipedia.org/wiki/Propri%C3%A9t%C3%A9s_ACID je n’en suis pas si sûr pour MarioleDB…
[^] # Re: I like =
Posté par Marotte ⛧ . En réponse au journal Analyse de texte. Évalué à 2.
Je viens de tester avec
=
à la place delike
les performances sont bien meilleures effectivement :)Merci à vous deux. Le temps d’exécution est passé de 15 min à 40 s (environ)…
Pour le nombre de chaque requête je ne sais pas encore, je garde en tête ta remarque très judicieuse sur l’ordre d’exécution.
[^] # Re: Je ne ferais pas ça comme ça.
Posté par Marotte ⛧ . En réponse au journal Analyse de texte. Évalué à 2.
En incluant les suites telles que "a ma" je peux toujours par la suite filtrer avec une expression régulière pour n’avoir que les mots.
[^] # Re: occurrence des mots
Posté par Marotte ⛧ . En réponse au journal Analyse de texte. Évalué à 3.
C’est volontaire de vouloir prendre les signes de ponctuation…
Pour moi 'mice' et 'mice.' ne sont pas deux chaînes identiques. De la deuxième on peut présager que le mot « mice » a des chances de terminer une phrase. À part le remplacement des retour à la ligne par des espaces puis la réduction des espaces consécutives à une seule espace je travaille en mode suite de caractères.
[^] # Re: Je ne ferais pas ça comme ça.
Posté par Marotte ⛧ . En réponse au journal Analyse de texte. Évalué à 3.
Oui c’est ça. N’importe quel groupe de caractères en fait.
Y’en a pas vraiment… je fais des essais on va dire…
[^] # Re: On dirait du C
Posté par Marotte ⛧ . En réponse au journal Analyse de texte. Évalué à 2.
Merci pour tous ces conseils avisés ! Surtout pour le collections.Counter je pense que ça va me servir…
Ma ligne peut avoir une longueur variable. J’aurais toujours besoin de la traiter en morceau de N caractères car je ne vais pas itérer sur tous les caractères de la ligne si je n’ai pas besoin de conserver des chaînes de plus de N caractères. J’ai également besoin d’avoir la fréquence de chaînes telles que "titi\ntoto" je devrais donc conserver un certain nombre de caractères de la ligne précédente à chaque fois…
J’ai trouvé plus simple d’avoir une sorte de « fenêtre » (buf) qui va « glisser » sur mon entrée… Par exemple si je prends un MAXLEN de 2 et que mon entrée est "abcd\n" j’aurais tour à tour :
Avec MAXLEN à 3 j’aurais, en plus :
Non, j’ai bien conscience que ce n’est pas à moi de gérer la mémoire.
[^] # Re: Je ne ferais pas ça comme ça.
Posté par Marotte ⛧ . En réponse au journal Analyse de texte. Évalué à 3.
Je le vois simplement grâce au
sys.stderr.write(…)
qui est fait entre chaque étape, la première étape (chargement) et la deuxième (suppression des chaînes qui coupent des mots en deux, sauf les petites…) s’exécutent en un temps acceptable.En faisant ça si j’ai une première ligne qui contient "voici une première ligne" et une seconde qui contient "et là une deuxième" je n’aurais pas d’entrée créée contenant le dernier mot de la première et le premier de la deuxième ("ligne et"). Je n’aurais pas non plus 'e e', 'e et', etc… (je remplace systématiquement "\n" par "n"…)
J’utilise donc un tampon de MAXLEN caractères qui se décale de MAXLEN-1 caractères à chaque fois afin que le dernier caractère du précédent tampon se retrouve premier du tampon suivant. Pour ça un deque me semblait plus adapté qu’un string
Comme tu peux le voir le programme lit l’entrée standard, pour l’instant il attend EOF pour traiter les données mais à terme je veux qu’il puisse tourner en continue, en faisant un màj de la base tous les N tampons/caractères.
[^] # Re: nltk
Posté par Marotte ⛧ . En réponse au journal Analyse de texte. Évalué à 3.
Comme un sac de caractères même… :)
Je me pencherai sur NLTK un de ces jours ça a l’air bien mais là je veux une approche plus « bête », basée seulement sur les fréquences d’apparition.
[^] # Re: nltk
Posté par Marotte ⛧ . En réponse au journal Analyse de texte. Évalué à 2.
Très intéressant, merci.
[^] # Re: En memoire
Posté par Marotte ⛧ . En réponse au journal Analyse de texte. Évalué à 2.
Euh… bah non :)
Utiliser time() je sais faire mais je ne l’ai pas fait ici, je lance simplement le script précédé de la commande time. Vu la sortie d’erreur je sais à peu près combien de temps prend chaque étape. Je remarque nettement que ça va à une vitesse correct quand la base est vide (donc que les INSERT aboutissent).
Oui. Plus les textes passeront plus il aura de mises à jour et moins d’insertions.
C’est ce que je vais faire.
[^] # Re: on teste le seuil bas de moinsage ?
Posté par Marotte ⛧ . En réponse au journal La fin de la fin.. Évalué à 2.
En te moinssant je participe à 33% de ta note à moi tout seul, ça me console.
[^] # Re: DMP et SESAM-Vitale: des certifications aussi
Posté par Marotte ⛧ . En réponse au journal la fin des logiciels libres de comptabilité et de gestion de caisses. Évalué à 2. Dernière modification le 15 décembre 2015 à 02:01.
Je viens de tomber sur ton commentaire. Est-ce que par « trivial » tu ne voulais pas tout à fait signifier l’inverse ?
Parce que la définition de « trivial » c’est :
alors que « kits de developement four-tout avec de multiples versions des protocoles »
ça pue un peu la complexité…
http://www.larousse.fr/dictionnaires/francais/trivial
:)
# Erratum
Posté par Marotte ⛧ . En réponse au journal Analyse de texte. Évalué à 2.
Le premier commentaire est erroné :
s/~which don't match~/which match/
[^] # Re: false
Posté par Marotte ⛧ . En réponse au journal Le core utile. Évalué à 4.
Le genre de code qui va (si je n’y suis pas habitué pour une raison ou une autre) me nécessiter une heure pour rechercher sur le web et assimiler (c’est à dire accepter en son âme de codeur, profondément, que cela puisse pertinemment s’écrire ainsi…) avant de passer à la suite du code…
J’ai dit « concis && élégant », j’aurais du ajouter lisible pour le commun des programmeurs :)
Il faut reconnaître que l’« expansion de variable » en shell c’est une des caractéristiques de ce genre de langage et c’est rudement pratique.
[^] # Re: false
Posté par Marotte ⛧ . En réponse au journal Le core utile. Évalué à 2.
Bah c’est justement ce que je dis, dans le premier cas la commande a un code retour ≠ 0 la condition est fausse alors que dans la deuxième le code retour est 0 est la condition est vrai :
≠ 0 implique faux
0 implique vrai
Oui mais quand on apprend la logique on note 0 = faux et 1 = vrai, c’est le contraire en shell.
Dans les autres langages de programmation on également plutôt l’habitude qu’une valeur différente de zéro soit évaluée vraie et zéro soit évalué faux.
[^] # Re: Des oublis ?
Posté par Marotte ⛧ . En réponse au journal Le core utile. Évalué à 2.
Ça t’évite de finir par devoir retaper deux fois des commandes quand tu te retrouves sur un AIX qui ne connaît que more, parce que tes doigts tapent less plus vite que ton cerveau ne réfléchit
^^
D’après le man de more:
Le fait que ce programme soit toujours disponible ne serait-il pas lié à la norme POSIX ?
[^] # Re: false
Posté par Marotte ⛧ . En réponse au journal Le core utile. Évalué à 5.
C’est horrible ce
[
… genreif [ -z $v ]; then…
ça ressemble à quoi ? :) On peut même ôter le crochet fermant qui n’est autorisé par le langage que pour la seule bonne raison qu’un crochet ouvert qui reste ouvert c’est très grave… presque autant qu’une parenthèse !Maintenant j’essaie d’écrire :
if test -z $v; then…
c’est bien ce qu’il y a de plus logique et clair à lire selon moi.Oui le shell c’est spécial comme « langage de programmation » vu que ce n’en ai pas vraiment un.
En m’y mettant j’ai eu un peu de mal avec la logique binaire… On a l’habitude de considérer que 1 représente vrai et 0 représente faux, or le code retour des programmes c’est 0 pour OK, par convention. Si je fais un if (rm toto) et que la commande « plante » (parce que le fichier toto n’existe pas par exemple) ma condition sera fausse.
L’utilisation des opérateurs
&&
et||
entre plusieurs commandes permet souvent d’être concis et élégant, je trouve.# Mais que fait la police ?
Posté par Marotte ⛧ . En réponse au journal Tentative d'escroquerie téléphonique. Évalué à 0.
Elle protège et elle sert !
C’était sûrement l’auto-dialer (de Marge)… Ne t’inquiète pas c’est réglé.
https://www.youtube.com/watch?v=nbbIXMDGb_w