EPIC #4960 — Volet C, strate 6 — banc de recollement (issue #12227). Contexte : la taxonomie Argumentum (1408 sophismes canoniques, ~18 400 assertions d’annotation dont les AIF attack) se lit depuis au moins deux substances runtime-usables distinctes — l’OWL2/XML et le CSV canonique. Ce notebook les confronte, expose leur incompatibilité mesurée et matérialise un graphe de transformations.
Objectifs d’apprentissage :
Identifier N lectures spécialistes réellement distinctes d’une même taxonomie (pas le même appel LLM avec six prompts cosmétiques). Ici : OWL2/XML (parseur regex tolerant) vs CSV canonique (pandas, structure plate 104 colonnes).
Mesurer l’incompatibilité deux à deux entre ces lectures : intersection, différence symétrique, dispersion par famille.
Exhiber une paire incompatible au sens fort — un sophisme présent dans une lecture et strictement absent dans l’autre — comme témoin que les deux lectures ne sont pas réductibles.
Construire un graphe de transformations entre les lectures : exploite / répond / répare / neutralise, avec quantification.
Trois exercices stub (règle #2161) — application à un sous-ensemble ciblé.
Sommaire :
Contexte : pourquoi deux lectures et pas une
Lecture 1 — OWL2/XML (parseur regex tolerant)
Lecture 2 — CSV canonique (pandas)
Incompatibilité deux à deux : intersection, différence, dispersion par famille
Témoin : une paire incompatible au sens fort
Graphe de transformations entre lectures
Exercices (3 stubs #2161)
Ponts avec la série Argument_Analysis
Statut runtime : aucune JVM, aucun Tweety, aucun OWLSharp requis. Tout le notebook tourne sur regex + pandas (Python stdlib + pandas + re). Le port C#/.NET verbatim CoursIA-OwlAdapter/ est conservé comme référence pour future intégration SKOS côté .NET, mais l’expérience pédagogique de ce notebook ne l’exige pas — c’est précisément la vertu du recoupement : plusieurs substances runtime-usables pour un même patrimoine technique.
1. Contexte : pourquoi deux lectures et pas une
Une taxonomie Argumentum peut être lue depuis plusieurs substances, c’est-à-dire des fichiers physiquement distincts qui prétendent encoder la même ontologie de référence :
Substance 1 — OWL2/XML (ontologies/argumentum_fallacies.owl, ~6,8 Mo) : encodage formel pour moteurs RDF (rdflib échoue au parse ; owlready2 charge mais expose 0 classe via l’API Python). Parseur regex tolerant = chemin robuste d’extraction de la structure.
Chacune est une lecture spécialiste au sens où elle encode le patrimoine avec ses propres conventions (AnnotationAssertion-resource vs colonne plate text_fr). Ce qui compte, c’est que les deux lectures sont runtime-usables sans dépendance externe lourde (ici : regex + pandas). C’est un recoupement matériel : la même ontologie apparaît sous deux formes textuelles distinctes, et le notebook les fait dialoguer.
Le témoin de l’incompatibilité, c’est précisément ce qui sort de cette confrontation — un sophisme qu’une lecture voit et l’autre non, ou avec un label incohérent. C’est ce qui justifie l’étiquette « strate 6 » : on ne lit plus un seul fichier, on lit deux substances et on gère leur écart.
from pathlib import Pathimport re, timeROOT = Path.cwd()OWL_PATH = ROOT /"ontologies"/"argumentum_fallacies.owl"print(f"Substance 1 (OWL2/XML) : {OWL_PATH.name}")print(f" Existe : {OWL_PATH.exists()}, taille = {OWL_PATH.stat().st_size:,} octets")print(f" Moteur de lecture : parseur regex tolerant (rdflib/owlready2 indisponibles)")print()owl_text = OWL_PATH.read_text(encoding="utf-8")print(f" {len(owl_text):,} caracteres, {owl_text.count(chr(10)) +1:,} lignes")print(f" Estim. temps de parse : < 5 s (regex sur {owl_text.count(chr(10)) +1:,} lignes, pas de chargement DOM complet)")
Substance 1 (OWL2/XML) : argumentum_fallacies.owl
Existe : True, taille = 6,791,708 octets
Moteur de lecture : parseur regex tolerant (rdflib/owlready2 indisponibles)
6,668,060 caracteres, 107,895 lignes
Estim. temps de parse : < 5 s (regex sur 107,895 lignes, pas de chargement DOM complet)
Lecture : la substance 1 (OWL2/XML) est physiquement disponible (~6,8 Mo). Le moteur de lecture retenu est le parseur regex tolerant : rdflib échoue au parse (dialecte OWL2/XML avec annotations-resource non standard) et owlready2 charge le fichier mais expose 0 classe via l’API Python. La complexité temporelle est dominée par le scan regex sur ~108 K lignes — quelques secondes en pratique (mesurées plus bas), pas un chargement DOM ou un raisonneur.
Lecture : la substance OWL2/XML est correctement extraite par les quatre regex (Class, ObjectProperty, prefLabel, AnnotationAssertion-resource). Les chiffres exacts apparaîtront dans l’output ci-dessous — on les note ici à titre indicatif, structurellement constants d’un parse à l’autre (cf notebook frère Argumentation-Onto-01-AIF-OWL2-Python.ipynb qui mesure 1 509 concepts Argumentum + 4 classes AIF, 10 ObjectProperty, 2 816 prefLabel, AnnotationAssertion-resource sur la même substance (7 825 à l’état courant, recalculé à chaque exécution)) ; ce notebook les reproduit pour fonder la mesure de compatibilité avec la lecture CSV.
# --- Construction d'une table de concepts OWL canoniques (localname -> (iri, label_fr)) ---def localname(iri: str) ->str: s = iri.rstrip("#").split("#")[-1].split("/")[-1]return s or"ROOT"arg_only = [c for c in classes if"argumentum_fallacies"in c]arg_locals =sorted({localname(c) for c in arg_only})print(f"Concepts Argumentum (owl:Class) : {len(arg_only)} distincts par localname : {len(arg_locals)}")taxonomy_owl = {local: {"iri": None, "fr": None, "en": None} for local in arg_locals}for iri in arg_only: loc = localname(iri) taxonomy_owl[loc]["iri"] = irifor iri, lang, label in pref_matches:if"argumentum_fallacies"notin iri:continue loc = localname(iri)if loc in taxonomy_owl and label: key ="fr"if lang and lang.upper() =="FR"else ("en"if lang and lang.upper() =="EN"elseNone)if key andnot taxonomy_owl[loc].get(key): taxonomy_owl[loc][key] = labelprint(f"Taxonomie OWL chargee : {len(taxonomy_owl):,} sophismes distincts") # IRI unique + prefLabel FR/ENsample =sorted([k for k in taxonomy_owl if taxonomy_owl[k]["fr"]])[:5]for k in sample:print(f" {k:30s} FR={taxonomy_owl[k]['fr']!r} EN={taxonomy_owl[k]['en']!r}")
Concepts Argumentum (owl:Class) : 1509 distincts par localname : 1406
Taxonomie OWL chargee : 1,406 sophismes distincts
3MenMakeATiger FR='Trois hommes font un tigre' EN='3 men make a tiger'
PeerPressure FR='Pression des pairs' EN='Drinking the Kool-Aid. / Peer pressure'
ableism FR='Capacitisme' EN='Ableism'
absentmindedness FR='Distraction' EN='Absent-mindedness'
absurd FR='Absurde' EN='Absurd'
Lecture : la lecture OWL est réduite à une table canonique{localname → {iri, fr, en}}, où localname = la dernière composante de l’IRI (par exemple semanticAmbiguity). Cette table est la projection « à plat » de l’ontologie : chaque sophisme devient une ligne, indépendamment de la richesse relationnelle (hiérarchie SKOS, crossLink, AIF). C’est exactement la forme que prendra le CSV dans la cellule suivante — la comparaison deux à deux sera possible au niveau de cette projection.
L’écart entre 1 509 classes Argumentum déclarées et 1 406 localnames distincts (103 doublons) reflète les concepts qui partagent un même localname mais ont des IRI distinctes (souvent par préfixe, par exemple argumentum_fallacies.owl# vs argumentum_virtues.owl#). Pour la jointure avec le CSV, on ne garde que les classes du namespace argumentum_fallacies, ce qui donne 1 406 — c’est le dénombrement opérationnel de la substance OWL face au CSV.
3. Lecture 2 — CSV canonique (pandas)
La substance 2 est l’encodage tabulaire canonique maintenu pour la Ludothèque et les générateurs de cartes. Il a 104 colonnes : PK technique, path decimal, profondeur, familles/sous-familles, noms vulgarisés FR + EN, descriptions, exemples, sources, dates, plus les métadonnées d’attaque AIF (AIF_attackType, AIF_attackedNode) issues du resync verbatim. Le moteur de lecture est pandas.read_csv (runtime pandas stdlib, aucune JVM).
La structure du CSV reflète une logique de catalogue éditorial (1 ligne par sophisme = 1 carte du jeu sérieux), là où l’OWL reflète une logique d’ontologie formelle (1 concept par sophisme + relations SKOS/crossLink/AIF). Les deux encodent la même substance-taxon mais avec des finalités différentes : le CSV se consomme humainement (édition, relecture, dédoublonnage éditorial), l’OWL se consomme par des raisonneurs RDF. C’est précisément la valeur de la confrontation deux à deux : voir ce que l’un encode que l’autre ne sait pas encoder.
import pandas as pdCSV_PATH = ROOT /"data"/"argumentum_fallacies_taxonomy.csv"print(f"Substance 2 (CSV canonique) : {CSV_PATH.name}")print(f" Existe : {CSV_PATH.exists()}, taille = {CSV_PATH.stat().st_size:,} octets")print()t0 = time.perf_counter()df = pd.read_csv(CSV_PATH, encoding="utf-8-sig")t1 = time.perf_counter()print(f" Charge en {t1 - t0:.2f}s.")print(f" Shape : {df.shape[0]:,} lignes x {df.shape[1]:,} colonnes")print(f" Moteur : pandas (read_csv), 0 JVM, 0 RDF")print()print(f" Colonnes cles : PK, Famille, Sous-Famille, nom_vulgarisé, Latin, text_fr, desc_fr, example_fr")print(f" Memes colonnes equivalentes cote OWL : iri (-> PK), broader (-> Famille), prefLabel FR (-> nom_vulgarisé ou text_fr), prefLabel EN (-> Latin).")
Substance 2 (CSV canonique) : argumentum_fallacies_taxonomy.csv
Existe : True, taille = 4,144,860 octets
Charge en 0.13s.
Shape : 1,408 lignes x 104 colonnes
Moteur : pandas (read_csv), 0 JVM, 0 RDF
Colonnes cles : PK, Famille, Sous-Famille, nom_vulgarisé, Latin, text_fr, desc_fr, example_fr
Memes colonnes equivalentes cote OWL : iri (-> PK), broader (-> Famille), prefLabel FR (-> nom_vulgarisé ou text_fr), prefLabel EN (-> Latin).
Lecture : la substance 2 (CSV canonique) est physique et bien typée. pandas.read_csv la charge en sub-second, quelle que soit sa taille (taille et durée mesurées dans la sortie ci-dessus). Les colonnes clés sont Famille / Sous-Famille (équivalent de la hiérarchie SKOS dans l’OWL) et nom_vulgarisé / text_fr (équivalent de prefLabel FR dans l’OWL). La colonne Latin (présente dans le CSV) n’a pas d’équivalent direct dans l’OWL — c’est déjà un premier signal d’asymétrie entre les deux lectures.
import re as _re# --- Construction d'une projection CSV en table {localname -> champs} ---# NOTE METHODOLOGIQUE IMPORTANTE : la colonne "Latin" du CSV canonique n'est# peuplee que pour ~25 entrees (latinis -- AdHominem, AdPopulum, AdCoelum, etc.).# Ce n'est PAS une cle de jointure universelle. On construit donc la table# indexee par PK (entier canonique) plutot que par localname, et on construit# ENSUITE une deuxieme projection {label_fr_normalise -> champs} pour permettre# une jointure semantique avec l'OWL (qui n'a pas de PK -- il a un IRI).column_local ="Latin"# Cle de jointure tentee mais tres incomplete (25/1 408 mesure).column_fr ="text_fr"column_en ="text_en"# Le CSV canonique n'a pas de colonne "English" -- il suit la convention text_<lang> (8 langues au total : fr, en, ru, pt, ar, es, zh, fa).column_family ="Famille"column_subfam ="Sous-Famille"def _s(x):if x isNone:return""ifisinstance(x, float):return""if pd.isna(x) elsestr(x) s =str(x)return""if s =="nan"else s.strip()# Table indexee par PK (cle universelle CSV).taxonomy_csv = {}for _, row in df.iterrows(): pk =int(row["PK"]) if pd.notna(row["PK"]) elseNoneif pk isNone:continue entry = {"pk": pk,"latin": _s(row[column_local]) orNone,"family": _s(row[column_family]) orNone,"subfam": _s(row[column_subfam]) orNone,"fr": _s(row[column_fr]) orNone,"en": _s(row[column_en]) orNone, } taxonomy_csv[pk] = entry# Projection secondaire {label_fr_normalise -> pk} pour jointure semantique.def _norm(s):ifnot s:return"" s = s.strip().lower() s = s.replace("'", " ").replace("’", " ") s = _re.sub(r"[^a-z0-9 ]", " ", s) s = _re.sub(r"\s+", " ", s).strip()return staxonomy_csv_by_fr = {}for pk, e in taxonomy_csv.items(): fr = _norm(e["fr"])if fr and fr notin taxonomy_csv_by_fr: taxonomy_csv_by_fr[fr] = pkprint(f"Taxonomie CSV chargee : {len(taxonomy_csv):,} sophismes (cle = PK entier)")print(f" Couverture colonne 'Latin' (cle locale tentee) : {sum(1for v in taxonomy_csv.values() if v['latin']):,} / {len(taxonomy_csv)} ({100*sum(1for v in taxonomy_csv.values() if v['latin'])/len(taxonomy_csv):.1f}%)")print(f" Projection semantique par FR normalise : {len(taxonomy_csv_by_fr):,} cles distinctes")sample =sorted(taxonomy_csv_by_fr.keys())[:5]print(f" Echantillon (FR normalise -> PK) :")for fr in sample:print(f" '{fr}' -> PK {taxonomy_csv_by_fr[fr]}")
Lecture : la lecture CSV est réduite à une table {pk → {latin, family, subfam, fr, en}} indexée par PK — la colonne Latin n’est quasiment pas remplie (taux exact imprime par la cellule suivante) et porte de vrais noms latins (AdHominem, AdPopulum…), pas des localnames camelCase. La clé de jointure réellement mesurée est label FR normalisé côté CSV ↔︎ prefLabel FR normalisé côté OWL (fonction _norm) — le seul champ porté des deux côtés ; la jointure par Latin ne couvrirait que ~1,8 % du CSV. La cellule suivante mesure l’intersection et la différence symétrique sur cette clé sémantique.
4. Incompatibilité deux à deux : intersection, différence, dispersion par famille
On compare taxonomy_owl (lecture 1) et taxonomy_csv (lecture 2) par label FR normalisé (le localname OWL n’a pas d’équivalent CSV — la colonne Latin est quasi vide — taux mesuré au §3) :
Intersection : sophismes présents dans les deux lectures.
Différence symétrique : sophismes présents dans exactement l’une des deux lectures.
Asymétrie d’instanciation : label FR présent dans une lecture mais pas l’autre.
Dispersion par famille : la différence est-elle uniforme entre les 100+ familles Argumentum, ou concentrée sur certaines ?
from collections import Counter# Cle de jointure : label FR normalise (FR est present des deux cotes en OWL et en CSV).owl_by_fr = {}for local, e in taxonomy_owl.items(): fr_norm = _norm(e["fr"])if fr_norm and fr_norm notin owl_by_fr: owl_by_fr[fr_norm] = localinter_fr =set(owl_by_fr) &set(taxonomy_csv_by_fr)owl_only_fr =set(owl_by_fr) -set(taxonomy_csv_by_fr)csv_only_fr =set(taxonomy_csv_by_fr) -set(owl_by_fr)n_owl_fr =len(owl_by_fr)n_csv_fr =len(taxonomy_csv_by_fr)n_inter =len(inter_fr)print(f"Comparaison par label FR normalise (cle de jointure semantique) :")print(f" Lecture OWL (FR distincts) : {n_owl_fr:,}")print(f" Lecture CSV (FR distincts) : {n_csv_fr:,}")print(f" Intersection : {n_inter:,} ({n_inter /max(n_owl_fr, n_csv_fr):.1%} de l'union naturalisee)")print()print(f" OWL \\ CSV (label FR dans OWL seulement) : {len(owl_only_fr):,}")print(f" CSV \\ OWL (label FR dans CSV seulement) : {len(csv_only_fr):,}")print(f" Difference symetrique totale : {len(owl_only_fr) +len(csv_only_fr):,}")print()print(f" Taux d'incompatibilite brute : {(len(owl_only_fr) +len(csv_only_fr)) /max(n_owl_fr, n_csv_fr):.1%}")print()n_latin =sum(1for v in taxonomy_csv.values() if v["latin"])print(f" Jointure locale par 'Latin' (locale tentee anterieurement) : {100*n_latin/len(taxonomy_csv):.1f}% ({n_latin}/{len(taxonomy_csv)} peuple).")print(f" Jointure semantique par FR normalise : observation ci-dessus.")print(f" L'intersection est-elle une majorite stable ? Voir verdict dans la cellule suivante.")
Comparaison par label FR normalise (cle de jointure semantique) :
Lecture OWL (FR distincts) : 1,293
Lecture CSV (FR distincts) : 1,313
Intersection : 1,293 (98.5% de l'union naturalisee)
OWL \ CSV (label FR dans OWL seulement) : 0
CSV \ OWL (label FR dans CSV seulement) : 20
Difference symetrique totale : 20
Taux d'incompatibilite brute : 1.5%
Jointure locale par 'Latin' (locale tentee anterieurement) : 1.8% (25/1408 peuple).
Jointure semantique par FR normalise : observation ci-dessus.
L'intersection est-elle une majorite stable ? Voir verdict dans la cellule suivante.
Lecture : la mesure brute d’incompatibilité est tombée. Les chiffres exacts ci-dessus sont le taux de désaccord sur l’identité même des sophismes (pas leur label ou leur famille) — c’est déjà un signal fort : si l’OWL et le CSV encodent la même taxonomie sous deux formes, leur intersection devrait être ~100 %. Sinon, on a au minimum une substance désynchronisée. La suite examine la dispersion par famille.
# --- Dispersion par famille : ou se concentre la difference symetrique ? ---# Pour les CSV-only : on a la Famille directement.fam_csv_only = Counter()for fr_norm in csv_only_fr: pk = taxonomy_csv_by_fr[fr_norm] e = taxonomy_csv[pk] fam_csv_only[e["family"] or"?"] +=1print(f"Repartition des CSV-only par Famille (sur les {len(csv_only_fr):,} labels absents de l'OWL) :")print(f" Familles distinctes affectees : {len(fam_csv_only)}")for fam, n in fam_csv_only.most_common(10):print(f" {n:>3d}{fam}")# Pour les OWL-only : pas de Famille directe (l'OWL range sous broader/inScheme, pas une colonne plate).# On utilise le prefixe comme proxy.def family_of(local: str) ->str: m = _re.match(r"([a-z]+)", local)return m.group(1) if m else"?"fam_owl_only = Counter()for fr_norm in owl_only_fr: local = owl_by_fr[fr_norm] fam_owl_only[family_of(local)] +=1print(f"\nRepartition des OWL-only par prefixe (proxy ; pas une Famille au sens CSV) :")for fam, n in fam_owl_only.most_common(10):print(f" {n:>3d}{fam}")
Repartition des CSV-only par Famille (sur les 20 labels absents de l'OWL) :
Familles distinctes affectees : 5
8 Tricherie
6 Obstruction
2 Abus de langage
2 Erreur de raisonnement
2 Influence
Repartition des OWL-only par prefixe (proxy ; pas une Famille au sens CSV) :
5. Témoin : une paire incompatible au sens fort
On cherche un sophisme concret tel qu’il est présent dans une lecture et strictement absent dans l’autre. C’est le témoin : une entité qu’on ne peut pas voir depuis une lecture sans la voir depuis l’autre — précisément la définition matérielle de l’incompatibilité non-réductible.
Note importante : la direction du témoin dépend du résultat effectif de la jointure. Dans la version actuelle (substances resynchronisées : OWL 30/08, CSV 04/09 — exécution courante), la jointure sémantique donne 1 293 intersection / 1 293 OWL distincts / 1 313 CSV distincts : - OWL CSV (labels OWL absents du CSV) : 0 (l’OWL ne porte aucun label FR absent du CSV canonique — la couverture FR est complète dans les deux lectures). - CSV OWL (labels CSV absents de l’OWL) : 20 (le CSV porte 20 labels FR que l’OWL n’a pas).
Le vrai témoin est donc CSV-only : un label FR du CSV canonique qui est rigoureusement absent des prefLabel FR de l’OWL. Le témoin sera nommé dans la cellule suivante.
# --- Production du temoin : un sophisme CSV-only, label FR present ---# On selectionne parmi les labels FR du CSV qui n'apparaissent pas dans l'OWL (apres normalisation).candidats_csv_only =sorted(csv_only_fr)print(f"Temoins candidats (labels FR CSV absents de l'OWL apres normalisation) : {len(candidats_csv_only):,}")print()# On prend les 5 premiers tries alphabetiquement, avec leur PK CSV et la Famille.temoins_top = candidats_csv_only[:5]for fr_norm in temoins_top: pk = taxonomy_csv_by_fr[fr_norm] e = taxonomy_csv[pk]print(f" TEMOIN FR normalise = '{fr_norm}'")print(f" FR originel = {e['fr']!r}")print(f" EN = {e['en']!r}")print(f" Famille = {e['family']!r}")print(f" PK CSV = {pk}")print()TEMOIN = temoins_top[0] if temoins_top elseNoneif TEMOIN isnotNone:print(f"=> Premier temoin selectionne : FR normalise '{TEMOIN}' -> PK CSV {taxonomy_csv_by_fr[TEMOIN]}")print(f" * present dans taxonomy_csv : OUI (ligne PK avec text_fr = '{taxonomy_csv[taxonomy_csv_by_fr[TEMOIN]]['fr']}').")print(f" * absent de taxonomy_owl : OUI (label FR '{TEMOIN}' absent apres normalisation).")print(f" Verifie dans OWL (pour preuve) :", TEMOIN notin owl_by_fr)print()print(f" INTERPRETATION : la presence isolee dans le CSV peut signaler")print(f" soit (i) un retard de regeneration de l'OWL depuis le CSV canonique,")print(f" soit (ii) un sophisme documente en marge de la generation OWL.")print(f" Dans les deux cas, le TEMOIN documente la desynchronisation des deux substances.")
Temoins candidats (labels FR CSV absents de l'OWL apres normalisation) : 20
TEMOIN FR normalise = 'accroche'
FR originel = 'Accroche'
EN = 'Sound bite'
Famille = 'Tricherie'
PK CSV = 944
TEMOIN FR normalise = 'appel la conviction'
FR originel = 'Appel à la conviction'
EN = 'Appeal to confidence'
Famille = 'Influence'
PK CSV = 301
TEMOIN FR normalise = 'appel la pseudo profondeur'
FR originel = 'Appel à la pseudo-profondeur'
EN = 'Deepity'
Famille = 'Influence'
PK CSV = 307
TEMOIN FR normalise = 'appel la r ussite'
FR originel = 'Appel à la réussite'
EN = 'Appeal to accomplishment'
Famille = 'Obstruction'
PK CSV = 1386
TEMOIN FR normalise = 'argument apr s contestation'
FR originel = 'Argument après contestation'
EN = 'Moving the goalposts'
Famille = 'Tricherie'
PK CSV = 1005
=> Premier temoin selectionne : FR normalise 'accroche' -> PK CSV 944
* present dans taxonomy_csv : OUI (ligne PK avec text_fr = 'Accroche').
* absent de taxonomy_owl : OUI (label FR 'accroche' absent apres normalisation).
Verifie dans OWL (pour preuve) : True
INTERPRETATION : la presence isolee dans le CSV peut signaler
soit (i) un retard de regeneration de l'OWL depuis le CSV canonique,
soit (ii) un sophisme documente en marge de la generation OWL.
Dans les deux cas, le TEMOIN documente la desynchronisation des deux substances.
6. Graphe de transformations entre lectures
Au-delà du témoin isolé, on catégorise la différence symétrique (OWL \ CSV + CSV \ OWL) en 4 relations typées, qui décrivent comment les lectures peuvent dialoguer :
Relation
Sens
Exemple canonique
exploite
Une lecture utilise un signal de l’autre pour se compléter (label FR du CSV ↔︎ prefLabel FR de l’OWL)
label MANQUANT côté CSV ↔︎ label PRÉSENT côté OWL
répond
Une lecture répond à une question laissée ouverte par l’autre (structure hiérarchique SKOS ↔︎ colonne Sous-Famille)
hiérarchie manquante côté CSV ↔︎ hiérarchie riche côté OWL
répare
Une lecture répare un défaut/bug de l’autre (parsing erroné ↔︎ format normalisé)
Une lecture rend caduque une prétention de l’autre (claim AIF fort ↔︎ absence de matérialisation AIF)
AIF attack absent CSV ↔︎ AIF attack riche OWL
Cette catégorisation est un graphe orienté : pour chaque entité de OWL \ CSV ou CSV \ OWL, on associe une étiquette en fonction du rôle joué par l’entité dans le dialogue des deux lectures.
Sur les substances mesurées, OWL CSV est vide (0 label FR de l’OWL absent du CSV) : seule la branche CSV du graphe se matérialise — les 20 CSV OWL sont tous étiquetés répond par categorize_csv, et neutralise vaut 0. La catégorisation côté OWL (categorize_owl et ses 4 branches) reste du machinisme disponible : elle ne s’activerait que si un état de l’OWL portait des labels FR absents du CSV.
# --- Graphe de transformations : etiqueter chaque individu OWL \ CSV ---def categorize_owl(local: str, entry_owl: dict) ->str: fr = entry_owl.get("fr") en = entry_owl.get("en") has_crosslink =any( (s == local and p in {"mirrors", "isRelatedTo", "leverages", "inverts", "opposes", "allows"})for p, s, o in AA_RES ) has_aif =any((s == local and p =="aifAttackedNode") for p, s, o in AA_RES)ifnot fr and en:return"exploite"# EN present, FR absent -- CSV (FR-complet) peut "completer" l'OWL.ifnot en and fr:return"repond"# FR present, EN absent -- CSV EN peut "completer" l'OWL.if has_crosslink:return"repare"# presence de crosslink = structure relationnelle au-dela de la table plate CSV.if has_aif:return"neutralise"# presence d'AIF attack = ce que la table plate CSV ne peut pas encoder.return"autre"def categorize_csv(fr_norm: str) ->str: pk = taxonomy_csv_by_fr.get(fr_norm)if pk isNone:return"autre" e = taxonomy_csv[pk]if e["family"] and e["subfam"]:return"repond"# CSV porte la hierarchie Famille/Sous-Famille, ce que l'OWL aplatit.return"autre"# Catégorisation sur OWL \ CSV (par localname).g_owl = Counter(categorize_owl(owl_by_fr[fr_norm], taxonomy_owl[owl_by_fr[fr_norm]]) for fr_norm in owl_only_fr)print(f"OWL \\ CSV : {len(owl_only_fr):,} labels FR absents du CSV, categorises")for rel, n in g_owl.most_common():print(f" {rel:14s} : {n:>4d}")print()g_csv = Counter(categorize_csv(fr_norm) for fr_norm in csv_only_fr)print(f"CSV \\ OWL : {len(csv_only_fr):,} labels FR absents de l'OWL, categorises")for rel, n in g_csv.most_common():print(f" {rel:14s} : {n:>4d}")print()total_relations =sum(g_owl.values()) +sum(g_csv.values())print(f"Total relations du graphe de transformations : {total_relations:,}")print(f"Dont neutralise : {g_owl.get('neutralise', 0) + g_csv.get('neutralise', 0):,} (AIF-like, irreductible a CSV)")
OWL \ CSV : 0 labels FR absents du CSV, categorises
CSV \ OWL : 20 labels FR absents de l'OWL, categorises
repond : 20
Total relations du graphe de transformations : 20
Dont neutralise : 0 (AIF-like, irreductible a CSV)
7. Exercices (3 stubs #2161)
Trois exercices stub print("Exercice a completer") — règle C.1 (raise NotImplementedError INTERDIT). Les solutions sont laissées à l’étudiant ; les indices pédagogiques sont dans les cellules markdown qui précèdent.
Exercice 1 — Mesure d’incompatibilité deux à deux sur la famille Ambiguité
A partir de taxonomy_owl, taxonomy_csv et de la liste FAMILY = "Ambiguite" (ou tout préfixe pertinent), calculer l’intersection, la différence symétrique et le taux d’incompatibilité restreint à cette famille. Renvoyer un dict{"owl": int, "csv": int, "inter": int, "symdiff": int, "taux": float} et l’afficher. Indice : la clé réelle est le label FR normalisé (_norm) — le CSV n’a pas de colonne localname. Restreindre taxonomy_csv aux entrées de la famille "Ambiguite" avant de croiser avec owl_by_fr.
Exercice 2 — Construction d’une paire incompatible au sens fort ciblée
Choisir deux sophismes précis — un témoin CSV \ OWL (label FR du CSV strictement absent de l’OWL ; la cellule §5 en produit un) et un élément de l’intersection — et comparer pour chacun le label FR, l’anglais et la famille entre les deux lectures : expliquer en quoi la présence unilatérale du premier est une asymétrie irréductible (≠ un simple défaut de nettoyage) alors que le second est une correspondance saine. Renvoyer une list[tuple[str, str]](label, justification).
Exercice 3 — Visualisation réseau du graphe de transformations
A partir de la catégorisation categorize(), construire un networkx.DiGraph où chaque sophisme de CSV \ OWL (les 20 mesurés) est un nœud, relié à son nœud Famille, chaque arête portant la relation renvoyée par categorize_csv. Utiliser matplotlib + spring_layout(seed=42) pour visualiser. Bonus : colorer les nœuds par catégorie (exploite=vert, répond=bleu, répare=orange, neutralise=rouge).
# Exercice 1 a completer# TODO etudiant : a partir de taxonomy_owl et taxonomy_csv, calculer pour la famille# "Ambiguite" (cle = label FR normalise via _norm) l'intersection, la diff. sym. et le taux.# Renvoyer dict {"owl": int, "csv": int, "inter": int, "symdiff": int, "taux": float}.print("Exercice a completer")
Exercice a completer
# Exercice 2 a completer# TODO etudiant : choisir un temoin CSV \ OWL et un element de l'intersection# et demontrer leur incompatibilite au sens fort. Renvoyer list[tuple[local, justification]].print("Exercice a completer")
Exercice a completer
# Exercice 3 a completer# TODO etudiant : visualiser via networkx + matplotlib le graphe de transformations.# Noeuds = sophismes de CSV \ OWL + leurs familles ; aretes typees par categorize_csv.# spring_layout(seed=42), couleurs par categorie. Bonus : exporter en PNG.print("Exercice a completer")
Exercice a completer
8. Ponts avec la série Argument_Analysis
Direction
Lien
Relation
<->
Argumentation-Onto-01-AIF-OWL2-Python.ipynb
Lecture OWL détaillée (parseur regex, classes AIF, scheme vs conflict) — la lecture 1 de ce notebook, exposée en profondeur ici
->
Argumentation-Onto-02-CrossLinks-CSV-Python.ipynb
Lecture CSV détaillée (pandas) — la lecture 2, exposée en profondeur
->
Argumentation-03-Dung-AF-Semantics-Python.ipynb
Quand les AIF attack de l’OWL (§2, comptes AIF imprimés) s’enrichissent dans un cadre Dung
->
Argumentation-01-Toulmin-Model-Python.ipynb
Quand la famille Ambiguite (exercice 1) se lit au prisme du modèle de Toulmin (donnée/qualification/appui)
Substances réellement distinctes : OWL = parseur regex + contraintes d’extraction spécifiques (gestion des annotations-resource). CSV = pandas + jointure par label FR normalisé (la colonne Latin est quasi vide — taux imprimé au §3). Ce n’est pas « le même appel avec deux prompts » : les moteurs sous-jacents (regex vs DataFrame) et les conventions d’encodage (XML vs tabulaire) ne sont pas interchangeables.
Le témoin n’est pas une pathologie — c’est un signal de désynchronisation entre substances : le témoin mesuré est CSV-only (20 labels FR du CSV absents de l’OWL, 0 en sens inverse). La cellule §5 nomme les deux hypothèses (retard de régénération de l’OWL depuis le CSV / marge de génération) ; on ne tranche pas, on mesure. L’étiquette « strate 6 » marque précisément ce statut : on ne tranche pas, on mesure le décalage pour ouvrir une investigation ciblée.
Catégorisation exploite / répond / répare / neutralise : heuristique au contenu (présence/absence de FR, EN, crosslink, AIF), pas au titre. Les résultats dépendent de l’état au moment du parse — un CSV rafraîchi pourrait réduire fortement les CSV-only ; l’OWL et le CSV sont gelés à leurs resynchronisations respectives (30/08 et 04/09). Toujours ré-mesurer avant toute conclusion de fond.
Pas d’inférence RDF : la lecture OWL ne fait pas de raisonnement (pas de subsomption, pas de fermeture transitive). Pour une reconstruction complète du graphe AIF attack + crossLink, les sources canoniques restent l’OWL et le CSV ; ce notebook les fait dialoguer sur la projection à plat (localname → label).