pyprocessors-coreference 1.6.26__tar.gz → 1.6.29__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/PKG-INFO +7 -1
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/README.md +6 -0
- pyprocessors_coreference-1.6.29/docs/adr/0002-acronymes-mentions-de-l-entite.md +52 -0
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/docs/sherpa/coreference.en.md +42 -1
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/docs/sherpa/coreference.fr.md +42 -1
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/src/pyprocessors_coreference/__init__.py +1 -1
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/src/pyprocessors_coreference/coreference.py +60 -2
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/.claude/settings.json +0 -0
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/.gitignore +0 -0
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/.gitmodules +0 -0
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/.python-version +0 -0
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/LICENSE +0 -0
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/docs/adr/0001-linking-candidats-entityfishing-decision-jev.md +0 -0
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/pyproject.toml +0 -0
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/submodules/python-archetype/.claude-plugin/marketplace.json +0 -0
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/submodules/python-archetype/.claude-plugin/plugin.json +0 -0
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/submodules/python-archetype/HOWTO.md +0 -0
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/submodules/python-archetype/README.md +0 -0
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/submodules/python-archetype/resources/Stages.yml +0 -0
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/submodules/python-archetype/resources/help/help_examples.py +0 -0
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/submodules/python-archetype/resources/help/plugin-help.py +0 -0
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/submodules/python-archetype/resources/help/template.en.md +0 -0
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/submodules/python-archetype/resources/help/template.fr.md +0 -0
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/submodules/python-archetype/skills/new-plugin/SKILL.md +0 -0
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/submodules/python-archetype/skills/new-plugin/references/annotator.md +0 -0
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/submodules/python-archetype/skills/new-plugin/references/converter.md +0 -0
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/submodules/python-archetype/skills/new-plugin/references/formatter.md +0 -0
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/submodules/python-archetype/skills/new-plugin/references/importer.md +0 -0
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/submodules/python-archetype/skills/new-plugin/references/processor.md +0 -0
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/submodules/python-archetype/skills/new-plugin/references/segmenter.md +0 -0
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/submodules/python-archetype/skills/new-plugin/references/types.md +0 -0
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/submodules/python-archetype/skills/plugin-help/SKILL.md +0 -0
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/submodules/python-archetype/tools/check-marketplace.py +0 -0
- {pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/submodules/python-archetype/vars/pythonPipeline.groovy +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: pyprocessors-coreference
|
|
3
|
-
Version: 1.6.
|
|
3
|
+
Version: 1.6.29
|
|
4
4
|
Summary: Processor grouping the mentions of the same entity
|
|
5
5
|
Project-URL: Homepage, https://bitbucket.org/kairntech/pyprocessors_coreference
|
|
6
6
|
Author-email: Olivier Terrier <olivier.terrier@kairntech.com>
|
|
@@ -69,6 +69,12 @@ Pour chaque document, entre mentions de même famille de labels :
|
|
|
69
69
|
groupes possibles pour que l'étape de linking tranche ;
|
|
70
70
|
- **la règle du nom partiel ne vaut que pour les personnes** : « Airbus » ne rejoint pas
|
|
71
71
|
« Airbus Defence and Space ».
|
|
72
|
+
- **les formes d'un acronyme rejoignent l'entité trouvée sur l'une d'elles** : les
|
|
73
|
+
annotations de `pyannotators_acronyms` (forme courte et forme développée, liées par
|
|
74
|
+
leur term `short#long` du lexique `acronyms`) prennent le label le plus fréquent parmi
|
|
75
|
+
les entités de leurs spans exacts et entrent dans son groupe (« MS » rejoint
|
|
76
|
+
« multiple sclerosis »). Une forme déjà annotée n'est pas dupliquée ; un acronyme sans
|
|
77
|
+
entité disparaît de la sortie ([ADR-0002](docs/adr/0002-acronymes-mentions-de-l-entite.md)).
|
|
72
78
|
|
|
73
79
|
Sur chaque annotation d'un label regroupé, il écrit dans `properties` :
|
|
74
80
|
|
|
@@ -39,6 +39,12 @@ Pour chaque document, entre mentions de même famille de labels :
|
|
|
39
39
|
groupes possibles pour que l'étape de linking tranche ;
|
|
40
40
|
- **la règle du nom partiel ne vaut que pour les personnes** : « Airbus » ne rejoint pas
|
|
41
41
|
« Airbus Defence and Space ».
|
|
42
|
+
- **les formes d'un acronyme rejoignent l'entité trouvée sur l'une d'elles** : les
|
|
43
|
+
annotations de `pyannotators_acronyms` (forme courte et forme développée, liées par
|
|
44
|
+
leur term `short#long` du lexique `acronyms`) prennent le label le plus fréquent parmi
|
|
45
|
+
les entités de leurs spans exacts et entrent dans son groupe (« MS » rejoint
|
|
46
|
+
« multiple sclerosis »). Une forme déjà annotée n'est pas dupliquée ; un acronyme sans
|
|
47
|
+
entité disparaît de la sortie ([ADR-0002](docs/adr/0002-acronymes-mentions-de-l-entite.md)).
|
|
42
48
|
|
|
43
49
|
Sur chaque annotation d'un label regroupé, il écrit dans `properties` :
|
|
44
50
|
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
status: proposed
|
|
3
|
+
date: 2026-10-14
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# ADR-0002 — Les formes d'un acronyme deviennent des mentions de l'entité trouvée sur l'une d'elles
|
|
7
|
+
|
|
8
|
+
- **Décision** — Quand `pyannotators_acronyms` a annoté les formes courtes et développées des acronymes d'un document, le processeur les rattache à la vraie entité (NER) trouvée sur l'une d'elles : elles prennent son label, entrent dans son groupe, et les acronymes sans entité disparaissent de la sortie.
|
|
9
|
+
- **Pourquoi** — Le NER biomédical rate une bonne partie des formes courtes. Dans l'article de la fixture (`quantigraph_ot-document-s10571-026-01785-9_reference.pdf.json`), il annote 6 « MS » sur 27, 35 « BBB » sur 58. L'acronyme dit que « MS » est « multiple sclerosis » partout dans le document : c'est un regroupement de mentions, l'étape que porte ce dépôt ([ADR-0001](0001-linking-candidats-entityfishing-decision-jev.md)).
|
|
10
|
+
- **Statut** — `proposed`.
|
|
11
|
+
|
|
12
|
+
## Le contexte
|
|
13
|
+
|
|
14
|
+
`pyannotators_acronyms` (`AcronymsAnnotator`, ou `AcronymsListProcessor` avec `drop_annotations=False`) annote :
|
|
15
|
+
- la forme développée et la forme courte d'une définition (« multiple sclerosis (MS) ») ;
|
|
16
|
+
- avec `propagate`, chaque forme courte qui suit sa définition.
|
|
17
|
+
|
|
18
|
+
Le lien entre les formes est leur term `{identifier: "MS#multiple sclerosis", lexicon: "acronyms"}`. Une forme courte propagée porte un term par forme développée connue : les variantes d'écriture d'une même forme développée (« blood-brain barrier », « blood brain barrier ») en font plusieurs.
|
|
19
|
+
|
|
20
|
+
L'export Sherpa (*download document*) perd les `terms` des annotations ; le processeur, lui, les reçoit. La fixture, elle, les contient.
|
|
21
|
+
|
|
22
|
+
Sur les 45 acronymes de l'article :
|
|
23
|
+
|
|
24
|
+
| Cas | Nb | Exemples |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| entité sur la forme courte et la développée | 17 | AD, BBB, CNS, MS |
|
|
27
|
+
| entité sur la forme courte seulement | 6 | ACE2, TREM2, HSV-1 |
|
|
28
|
+
| entité sur la forme développée seulement | 2 | UPR, BCB |
|
|
29
|
+
| aucune entité | 19 | AI, KG, GWAS, instituts (LIH, ICREA) |
|
|
30
|
+
| labels en conflit | 1 | PD : `diseases_and_syndromes` et `comorbidities` |
|
|
31
|
+
|
|
32
|
+
## Les règles
|
|
33
|
+
|
|
34
|
+
1. **Une annotation d'acronyme se reconnaît à son term du lexique `acronyms`**, pas à son label : `short_label` et `long_label` sont libres. Pas de nouveau paramètre.
|
|
35
|
+
2. **Un acronyme est sa forme courte**, prise dans l'identifiant du term (`MS#…` → `MS`) : toutes les formes qui la partagent sont une seule entité, quelles que soient les écritures de la forme développée.
|
|
36
|
+
3. **Le label est celui des entités trouvées sur l'empan exact de ses formes, le plus fréquent.** Le vote tranche les labels en conflit (PD → `diseases_and_syndromes`).
|
|
37
|
+
4. **Une forme dont l'empan porte déjà une entité est supprimée** : c'est un doublon. L'entité garde son label, même minoritaire.
|
|
38
|
+
5. **Les autres formes prennent le label** (`labelName` et `label`) et perdent leur term `acronyms` : c'est un signal d'entrée, consommé ici.
|
|
39
|
+
6. **Les formes et les entités au label retenu forment un seul groupe** : « MS » et « multiple sclerosis » ont le même `entity_group`, et la forme développée, plus longue, devient `canonical_form`.
|
|
40
|
+
7. **Un acronyme sans entité disparaît de la sortie** : ses formes ne sont pas des entités du projet, et des groupes `short` / `long` n'ont rien à faire au linking.
|
|
41
|
+
|
|
42
|
+
## Les limites connues
|
|
43
|
+
|
|
44
|
+
- **Empan exact seulement** : une entité qui déborde ou coupe la forme développée ne compte pas. Une règle d'inclusion viendra si ces ratés se voient dans les données.
|
|
45
|
+
- **Une forme courte à deux sens dans un même document** (MS pour *multiple sclerosis* et *mass spectrometry*) fait une seule entité. Marqué `ponytail:` dans le code.
|
|
46
|
+
- **Les incohérences du NER restent** : il annote aussi « BBB » en `diseases_and_syndromes` ; ces annotations gardent leur label et leur propre groupe, à côté du groupe `tissues` des formes de l'acronyme.
|
|
47
|
+
|
|
48
|
+
## Les options écartées
|
|
49
|
+
|
|
50
|
+
- **Garder les acronymes sans entité** : ils remonteraient au linking sous des labels `short` / `long` sans base de connaissances.
|
|
51
|
+
- **Lire le lien dans l'altText `acronyms`** de `AcronymsListProcessor` : présent dans l'export, mais c'est une dépendance de plus, sur un format texte, quand les terms sont la sortie native de l'annotateur.
|
|
52
|
+
- **Un acronyme par identifiant `court#long`** : les variantes d'écriture de la forme développée scindaient l'acronyme, et une forme courte propagée avec deux terms ne pouvait être rangée nulle part.
|
{pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/docs/sherpa/coreference.en.md
RENAMED
|
@@ -18,6 +18,7 @@ Groups the mentions of the same entity in a document: "Emmanuel Macron", "M. Mac
|
|
|
18
18
|
| Prerequisite | Required | Why |
|
|
19
19
|
|---|---|---|
|
|
20
20
|
| Entity annotations | **Yes** | The processor only groups existing annotations. A document without annotation is returned as is. |
|
|
21
|
+
| Acronyms annotator (`acronyms`), or Acronyms list processor (`acronyms_list`) with **Drop Annotations** off | No | Its short and expanded forms become mentions of the entity found on one of them. Placed before this processor. |
|
|
21
22
|
|
|
22
23
|
<!-- generated:languages -->
|
|
23
24
|
|
|
@@ -29,7 +30,7 @@ Name comparison does not depend on the language, but the default **Honorifics**
|
|
|
29
30
|
|
|
30
31
|
## What it produces
|
|
31
32
|
|
|
32
|
-
The processor
|
|
33
|
+
The processor creates no annotation, and only removes acronym annotations (see **How it works**). It adds **properties** to each annotation it groups:
|
|
33
34
|
|
|
34
35
|
| Property | Content | Example |
|
|
35
36
|
|---|---|---|
|
|
@@ -61,6 +62,10 @@ Two mentions go in the same group in the following cases:
|
|
|
61
62
|
1. **Same form and same label family.** The comparison ignores case and apostrophe variants: "Airbus" and "AIRBUS" are grouped. A label whose name ends with another grouped label is of its family: a "Washington" annotated `location` by a model and `afplocation` by a lexicon forms a single group, as do `organization` and `afporganization`.
|
|
62
63
|
2. **Same family of persons.** All person labels count as one, whatever their name: a "George Lucas" annotated `person` by a model and `afpperson` by a lexicon forms a single group.
|
|
63
64
|
3. **Partial name, for persons only.** A name that ends a longer name joins this name, whether it appears before or after it in the document: "Macron" and "M. Macron" join "Emmanuel Macron". The **Honorifics** titles are ignored for the comparison.
|
|
65
|
+
4. **Forms of one acronym.** When the Acronyms annotator ran before, the short form ("SEP") and the expanded form ("sclérose en plaques") of an acronym join the entity found exactly on one of them, everywhere in the document:
|
|
66
|
+
- they take its label, the most frequent one if the entities of their forms have several;
|
|
67
|
+
- a form already annotated by an entity is not duplicated: the acronym annotation is removed;
|
|
68
|
+
- an acronym without any entity on its forms is gone from the output, short and expanded annotations included.
|
|
64
69
|
|
|
65
70
|
Are **never** grouped:
|
|
66
71
|
|
|
@@ -223,6 +228,39 @@ Output annotations:
|
|
|
223
228
|
|
|
224
229
|
<!-- /generated:example:labels -->
|
|
225
230
|
|
|
231
|
+
### 6. The forms of an acronym
|
|
232
|
+
|
|
233
|
+
Only the first "SEP" is a `maladie` entity: on input it carries two annotations, the entity and the short form of the acronym. The expanded form and the second "SEP" join it; the acronym "AP", without entity, is gone:
|
|
234
|
+
|
|
235
|
+
<!-- generated:example:acronyms -->
|
|
236
|
+
|
|
237
|
+
Text (French):
|
|
238
|
+
|
|
239
|
+
> La sclérose en plaques (SEP) touche le cerveau. La SEP est chronique. L'apprentissage profond (AP) aide le diagnostic.
|
|
240
|
+
|
|
241
|
+
Input annotations:
|
|
242
|
+
|
|
243
|
+
| Annotated text | Label | Preferred form | Score |
|
|
244
|
+
|---|---|---|---|
|
|
245
|
+
| sclérose en plaques | `long` | sclérose en plaques | 1.00 |
|
|
246
|
+
| SEP *(#1)* | `maladie` | — | 1.00 |
|
|
247
|
+
| SEP *(#1)* | `short` | sclérose en plaques | 1.00 |
|
|
248
|
+
| SEP *(#2)* | `short` | sclérose en plaques | 1.00 |
|
|
249
|
+
| apprentissage profond | `long` | apprentissage profond | 1.00 |
|
|
250
|
+
| AP | `short` | apprentissage profond | 1.00 |
|
|
251
|
+
|
|
252
|
+
Default parameters.
|
|
253
|
+
|
|
254
|
+
Output annotations:
|
|
255
|
+
|
|
256
|
+
| Annotated text | Label | Score | Properties |
|
|
257
|
+
|---|---|---|---|
|
|
258
|
+
| sclérose en plaques | `maladie` | 1.00 | `canonical_form`: sclérose en plaques<br>`entity_group`: g1<br>`family`: maladie<br>`mention_type`: name |
|
|
259
|
+
| SEP *(#1)* | `maladie` | 1.00 | `canonical_form`: sclérose en plaques<br>`entity_group`: g1<br>`family`: maladie<br>`mention_type`: name |
|
|
260
|
+
| SEP *(#2)* | `maladie` | 1.00 | `canonical_form`: sclérose en plaques<br>`entity_group`: g1<br>`family`: maladie<br>`mention_type`: name |
|
|
261
|
+
|
|
262
|
+
<!-- /generated:example:acronyms -->
|
|
263
|
+
|
|
226
264
|
## Known limitations
|
|
227
265
|
|
|
228
266
|
- **A `PER` label is not recognized as a person by default.** Only the labels whose name ends with "person" are. With `PER` annotations, "Macron" does not join "Emmanuel Macron" as long as **Label Families** does not map `PER` to `person`. Once declared, it groups them:
|
|
@@ -255,3 +293,6 @@ Output annotations:
|
|
|
255
293
|
- **A title missing from the list is part of the name.** "Général Martin" is not reduced to "Martin"; it is grouped with "Martin" by the partial-name rule, but the canonical form of the group then becomes "Général Martin".
|
|
256
294
|
- **A label belongs to one family only.** Declared with `organization`, a `loc_org` never joins a `location` of the same form.
|
|
257
295
|
- **Mentions of labels excluded by Labels receive nothing**, not even a group of their own.
|
|
296
|
+
- **An acronym form only joins an entity at exactly the same position.** If the entity covers "la sclérose en plaques" and the acronym "sclérose en plaques", the acronym does not find it.
|
|
297
|
+
- **An acronym is identified by its short form.** One short form standing for two things in a document ("MS" for *multiple sclerosis* and *mass spectrometry*) makes a single entity.
|
|
298
|
+
- **Contradicting labels of the entity annotator stay.** An entity on an acronym form keeps its label, even when it is not the one chosen for the acronym: it then forms its own group.
|
{pyprocessors_coreference-1.6.26 → pyprocessors_coreference-1.6.29}/docs/sherpa/coreference.fr.md
RENAMED
|
@@ -18,6 +18,7 @@ Regroupe les mentions d'une même entité dans un document : « Emmanuel Macron
|
|
|
18
18
|
| Prérequis | Obligatoire | Pourquoi |
|
|
19
19
|
|---|---|---|
|
|
20
20
|
| Annotations d'entités | **Oui** | Le processeur ne regroupe que des annotations existantes. Un document sans annotation est rendu tel quel. |
|
|
21
|
+
| Annotateur Acronymes (`acronyms`), ou processeur Liste des acronymes (`acronyms_list`) avec **Drop Annotations** désactivé | Non | Ses formes courtes et développées deviennent des mentions de l'entité trouvée sur l'une d'elles. Placé avant ce processeur. |
|
|
21
22
|
|
|
22
23
|
<!-- generated:languages -->
|
|
23
24
|
|
|
@@ -29,7 +30,7 @@ La comparaison des noms ne dépend pas de la langue, mais la liste par défaut d
|
|
|
29
30
|
|
|
30
31
|
## Ce qu'il produit
|
|
31
32
|
|
|
32
|
-
Le processeur ne crée
|
|
33
|
+
Le processeur ne crée aucune annotation, et ne retire que des annotations d'acronymes (voir **Fonctionnement**). Il ajoute des **propriétés** à chaque annotation qu'il regroupe :
|
|
33
34
|
|
|
34
35
|
| Propriété | Contenu | Exemple |
|
|
35
36
|
|---|---|---|
|
|
@@ -61,6 +62,10 @@ Deux mentions vont dans le même groupe dans les cas suivants :
|
|
|
61
62
|
1. **Même forme et même famille de labels.** La comparaison ignore les majuscules et les variantes d'apostrophe : « Airbus » et « AIRBUS » sont regroupés. Un label dont le nom finit par un autre label regroupé est de sa famille : un « Washington » annoté `location` par un modèle et `afplocation` par un lexique forme un seul groupe, comme `organization` et `afporganization`.
|
|
62
63
|
2. **Même famille de personnes.** Tous les labels de personnes comptent comme un seul, quel que soit leur nom : un « George Lucas » annoté `person` par un modèle et `afpperson` par un lexique forme un seul groupe.
|
|
63
64
|
3. **Nom partiel, pour les personnes seulement.** Un nom qui termine un nom plus long rejoint ce nom, qu'il apparaisse avant ou après lui dans le document : « Macron » et « M. Macron » rejoignent « Emmanuel Macron ». Les civilités de **Honorifics** sont ignorées pour comparer.
|
|
65
|
+
4. **Formes d'un même acronyme.** Quand l'annotateur Acronymes est passé avant, la forme courte (« SEP ») et la forme développée (« sclérose en plaques ») d'un acronyme rejoignent l'entité trouvée exactement sur l'une d'elles, partout dans le document :
|
|
66
|
+
- elles prennent son label, le plus fréquent si les entités de leurs formes en ont plusieurs ;
|
|
67
|
+
- une forme déjà annotée par une entité n'est pas dupliquée : l'annotation de l'acronyme est retirée ;
|
|
68
|
+
- un acronyme sans aucune entité sur ses formes disparaît de la sortie, ses annotations courtes et développées comprises.
|
|
64
69
|
|
|
65
70
|
Ne sont **jamais** regroupés :
|
|
66
71
|
|
|
@@ -223,6 +228,39 @@ Annotations en sortie :
|
|
|
223
228
|
|
|
224
229
|
<!-- /generated:example:labels -->
|
|
225
230
|
|
|
231
|
+
### 6. Les formes d'un acronyme
|
|
232
|
+
|
|
233
|
+
Seule la première « SEP » est une entité `maladie` : elle porte en entrée deux annotations, l'entité et la forme courte de l'acronyme. La forme développée et la seconde « SEP » la rejoignent ; l'acronyme « AP », sans entité, disparaît :
|
|
234
|
+
|
|
235
|
+
<!-- generated:example:acronyms -->
|
|
236
|
+
|
|
237
|
+
Texte (français) :
|
|
238
|
+
|
|
239
|
+
> La sclérose en plaques (SEP) touche le cerveau. La SEP est chronique. L'apprentissage profond (AP) aide le diagnostic.
|
|
240
|
+
|
|
241
|
+
Annotations en entrée :
|
|
242
|
+
|
|
243
|
+
| Texte annoté | Label | Forme préférée | Score |
|
|
244
|
+
|---|---|---|---|
|
|
245
|
+
| sclérose en plaques | `long` | sclérose en plaques | 1,00 |
|
|
246
|
+
| SEP *(n°1)* | `maladie` | — | 1,00 |
|
|
247
|
+
| SEP *(n°1)* | `short` | sclérose en plaques | 1,00 |
|
|
248
|
+
| SEP *(n°2)* | `short` | sclérose en plaques | 1,00 |
|
|
249
|
+
| apprentissage profond | `long` | apprentissage profond | 1,00 |
|
|
250
|
+
| AP | `short` | apprentissage profond | 1,00 |
|
|
251
|
+
|
|
252
|
+
Paramètres par défaut.
|
|
253
|
+
|
|
254
|
+
Annotations en sortie :
|
|
255
|
+
|
|
256
|
+
| Texte annoté | Label | Score | Propriétés |
|
|
257
|
+
|---|---|---|---|
|
|
258
|
+
| sclérose en plaques | `maladie` | 1,00 | `canonical_form` : sclérose en plaques<br>`entity_group` : g1<br>`family` : maladie<br>`mention_type` : name |
|
|
259
|
+
| SEP *(n°1)* | `maladie` | 1,00 | `canonical_form` : sclérose en plaques<br>`entity_group` : g1<br>`family` : maladie<br>`mention_type` : name |
|
|
260
|
+
| SEP *(n°2)* | `maladie` | 1,00 | `canonical_form` : sclérose en plaques<br>`entity_group` : g1<br>`family` : maladie<br>`mention_type` : name |
|
|
261
|
+
|
|
262
|
+
<!-- /generated:example:acronyms -->
|
|
263
|
+
|
|
226
264
|
## Limites à connaître
|
|
227
265
|
|
|
228
266
|
- **Un label `PER` n'est pas reconnu comme personne par défaut.** Seuls les labels dont le nom finit par « person » le sont. Avec des annotations `PER`, « Macron » ne rejoint pas « Emmanuel Macron » tant que **Label Families** n'associe pas `PER` à `person`. Déclaré, il les regroupe :
|
|
@@ -255,3 +293,6 @@ Annotations en sortie :
|
|
|
255
293
|
- **Une civilité absente de la liste fait partie du nom.** « Général Martin » ne se réduit pas à « Martin » ; il est regroupé avec « Martin » par la règle du nom partiel, mais la forme canonique du groupe devient alors « Général Martin ».
|
|
256
294
|
- **Un label n'appartient qu'à une famille.** Déclaré avec `organization`, un `loc_org` ne rejoint plus jamais un `location` de même forme.
|
|
257
295
|
- **Les mentions des labels exclus par Labels ne reçoivent rien**, pas même un groupe à elles seules.
|
|
296
|
+
- **Une forme d'acronyme ne rejoint qu'une entité de même position exacte.** Si l'entité couvre « la sclérose en plaques » et l'acronyme « sclérose en plaques », l'acronyme ne la trouve pas.
|
|
297
|
+
- **Un acronyme est identifié par sa forme courte.** Une même forme courte qui désigne deux choses dans un document (« MS » pour *multiple sclerosis* et *mass spectrometry*) ne fait qu'une entité.
|
|
298
|
+
- **Les étiquettes contradictoires de l'annotateur d'entités restent.** Une entité posée sur une forme d'acronyme garde son label, même s'il n'est pas celui retenu pour l'acronyme : elle forme alors son propre groupe.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import re
|
|
2
2
|
import unicodedata
|
|
3
|
-
from collections import defaultdict
|
|
3
|
+
from collections import Counter, defaultdict
|
|
4
4
|
from typing import cast
|
|
5
5
|
|
|
6
6
|
from pydantic import BaseModel, Field
|
|
@@ -21,6 +21,10 @@ FAMILY = "family"
|
|
|
21
21
|
# Only on an ambiguous partial name: the groups it may belong to.
|
|
22
22
|
ENTITY_GROUP_CANDIDATES = "entity_group_candidates"
|
|
23
23
|
|
|
24
|
+
# Lexicon of the terms pyannotators_acronyms writes on the short and expanded forms of
|
|
25
|
+
# an acronym, identified by "short#long": the link between them.
|
|
26
|
+
ACRONYMS = "acronyms"
|
|
27
|
+
|
|
24
28
|
# Labels of one family are compared as one: the NER's "location" and the AFP
|
|
25
29
|
# lexicon's "afplocation" annotate the same span, and must land in the same group.
|
|
26
30
|
# All person labels form one family, whatever their name.
|
|
@@ -77,7 +81,10 @@ class CoreferenceProcessor(ProcessorBase):
|
|
|
77
81
|
- a first name alone never joins ("Emmanuel" stays on its own);
|
|
78
82
|
- an ambiguous partial name ("Macron" with both Emmanuel and Brigitte Macron)
|
|
79
83
|
keeps a group of its own, and lists the possible groups in
|
|
80
|
-
`entity_group_candidates` for the linking step to decide
|
|
84
|
+
`entity_group_candidates` for the linking step to decide;
|
|
85
|
+
- acronyms (pyannotators_acronyms, linked by their term in the lexicon "acronyms"):
|
|
86
|
+
the short and expanded forms of an acronym join the entity found on any of them,
|
|
87
|
+
and take its label. A form without entity is dropped.
|
|
81
88
|
"""
|
|
82
89
|
|
|
83
90
|
def process(self, documents: list[Document], parameters: ProcessorParameters) -> list[Document]:
|
|
@@ -94,6 +101,7 @@ class CoreferenceProcessor(ProcessorBase):
|
|
|
94
101
|
|
|
95
102
|
def group_document(document: Document, params: CoreferenceParameters, honorifics: set[str]) -> None:
|
|
96
103
|
text = document.text or ""
|
|
104
|
+
links = resolve_acronyms(document)
|
|
97
105
|
mentions = [a for a in (document.annotations or []) if a.labelName and (not params.labels or a.labelName in params.labels)]
|
|
98
106
|
if not mentions:
|
|
99
107
|
return
|
|
@@ -101,11 +109,13 @@ def group_document(document: Document, params: CoreferenceParameters, honorifics
|
|
|
101
109
|
# 1. Same label family, same normalized surface: one surface group.
|
|
102
110
|
families = label_families({a.labelName for a in mentions}, params)
|
|
103
111
|
surfaces: dict[tuple[str, tuple[str, ...]], list[Annotation]] = defaultdict(list)
|
|
112
|
+
surface_key: dict[int, tuple[str, tuple[str, ...]]] = {}
|
|
104
113
|
for a in sorted(mentions, key=lambda a: (a.start, a.end)):
|
|
105
114
|
family = families[a.labelName]
|
|
106
115
|
tokens = name_tokens(surface_of(a, text), honorifics if family == PERSON_FAMILY else set())
|
|
107
116
|
if tokens:
|
|
108
117
|
surfaces[(family, tokens)].append(a)
|
|
118
|
+
surface_key[id(a)] = (family, tokens)
|
|
109
119
|
|
|
110
120
|
# 2. Person labels: a surface that is a proper suffix of longer surfaces joins
|
|
111
121
|
# them when they are all one entity. Longest first, so that "Emmanuel Macron" has
|
|
@@ -118,6 +128,12 @@ def group_document(document: Document, params: CoreferenceParameters, honorifics
|
|
|
118
128
|
key = parent[key]
|
|
119
129
|
return key
|
|
120
130
|
|
|
131
|
+
# The short and expanded forms of one acronym are one entity ("AD", "Alzheimer's disease").
|
|
132
|
+
for link in links:
|
|
133
|
+
keys = [root(surface_key[id(a)]) for a in link if id(a) in surface_key]
|
|
134
|
+
for key in keys[1:]:
|
|
135
|
+
parent[root(key)] = root(keys[0])
|
|
136
|
+
|
|
121
137
|
ambiguous: dict[tuple, set] = {}
|
|
122
138
|
for key in sorted(surfaces, key=lambda k: -len(k[1])):
|
|
123
139
|
family, tokens = key
|
|
@@ -153,6 +169,48 @@ def group_document(document: Document, params: CoreferenceParameters, honorifics
|
|
|
153
169
|
a.properties = properties
|
|
154
170
|
|
|
155
171
|
|
|
172
|
+
def resolve_acronyms(document: Document) -> list[list[Annotation]]:
|
|
173
|
+
"""The forms of an acronym (the annotations whose term in the lexicon "acronyms" has its short form)
|
|
174
|
+
take the label most found among the entities of exactly their spans. A form whose span
|
|
175
|
+
already has an entity is dropped as a duplicate, the others take the label and lose
|
|
176
|
+
their acronym term; an acronym without entity is dropped. Returns, per acronym, the
|
|
177
|
+
mentions to group together."""
|
|
178
|
+
entities: dict[tuple[int, int], list[Annotation]] = defaultdict(list)
|
|
179
|
+
acronyms: dict[str, list[Annotation]] = defaultdict(list)
|
|
180
|
+
dropped: set[int] = set()
|
|
181
|
+
for a in document.annotations or []:
|
|
182
|
+
# The acronym is its short form: its expansions vary in spelling ("blood-brain
|
|
183
|
+
# barrier", "blood brain barrier"), and a propagated short form lists them all.
|
|
184
|
+
# ponytail: a short form with two meanings in one document (MS) makes one entity.
|
|
185
|
+
shorts = {t.identifier.split("#", 1)[0] for t in a.terms or [] if t.lexicon == ACRONYMS}
|
|
186
|
+
if not shorts:
|
|
187
|
+
if a.labelName:
|
|
188
|
+
entities[(a.start, a.end)].append(a)
|
|
189
|
+
continue
|
|
190
|
+
dropped.add(id(a))
|
|
191
|
+
acronyms[min(shorts)].append(a)
|
|
192
|
+
if not dropped:
|
|
193
|
+
return []
|
|
194
|
+
|
|
195
|
+
links = []
|
|
196
|
+
for forms in acronyms.values():
|
|
197
|
+
found = [e for f in forms for e in entities.get((f.start, f.end), [])]
|
|
198
|
+
if not found:
|
|
199
|
+
continue
|
|
200
|
+
label = Counter(e.labelName for e in found).most_common(1)[0][0]
|
|
201
|
+
model = next(e for e in found if e.labelName == label)
|
|
202
|
+
link = [e for e in found if e.labelName == label]
|
|
203
|
+
for f in forms:
|
|
204
|
+
if (f.start, f.end) not in entities:
|
|
205
|
+
f.labelName, f.label = label, model.label
|
|
206
|
+
f.terms = [t for t in f.terms or [] if t.lexicon != ACRONYMS] or None
|
|
207
|
+
dropped.discard(id(f))
|
|
208
|
+
link.append(f)
|
|
209
|
+
links.append(link)
|
|
210
|
+
document.annotations = [a for a in document.annotations or [] if id(a) not in dropped]
|
|
211
|
+
return links
|
|
212
|
+
|
|
213
|
+
|
|
156
214
|
def label_families(labels: set[str], params: CoreferenceParameters) -> dict[str, str]:
|
|
157
215
|
"""Label → family, ignoring case: its value for a label declared in `label_families`
|
|
158
216
|
(a value is its own family, so that "organization" joins the labels mapped to it),
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|