book-generator-orgmode/analyse_frequence_mots.py

181 lines
6.9 KiB
Python
Raw Normal View History

2025-08-30 18:57:27 +02:00
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Script pour analyser la fréquence des mots dans un fichier livre.org
et générer un nuage de mots coloré en tons pastels.
Ce script:
1. Lit le fichier livre.org
2. Extrait le texte en ignorant les métadonnées et les commentaires
3. Compte la fréquence des mots
4. Sauvegarde les 500 mots les plus fréquents dans un fichier CSV
5. Génère un nuage de mots en SVG et PNG avec des couleurs pastel aléatoires
"""
import re
import os
import csv
import random
import argparse
import numpy as np
import matplotlib.pyplot as plt
from collections import Counter
from wordcloud import WordCloud
import matplotlib.colors as mcolors
# Définir les arguments en ligne de commande
parser = argparse.ArgumentParser(description='Analyser la fréquence des mots dans un fichier Org-mode.')
parser.add_argument('dossier', nargs='?', help='Le chemin du dossier contenant le fichier livre.org. Si aucun dossier n\'est spécifié, le dossier courant sera utilisé.', default=os.getcwd())
args = parser.parse_args()
# Chemin vers le fichier livre.org
fichier_livre = f"{args.dossier}/livre.org"
# Liste des mots vides (stopwords) en français
stopwords = set([
"le", "la", "les", "un", "une", "des", "du", "de", "d'", "l'", "et", "ou", "",
"à", "au", "aux", "ce", "ces", "cette", "cet", "il", "ils", "elle", "elles",
"nous", "vous", "je", "tu", "on", "son", "sa", "ses", "leur", "leurs", "mon",
"ma", "mes", "ton", "ta", "tes", "que", "qui", "quoi", "dont", "pour", "par",
"dans", "sur", "sous", "avec", "sans", "en", "y", "est", "sont", "était",
"étaient", "sera", "seront", "a", "ont", "avait", "avaient", "aura", "auront",
"plus", "moins", "très", "peu", "beaucoup", "trop", "pas", "ne", "n'", "si",
"comme", "mais", "ou", "et", "donc", "car", "quand", "lorsque", "puis", "ensuite",
"alors", "ainsi", "aussi", "même", "tout", "tous", "toute", "toutes", "autre",
"autres", "certain", "certains", "certaine", "certaines", "tel", "tels", "telle",
"telles", "ceci", "cela", "ça", "c'", "s'", "d'", "l'", "qu'", "n'", "m'", "t'",
"se", "me", "te", "lui", "leur", "y", "en", "", "ici", "voici", "voilà", "ci",
"", "cet", "cette", "ces", "celui", "celle", "ceux", "celles", "celui-ci",
"celle-ci", "ceux-ci", "celles-ci", "celui-là", "celle-là", "ceux-là", "celles-là"
])
def generate_pastel_color():
"""Génère une couleur pastel aléatoire en format RGB."""
# NOTE: PIL's ImageDraw requires integer RGB values (0-255), not floats.
# La version précédente retournait des valeurs flottantes entre 0.6 et 0.9,
# ce qui causait une erreur: 'float' object cannot be interpreted as an integer
# Générer des valeurs RGB entre 153 et 229 (0.6*255 et 0.9*255) pour obtenir des tons pastels
# tout en convertissant en entiers pour la compatibilité avec PIL
r = int(random.uniform(0.6, 0.9) * 255)
g = int(random.uniform(0.6, 0.9) * 255)
b = int(random.uniform(0.6, 0.9) * 255)
return (r, g, b)
def extract_text_from_org(file_path):
"""
Extrait le texte d'un fichier org-mode en ignorant les métadonnées et les commentaires.
"""
with open(file_path, 'r', encoding='utf-8') as file:
content = file.read()
# Supprimer les blocs de commentaires
content = re.sub(r'#\+begin_comment.*?#\+end_comment', '', content, flags=re.DOTALL | re.IGNORECASE)
# Supprimer les lignes de métadonnées (commençant par #+)
content = re.sub(r'^\s*#\+.*$', '', content, flags=re.MULTILINE)
# Supprimer les lignes de propriétés
content = re.sub(r'^\s*:.*:.*$', '', content, flags=re.MULTILINE)
# Supprimer les titres de chapitres (lignes commençant par * ou **)
content = re.sub(r'^\s*\*+.*$', '', content, flags=re.MULTILINE)
# Supprimer les liens org-mode [[...][...]] et [[...]]
content = re.sub(r'\[\[.*?\]\](?:\[.*?\])?', '', content)
# Supprimer les caractères spéciaux et la ponctuation
content = re.sub(r'[^\w\s]', ' ', content)
# Convertir en minuscules
content = content.lower()
return content
def count_word_frequencies(text):
"""
Compte la fréquence des mots dans un texte.
Ignore les mots vides (stopwords) et les mots de moins de 3 caractères.
"""
# Diviser le texte en mots
words = re.findall(r'\b\w+\b', text)
# Filtrer les mots courts et les stopwords
filtered_words = [word for word in words if len(word) >= 3 and word not in stopwords]
# Compter les fréquences
word_counts = Counter(filtered_words)
return word_counts
def save_to_csv(word_counts, output_path, limit=500):
"""
Sauvegarde les mots les plus fréquents dans un fichier CSV.
"""
# Obtenir les mots les plus fréquents
most_common = word_counts.most_common(limit)
# Écrire dans le fichier CSV
with open(output_path, 'w', newline='', encoding='utf-8') as csvfile:
writer = csv.writer(csvfile)
writer.writerow(['Mot', 'Fréquence'])
for word, count in most_common:
writer.writerow([word, count])
print(f"Les {limit} mots les plus fréquents ont été sauvegardés dans {output_path}")
def generate_wordcloud(word_counts, output_svg, output_png):
"""
Génère un nuage de mots en SVG et PNG avec des couleurs pastel aléatoires.
"""
# Fonction pour attribuer des couleurs pastel aléatoires aux mots
def color_func(word, font_size, position, orientation, random_state=None, **kwargs):
return generate_pastel_color()
# Créer le nuage de mots
wordcloud = WordCloud(
width=1200,
height=800,
background_color='white',
max_words=500,
color_func=color_func,
prefer_horizontal=0.9,
relative_scaling=0.5
).generate_from_frequencies(word_counts)
# Sauvegarder en SVG
plt.figure(figsize=(12, 8), dpi=300)
plt.imshow(wordcloud, interpolation='bilinear')
plt.axis('off')
plt.tight_layout(pad=0)
plt.savefig(output_svg, format='svg', bbox_inches='tight')
# Sauvegarder en PNG
plt.savefig(output_png, format='png', bbox_inches='tight')
plt.close()
print(f"Nuage de mots sauvegardé en SVG: {output_svg}")
print(f"Nuage de mots sauvegardé en PNG: {output_png}")
def main():
# Extraire le texte du fichier livre.org
print(f"Analyse du fichier: {fichier_livre}")
text = extract_text_from_org(fichier_livre)
# Compter les fréquences des mots
word_counts = count_word_frequencies(text)
# Définir les chemins de sortie
csv_output = f"{args.dossier}/frequence_mots_top500.csv"
svg_output = f"{args.dossier}/nuage_mots.svg"
png_output = f"{args.dossier}/nuage_mots.png"
# Sauvegarder les résultats
save_to_csv(word_counts, csv_output)
generate_wordcloud(word_counts, svg_output, png_output)
print("Analyse de fréquence des mots terminée avec succès!")
if __name__ == "__main__":
main()