• # Ça avance

    Posté par . 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.

    #!/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"
    import sys
    import re
    import sqlite3
    from collections import Counter
    class Reader:
     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 database
     def read(self,f):
     """Read input and populate `sentences`"""
     while True:
     c = f.read(1)
     if not c:
     break
     if c == '\xa0':
     c = ' '
     self.characters[c] += 1 
     self.sb += c
     if self.sb in [' ','\n','»']:
     self.sb = '' 
     continue
     if self.discard.match(self.sb):
     self.sb = ''
     continue
     if self.start.match(self.sb):
     continue
     if self.stop.match(self.sb) and not self.cancel_stop.match(self.sb):
     if self.correct_tiret.match(self.sb):
     self.sb = self.sb.replace('-','–') 
     self.sentences[self.sb.strip()] += 1
     self.sb = ''
     continue
     for c in self.characters.most_common():
     self.signature += c[0]
     self.sig96 = self.signature[:96].replace('\n',' ') 
     def store(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 database
     for s in self.sentences.most_common():
     try:
     cursor.execute("insert into sentences values (?, ?)",(s[0], s[1]))
     self.nb_insert += 1
     except sqlite3.IntegrityError:
     self.nb_update += 1
     cursor.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()
    except KeyboardInterrupt as error:
     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
    

    Vous pouvez reprendre une activité normale.