|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331 |
- <!doctype html>
- <html lang=fr>
- <head>
- <!-- Always define the charset before the title -->
- <meta charset=utf-8>
- <title>Nombre d'occurences d'un mot dans un texte en Python — Biologeek — David Larlet</title>
- <!-- Define a viewport to mobile devices to use - telling the browser to assume that the page is as wide as the device (width=device-width) and setting the initial page zoom level to be 1 (initial-scale=1.0) -->
- <meta name="viewport" content="width=device-width, initial-scale=1"/>
- <!-- Fake favicon, to avoid extra request to the server -->
- <link rel="icon" href="data:;base64,iVBORw0KGgo=">
- <link type="application/atom+xml" rel="alternate" title="Feed" href="/david/log/" />
- <link rel="manifest" href="/manifest.json">
-
- <link rel="stylesheet" href="/static/david/css/larlet-david-_J6Rv.css" data-instant-track />
-
- <noscript>
- <style type="text/css">
- /* Otherwise fonts are loaded by JS for faster initial rendering. See scripts at the bottom. */
- body {
- font-family: 'EquityTextB', serif;
- }
- h1, h2, h3, h4, h5, h6, time, nav a, nav a:link, nav a:visited {
- font-family: 'EquityCapsB', sans-serif;
- font-variant: normal;
- }
- </style>
- </noscript>
-
- <!-- Canonical URL for SEO purposes -->
- <link rel="canonical" href="https://larlet.fr/david/biologeek/archives/20081206-nombre-doccurences-dun-mot-dans-un-texte-en-python">
-
- </head>
- <body>
- <div>
-
- <header>
- <nav>
- <p>
- <small>
- Je suis <a href="/david/" title="Profil public">David Larlet</a>, <a href="/david/pro/" title="Activité professionnelle">artisan</a> du web qui vous <a href="/david/pro/accompagnement/" title="Activité d’accompagnement">accompagne</a><span class="more-infos"> dans l’acquisition de savoirs pour concevoir des <a href="/david/pro/produits-essentiels/" title="Qu’est-ce qu’un produit essentiel ?">produits essentiels</a></span>. <span class="more-more-infos">Discutons ensemble d’une <a href="/david/pro/devis/" title="En savoir plus">non-demande de devis</a>.</span> Je partage ici mes <a href="/david/blog/" title="Expériences bienveillantes">réflexions</a> et <a href="/david/correspondances/2017/" title="Lettres hebdomadaires">correspondances</a>.
- </small>
- </p>
- </nav>
- </header>
-
-
- <section>
- <h1 property="schema:name">Nombre d'occurences d'un mot dans un texte en Python</h1>
- <article typeof="schema:BlogPosting">
- <div property="schema:articleBody">
-
- <p>Juste un petit snippet car j'en ai eu besoin récemment pour faire des statistiques sur des termes recherchés et je pense que ça peut être utile :</p>
- <pre><code>from itertools import groupby
-
- def word_frequencies(content, blacklist):
- """
- Count the number of words in a content, excluding blacklisted terms.
- Return a generator of tuples (count, word) sorted by descending frequency.
-
- Example::
-
- >>> song = 'Ob la di ob la da "rla di da" da "da"'
- >>> for count, word in word_frequencies(song, ['di']):
- ... print "%s %s" % (count, word)
- ...
- 4 da
- 2 la
- 2 ob
- 1 rla
- """
- sorted_words = sorted(word \
- for word in content.lower().replace('"', '').split() \
- if word not in blacklist)
- return ((len(list(group)), word) for word, group in groupby(sorted_words))
-
- if __name__ == "__main__":
- import doctest
- doctest.testmod(verbose=True)
- </code></pre>
- <p>À adapter selon votre convenance, si vous avez mieux je suis preneur, comme toujours.</p>
- </div>
- </article>
- <footer>
- <h6 property="schema:datePublished">— 06/12/2008</h6>
- </footer>
- </section>
- <section>
- <div>
- <h3>Articles peut-être en rapport</h3>
- <ul>
- <li><a href="/david/biologeek/archives/20081204-la-documentation-django-en-local-html-et-pdf/" title="Accès à La documentation Django en local (html et pdf)">La documentation Django en local (html et pdf)</a></li>
- <li><a href="/david/biologeek/archives/20081203-interfaces-et-promotion-du-web-semantique/" title="Accès à Interfaces et promotion du Web Sémantique">Interfaces et promotion du Web Sémantique</a></li>
- <li><a href="/david/biologeek/archives/20081201-24ways-le-calendrier-de-lavant-des-geeks-web/" title="Accès à 24ways, le calendrier de l'Avent des geeks web">24ways, le calendrier de l'Avent des geeks web</a></li>
- </ul>
- </div>
- </section>
- <section>
- <div id="comments">
- <h3>Commentaires</h3>
-
- <div class="comment" typeof="schema:UserComments">
- <p class="comment-meta">
- <span class="comment-author" property="schema:creator">kim</span> le <span class="comment-date" property="schema:commentTime">06/12/2008</span> :
- </p>
- <div class="comment-content" property="schema:commentText">
- <p>en perl, on a droit ?</p>
-
- <p>(my $string = 'Ob la di ob la da "rla di da" da "da"' ) =~ s/"//g;<br />my %hash;<br />map { $hash{$_} = (defined $hash{$_}) ? 1 : $hash{$_} + 1; } (split(" ",$string);<br />map { print "$_ -> $hash{$_}\n"; } (keys %hash);</p>
- </div>
- </div>
- <div class="comment" typeof="schema:UserComments">
- <p class="comment-meta">
- <span class="comment-author" property="schema:creator">maxime</span> le <span class="comment-date" property="schema:commentTime">06/12/2008</span> :
- </p>
- <div class="comment-content" property="schema:commentText">
- <p>kim: voilà qui illustre bien la différence de lisibilité entre le python et le perl ;)</p>
- </div>
- </div>
- <div class="comment" typeof="schema:UserComments">
- <p class="comment-meta">
- <span class="comment-author" property="schema:creator">David, biologeek</span> le <span class="comment-date" property="schema:commentTime">06/12/2008</span> :
- </p>
- <div class="comment-content" property="schema:commentText">
- <p>@kim : tiens ça me fait penser que je pourrais effectivement virer les " dès le début. Ça me fait aussi penser que perl tiens bien sa réputation ;-).</p>
- </div>
- </div>
- <div class="comment" typeof="schema:UserComments">
- <p class="comment-meta">
- <span class="comment-author" property="schema:creator">kim</span> le <span class="comment-date" property="schema:commentTime">06/12/2008</span> :
- </p>
- <div class="comment-content" property="schema:commentText">
- <p>@tous les deux : en même temps, j'ai fait exprès de "compresser le code"... M'enfin je trouve le perl plus lisible que le python, question d'habitude :)<br />Cela dit je remarque que j'ai oublié le blacklist et le lowercase. Donc voilà pareil, en reprenant le principe de ton code python (sort + j'ai ajouté lc & blacklist), et sans hash remplie au fur et à mesure :</p>
-
- <p>(my $string = lc('Ob la di ob la da "rla di da" da "da"') ) =~ s/"//g;<br />my @string = sort(split(" ",$string));<br />my ($last,%hash) = ("",);<br />foreach @string {<br /> next if($last eq $_);<br /> next if(!grep {/^$_$/} @blacklist);<br /> $last = $_;<br /> $hash{$_} = scalar(grep { /^$_$/ } @string);<br />}<br /># un peu d'affichage si on veut :<br />#map { print "$_ -> $hash{$_}\n" } (keys %hash);<br />return %hash;</p>
-
- <p>Après, l'utilisation de sort, en perl comme en python, est discutable. Est-ce que trier puis faire un groupby est meilleur en temps & mémoire, par rapport à un stockage "au fil de la lecture" (donc, pas de sort). Dans ton exemple, le texte est court, quid de si on fait le même test sur le texte du code civil par exemple ? ça pourrait être intéressant de faire des tests sur le sujet ;) Je pense que ce genre de questions peut facilement rendre le code un moins "oneline" :)</p>
- </div>
- </div>
- <div class="comment" typeof="schema:UserComments">
- <p class="comment-meta">
- <span class="comment-author" property="schema:creator">Jdoe</span> le <span class="comment-date" property="schema:commentTime">07/12/2008</span> :
- </p>
- <div class="comment-content" property="schema:commentText">
- <p>@kim, même sans la compression c'est ingérable du code comme ça. dans 6 mois tu retournes dans ta fonction, il te faudra trop de temps pour savoir ce qu'elle fait.</p>
-
- <p>Avant j'aimais bien le perl mais qd on a goûté à la facilité du python, c'est comme une drogue.</p>
- </div>
- </div>
- <div class="comment" typeof="schema:UserComments">
- <p class="comment-meta">
- <span class="comment-author" property="schema:creator">Jean-Philippe Camguilhem</span> le <span class="comment-date" property="schema:commentTime">07/12/2008</span> :
- </p>
- <div class="comment-content" property="schema:commentText">
- <p>hum hum !</p>
-
- <p>Si je n'ai pas mal compris la doc string de ta fonction :</p>
-
- <p>"""<br />Compte le nombre d'ocuurences des mots contenus dans un texte (exceptés ceux présents dans la black list).</p>
-
- <p>Retourne un générateur de tuples classés par ordre décroissant de fréquences <br />"""</p>
-
- <p>Alors ton snipet est buggué dans le sens où il retourne les tuples classés par ordre alphabétique des mots, et non comme indiqué.</p>
-
- <p>Un heureux hasard, trompe le lecteur car ta chanson de départ contient 4 "da".</p>
-
- <p>remplace les par "za"</p>
-
- <p>>>> song = 'Ob la di ob la za "rla di za" za "za"'</p>
-
- <p>Failed example:<br /> for count, word in word_frequencies(song, ['di']):<br /> print "%s %s" % (count, word)<br />Expected:<br /> 4 za<br /> 2 la<br /> 2 ob<br /> 1 rla<br />Got:<br /> 2 la<br /> 2 ob<br /> 1 rla<br /> 4 za<br />1 items had no tests:<br /> __main__<br />**********************************************************************<br />1 items had failures:<br /> 1 of 2 in __main__.word_frequencies<br />2 tests in 2 items.<br />1 passed and 1 failed.<br />***Test Failed*** 1 failures.</p>
-
- <p>Je laisse aux autres lecteurs le soin de corriger le code à titre d'exercice :)</p>
-
- <p>@++</p>
- </div>
- </div>
- <div class="comment" typeof="schema:UserComments">
- <p class="comment-meta">
- <span class="comment-author" property="schema:creator">kim</span> le <span class="comment-date" property="schema:commentTime">07/12/2008</span> :
- </p>
- <div class="comment-content" property="schema:commentText">
- <p>@jdoe : bon on va éviter de rentrer dans le troll de bas étage :) mais même sans commentaire, je trouve le code lisible et maintenable. Après, si ce n'est qu'une question de commentaire, c'était voulu, David les avait mis dans son code, je vais pas les *rappeler*.</p>
-
- <p>J'ai curieusement fait exactement l'inverse de toi, j'aimais bien le perl, j'ai goûté au python pendant 6 mois dans le cadre d'un job, hé ben j'en suis revenu au perl pour trois raisons tout à fait subjectives :<br />* plus souple<br />* plus agréable<br />* on n'a pas à s'emmerder avec l'indentation. Et là, par contre, c'est une plaie à mon sens : j'ai récupéré du code modifié par deux personnes, on retrouvait des tabulations, des espaces, c'est super lourd à maintenir (sans compter les copier/coller qu'il faut systématiquement réindenter : le python *nécessite* un éditeur de texte adapté, ce qui n'est pas normal à mon sens).</p>
-
- <p>Après, je suis d'accord que "à lire", le python peut paraître plus agréable (bon, peut être pas dans l'exemple ci dessus parce qu'il se trouve que le code est *aussi* condensé et on arrive à deux lignes de code contenant au total une douzaine d'instruction...)</p>
- </div>
- </div>
- <div class="comment" typeof="schema:UserComments">
- <p class="comment-meta">
- <span class="comment-author" property="schema:creator">benoitc</span> le <span class="comment-date" property="schema:commentTime">07/12/2008</span> :
- </p>
- <div class="comment-content" property="schema:commentText">
- <p>en me e temps ce pourrait être plus somple en python :</p>
-
- <p>>>> songs = 'Ob la di ob la da "rla di da" da "da"'<br />>>> lsongs = [song.replace('"', '').lower() for song in songs.split()]<br />>>> freqs = [(- lsongs.count(song), song) for song in set(lsongs)]<br />>>> print "\n".join("%-10s : %s" % (n, -f) for f, n in sorted(freqs))<br />da : 4<br />di : 2<br />la : 2<br />ob : 2<br />rla : 1</p>
- </div>
- </div>
- <div class="comment" typeof="schema:UserComments">
- <p class="comment-meta">
- <span class="comment-author" property="schema:creator">benoitc</span> le <span class="comment-date" property="schema:commentTime">07/12/2008</span> :
- </p>
- <div class="comment-content" property="schema:commentText">
- <p>autre possibilité :</p>
-
- <p>>>> songs = 'Ob la di ob la da "rla di da" da "da"'<br />>>> lsongs = [song.replace('"', '').lower() for song in songs.split()]<br />>>> freqs = [lsongs.count(song) for song in lsongs]<br />>>> dict(zip(lsongs, freqs))<br />{'da': 4, 'di': 2, 'rla': 1, 'ob': 2, 'la': 2}</p>
-
- <p>perso dans ls 2 cas je trouve le python plus lisible que le perl .... les $_ me semblant pas naturel (surtout pour un français). Pour les badwords il suffit de mettre un son in songs.split() and not in badwords .</p>
- </div>
- </div>
- <div class="comment" typeof="schema:UserComments">
- <p class="comment-meta">
- <span class="comment-author" property="schema:creator">jean-Philippe Camguilhem</span> le <span class="comment-date" property="schema:commentTime">07/12/2008</span> :
- </p>
- <div class="comment-content" property="schema:commentText">
- <p>Bon benoitc n'a pas trainé pour corriger.</p>
-
- <p>J'aime beaucoup sa façon originale de trier de façon inverse en passant par des valeurs négatives dans sa première proposition.</p>
-
- <p>J'en profite cependant pour placer une méthode souvent méconnue pour les tris de listes à plusieurs dimensions.</p>
-
- <p>sorted tri sur le premier élément, mais on peut lui demander de trier sur un autre élément via operator :</p>
-
- <p>>>> import operator<br />>>> words_frequencies=(('za', 4), ('rla', 1), ('la', 2), ('ob', 2))<br />>>> print sorted(words_frequencies, key=operator.itemgetter(1), reverse=True)<br />[('za', 4), ('la', 2), ('ob', 2), ('rla', 1)]</p>
-
- <p>@++</p>
-
- <p>@kim pour connaître la réponse au match perl vs python<br />il faudra repasser vendredi sur cette url :)<br /><a href="http://jp.camguilhem.net/?user=kim&cool=perl&bad=python">http://jp.camguilhem.net/?user=kim&cool=perl&bad=python</a></p>
- </div>
- </div>
- <div class="comment" typeof="schema:UserComments">
- <p class="comment-meta">
- <span class="comment-author" property="schema:creator">benoitc</span> le <span class="comment-date" property="schema:commentTime">07/12/2008</span> :
- </p>
- <div class="comment-content" property="schema:commentText">
- <p>Je n'utilis epas svt operator, une autre solution si on veut trier :</p>
-
- <p>>>> songs = 'Ob la di ob la da "rla di da" da "da"'<br />>>> lsongs = [song.replace('"', '').lower() for song in songs.split()]<br />>>> freqs = [lsongs.count(song) for song in set(lsongs)]<br />>>> a = zip(lsongs,freqs)<br />>>> a.sort(lambda a,b: cmp(a[1],b[1]))<br />>>> a<br />[('di', 1), ('la', 2), ('ob', 2), ('la', 2), ('ob', 4)]</p>
- </div>
- </div>
- <div class="comment" typeof="schema:UserComments">
- <p class="comment-meta">
- <span class="comment-author" property="schema:creator">benoitc</span> le <span class="comment-date" property="schema:commentTime">07/12/2008</span> :
- </p>
- <div class="comment-content" property="schema:commentText">
- <p>Bon suite à un question de Kael qui m'a titillé j'allais poser ici l'algo en erlang quand j'ai vu que mon dernier exemple était faux. Ceci est plus correct :</p>
-
- <p>>>> songs = 'Ob la di ob la da "rla di da" da "da"'<br />>>> lsongs = [song.replace('"', '').lower() for song in songs.split()]<br />>>> freqs = [lsongs.count(song) for song in lsongs]<br />>>> a = dict(zip(lsongs, freqs))<br />>>> a<br />{'da': 4, 'di': 2, 'rla': 1, 'ob': 2, 'la': 2}<br />>>> items = a.items()<br />>>> items.sort(lambda a, b: cmp(a[1], b[1]))<br />>>> items<br />[('rla', 1), ('di', 2), ('ob', 2), ('la', 2), ('da', 4)]</p>
-
- <p>et reverse pour l'inverse...</p>
-
- <p>En erlang cela donne :</p>
-
- <p>1> S = "Ob la di ob la da \"rla di da\" da \"da\"",<br />1> Map = lists:map(fun(Word) -> {string:to_lower(Word),1} end, string:tokens(S, " \"")),<br />1> Result = lists:foldl(fun({Word,_}, Dict) -><br />1> case dict:is_key(Word, Dict) of <br />1> true -> dict:store(Word, dict:fetch(Word, Dict) + 1, Dict);<br />1> false -> dict:store(Word, 1, Dict)<br />1> end<br />1> end, dict:new(), Map),<br />1> dict:fold(fun(Word, Freq, Acc) -> [{Word,Freq}|Acc] end, [], Result).<br />[{"da",4},{"rla",1},{"ob",2},{"di",2},{"la",2}]</p>
-
- <p><a href="http://friendpaste.com/Hy6KzphN">http://friendpaste.com/Hy6KzphN</a></p>
-
- <p>(mis sur friendpaste car ton système n'accepte pas les "+ 1" dans les posts!)</p>
-
- <p></p>
- </div>
- </div>
- <div class="comment" typeof="schema:UserComments">
- <p class="comment-meta">
- <span class="comment-author" property="schema:creator">scar</span> le <span class="comment-date" property="schema:commentTime">08/12/2008</span> :
- </p>
- <div class="comment-content" property="schema:commentText">
- <p>En php :<br /><?php<br />$song = 'Ob la di ob la da "rla di da" da "da"';<br />$res = array();<br />foreach (preg_split('/\W/i', $song) as $w) <br /> if(!empty($w)) ++$res[$w];<br />natsort($res);<br />var_export($res);<br />?><br />Résultat :<br />array (<br /> 'Ob' => 1,<br /> 'rla' => 1,<br /> 'ob' => 1,<br /> 'di' => 2,<br /> 'la' => 2,<br /> 'da' => 4,<br />)<br />Avec array_reverse($res, true) pour le résultat inverse.</p>
-
- <p>Après le choix du language, c'est bien souvent une question de goût :)</p>
- </div>
- </div>
- <div class="comment" typeof="schema:UserComments">
- <p class="comment-meta">
- <span class="comment-author" property="schema:creator">vincent rabah</span> le <span class="comment-date" property="schema:commentTime">11/12/2008</span> :
- </p>
- <div class="comment-content" property="schema:commentText">
- <p>Sous (L)unix il existe une commande qui est "wc" qui renvoie le nombre de mots d'un texte et "wc -l" qui renvoie le nombre de ligne d'un texte :)</p>
- </div>
- </div>
- <div class="comment" typeof="schema:UserComments">
- <p class="comment-meta">
- <span class="comment-author" property="schema:creator">Olivier</span> le <span class="comment-date" property="schema:commentTime">16/12/2008</span> :
- </p>
- <div class="comment-content" property="schema:commentText">
- <p>Un minuscule détail: je ne mettrais pas de backslash du tout, si j'étais toi. C'est même recommandé dans la doc python: <a href="http://docs.python.org/howto/doanddont.html#using-backslash-to-continue-statements">http://docs.python.org/howto/doanddont.html#using-backslash-to-continue-statements</a></p>
- </div>
- </div>
- <div class="comment" typeof="schema:UserComments">
- <p class="comment-meta">
- <span class="comment-author" property="schema:creator">DecIRC</span> le <span class="comment-date" property="schema:commentTime">18/12/2008</span> :
- </p>
- <div class="comment-content" property="schema:commentText">
- <p>@vincent : un peu simpliste, non ? On demande pas le nombre de mots du texte mais le nombre d'occurences de chaque mot.</p>
-
- <p>cEd</p>
- </div>
- </div>
- <div class="comment" typeof="schema:UserComments">
- <p class="comment-meta">
- <span class="comment-author" property="schema:creator">benoitc</span> le <span class="comment-date" property="schema:commentTime">21/12/2008</span> :
- </p>
- <div class="comment-content" property="schema:commentText">
- <p>oui en shell ce serait plutôt :</p>
-
- <p>sed -e 's/\.//g' -e 's/\,//g' -e 's/ /\<br />/g' /filepath | tr 'A-Z' 'a-z' | sort | uniq -c | sort -nr</p>
- </div>
- </div>
-
- </div>
- </section>
-
-
- <footer>
- <nav>
- <p>
- <small>
- Je réponds quasiment toujours aux <a href="mailto:david%40larlet.fr" title="Envoyer un email">emails</a> (<a href="/david/signature/" title="Ma signature actuelle avec possibilité de chiffrement">signés</a>) et vous pouvez me rencontrer à Montréal. <span class="more-infos">N’hésitez pas à <a href="/david/log/" title="Être tenu informé des mises à jour">vous abonner</a> pour être tenu informé des publications récentes.</span>
- </small>
- </p>
- </nav>
- </footer>
-
- </div>
-
- <script src="/static/david/js/larlet-david-3ee43f.js" data-no-instant></script>
-
- <script data-no-instant>InstantClick.init()</script>
-
- </body>
- </html>
|