Files
anonymisation/scripts/build_noms_famille_gazetteer.py
Domi31tls c9572c383a feat(phase2): Fine-tuning CamemBERT-bio v2 (F1=0.90) + enrichissement données
- Fine-tuning camembert-bio-base : F1=0.903, Recall=0.930 (vs 0.89/0.85)
- Data augmentation : substitution noms INSEE (219K patronymes, x3 copies)
- Hard negatives BDPM (5.7K médicaments) + QUAERO (1319 termes médicaux)
- Annotations silver enrichies par gazetteers (+612 VILLE, +5 HOPITAL)
- Export silver avec support multi-répertoires (--extra-dir)
- Gazetteers QUAERO : CHEM, DISO, PROC, ANAT depuis DrBenchmark/QUAERO
- Gazetteers INSEE : noms de famille fréquents (96K) et complets (219K)
- Batch silver 1194 PDFs (run_batch_silver_export.py) pour dataset v3

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 02:06:08 +01:00

119 lines
4.0 KiB
Python

#!/usr/bin/env python3
"""
build_noms_famille_gazetteer.py
Construit deux gazetteers de noms de famille français à partir du fichier INSEE.
Source : /home/dom/ai/anonymisation/data/insee/noms2008nat_txt.txt
(TSV : NOM, puis effectifs par décennie 1891-2000)
Sorties dans /home/dom/ai/anonymisation/data/insee/ :
- noms_famille_france.txt : TOUS les noms (219K), un par ligne, majuscules
- noms_famille_frequents.txt : noms avec total >= 100 ET longueur >= 3 caractères
"""
import os
from collections import Counter
INPUT_FILE = "/home/dom/ai/anonymisation/data/insee/noms2008nat_txt.txt"
OUTPUT_DIR = "/home/dom/ai/anonymisation/data/insee"
OUTPUT_ALL = os.path.join(OUTPUT_DIR, "noms_famille_france.txt")
OUTPUT_FREQ = os.path.join(OUTPUT_DIR, "noms_famille_frequents.txt")
MIN_TOTAL = 100 # seuil de fréquence pour le fichier "fréquents"
MIN_LENGTH = 3 # longueur minimale pour le fichier "fréquents"
SKIP_NAMES = {"AUTRES NOMS"}
def main():
all_names = []
freq_names = []
frequencies = [] # pour les stats de distribution
with open(INPUT_FILE, "r", encoding="utf-8") as f:
header = f.readline() # skip header
print(f"En-tête : {header.strip()}")
for line in f:
line = line.strip()
if not line:
continue
parts = line.split("\t")
nom = parts[0].strip()
if nom in SKIP_NAMES:
continue
# Calculer la fréquence totale sur toutes les décennies
total = 0
for val in parts[1:]:
val = val.strip()
try:
total += int(val)
except ValueError:
pass
# Tous les noms (uppercase, déjà le cas dans le fichier)
nom_upper = nom.upper()
all_names.append(nom_upper)
frequencies.append(total)
# Filtre pour les noms fréquents
if total >= MIN_TOTAL and len(nom_upper) >= MIN_LENGTH:
freq_names.append(nom_upper)
# Écriture des fichiers
with open(OUTPUT_ALL, "w", encoding="utf-8") as f:
f.write("\n".join(all_names) + "\n")
with open(OUTPUT_FREQ, "w", encoding="utf-8") as f:
f.write("\n".join(freq_names) + "\n")
# --- Stats ---
print(f"\n{'='*60}")
print(f"STATISTIQUES")
print(f"{'='*60}")
print(f"Fichier source : {INPUT_FILE}")
print(f"Noms totaux lus : {len(all_names):>10,}")
print(f"{OUTPUT_ALL}")
print(f"Noms fréquents : {len(freq_names):>10,} (total >= {MIN_TOTAL}, len >= {MIN_LENGTH})")
print(f"{OUTPUT_FREQ}")
print(f"Noms exclus (filtre): {len(all_names) - len(freq_names):>10,}")
# Distribution de fréquence
print(f"\n--- Distribution des fréquences ---")
buckets = [
(0, 0, "0 (hapax)"),
(1, 9, "1-9"),
(10, 49, "10-49"),
(50, 99, "50-99"),
(100, 499, "100-499"),
(500, 999, "500-999"),
(1000, 4999, "1 000-4 999"),
(5000, 9999, "5 000-9 999"),
(10000, 49999, "10 000-49 999"),
(50000, 99999, "50 000-99 999"),
(100000, float("inf"), "100 000+"),
]
for lo, hi, label in buckets:
count = sum(1 for freq in frequencies if lo <= freq <= hi)
if count > 0:
print(f" {label:>20s} : {count:>8,} noms")
# Top 20
print(f"\n--- Top 20 noms les plus fréquents ---")
indexed = list(zip(all_names, frequencies))
indexed.sort(key=lambda x: x[1], reverse=True)
for i, (nom, freq) in enumerate(indexed[:20], 1):
print(f" {i:>2}. {nom:<25s} {freq:>10,}")
# Quelques stats globales
total_naissances = sum(frequencies)
print(f"\nTotal naissances couvertes : {total_naissances:>12,}")
print(f"Fréquence médiane : {sorted(frequencies)[len(frequencies)//2]:>12,}")
print(f"Fréquence moyenne : {total_naissances / len(frequencies):>12,.1f}")
if __name__ == "__main__":
main()