The ConTextGraph class defines the internal structure of the ConText algorithm. It stores a collection of modifiers,
matched with ConTextRules, and targets from some other source such as the TargetMatcher or a spaCy NER model.
Each modifier can have some number of associated targets that it modifies. This relationship is stored as edges of
of the graph.
Source code in medspacy/context/context_graph.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105 | class ConTextGraph:
"""
The ConTextGraph class defines the internal structure of the ConText algorithm. It stores a collection of modifiers,
matched with ConTextRules, and targets from some other source such as the TargetMatcher or a spaCy NER model.
Each modifier can have some number of associated targets that it modifies. This relationship is stored as edges of
of the graph.
"""
def __init__(
self,
targets: Optional[List[Span]] = None,
modifiers: Optional[List[ConTextModifier]] = None,
edges: Optional[List] = None,
prune_on_modifier_overlap: bool = False,
):
"""
Creates a new ConTextGraph object.
Args:
targets: A spans that context might modify.
modifiers: A list of ConTextModifiers that might modify the targets.
edges: A list of edges between targets and modifiers representing the modification relationship.
prune_on_modifier_overlap: Whether to prune modifiers when one modifier completely covers another.
"""
self.targets = targets if targets is not None else []
self.modifiers = modifiers if modifiers is not None else []
self.edges = edges if edges is not None else []
self.prune_on_modifier_overlap = prune_on_modifier_overlap
def update_scopes(self):
"""
Update the scope of all ConTextModifier.
For each modifier in a list of ConTextModifiers, check against each other
modifier to see if one of the modifiers should update the other.
This allows neighboring similar modifiers to extend each other's
scope and allows "terminate" modifiers to end a modifier's scope.
"""
for i in range(len(self.modifiers) - 1):
modifier1 = self.modifiers[i]
for j in range(i + 1, len(self.modifiers)):
modifier2 = self.modifiers[j]
# TODO: Add modifier -> modifier edges
modifier1.limit_scope(modifier2)
modifier2.limit_scope(modifier1)
def apply_modifiers(self):
"""
Checks each target/modifier pair. If modifier modifies target,
create an edge between them.
"""
if self.prune_on_modifier_overlap:
for i in range(len(self.modifiers) - 1, -1, -1):
modifier = self.modifiers[i]
for target in self.targets:
if tuple_overlaps(
(target.start, target.end), modifier.modifier_span
):
self.modifiers.pop(i)
break
edges = []
for target in self.targets:
for modifier in self.modifiers:
if modifier.modifies(target):
modifier.modify(target)
# Now do a second pass and reduce the number of targets
# for any modifiers with a max_targets int
for modifier in self.modifiers:
modifier.reduce_targets()
for target in modifier._targets:
edges.append((target, modifier))
self.edges = edges
def __repr__(self):
return f"<ConTextGraph> with {len(self.targets)} targets and {len(self.modifiers)} modifiers"
def serialized_representation(self) -> Dict[str, Any]:
"""
Returns the serialized representation of the ConTextGraph
"""
return self.__dict__
@classmethod
def from_serialized_representation(cls, serialized_representation) -> ConTextGraph:
"""
Creates the ConTextGraph from the serialized representation
"""
context_graph = ConTextGraph(**serialized_representation)
return context_graph
|
__init__(targets=None, modifiers=None, edges=None, prune_on_modifier_overlap=False)
Creates a new ConTextGraph object.
Parameters:
| Name |
Type |
Description |
Default |
targets
|
Optional[List[Span]]
|
A spans that context might modify.
|
None
|
modifiers
|
Optional[List[ConTextModifier]]
|
A list of ConTextModifiers that might modify the targets.
|
None
|
edges
|
Optional[List]
|
A list of edges between targets and modifiers representing the modification relationship.
|
None
|
prune_on_modifier_overlap
|
bool
|
Whether to prune modifiers when one modifier completely covers another.
|
False
|
Source code in medspacy/context/context_graph.py
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40 | def __init__(
self,
targets: Optional[List[Span]] = None,
modifiers: Optional[List[ConTextModifier]] = None,
edges: Optional[List] = None,
prune_on_modifier_overlap: bool = False,
):
"""
Creates a new ConTextGraph object.
Args:
targets: A spans that context might modify.
modifiers: A list of ConTextModifiers that might modify the targets.
edges: A list of edges between targets and modifiers representing the modification relationship.
prune_on_modifier_overlap: Whether to prune modifiers when one modifier completely covers another.
"""
self.targets = targets if targets is not None else []
self.modifiers = modifiers if modifiers is not None else []
self.edges = edges if edges is not None else []
self.prune_on_modifier_overlap = prune_on_modifier_overlap
|
apply_modifiers()
Checks each target/modifier pair. If modifier modifies target,
create an edge between them.
Source code in medspacy/context/context_graph.py
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87 | def apply_modifiers(self):
"""
Checks each target/modifier pair. If modifier modifies target,
create an edge between them.
"""
if self.prune_on_modifier_overlap:
for i in range(len(self.modifiers) - 1, -1, -1):
modifier = self.modifiers[i]
for target in self.targets:
if tuple_overlaps(
(target.start, target.end), modifier.modifier_span
):
self.modifiers.pop(i)
break
edges = []
for target in self.targets:
for modifier in self.modifiers:
if modifier.modifies(target):
modifier.modify(target)
# Now do a second pass and reduce the number of targets
# for any modifiers with a max_targets int
for modifier in self.modifiers:
modifier.reduce_targets()
for target in modifier._targets:
edges.append((target, modifier))
self.edges = edges
|
from_serialized_representation(serialized_representation)
classmethod
Creates the ConTextGraph from the serialized representation
Source code in medspacy/context/context_graph.py
98
99
100
101
102
103
104
105 | @classmethod
def from_serialized_representation(cls, serialized_representation) -> ConTextGraph:
"""
Creates the ConTextGraph from the serialized representation
"""
context_graph = ConTextGraph(**serialized_representation)
return context_graph
|
serialized_representation()
Returns the serialized representation of the ConTextGraph
Source code in medspacy/context/context_graph.py
| def serialized_representation(self) -> Dict[str, Any]:
"""
Returns the serialized representation of the ConTextGraph
"""
return self.__dict__
|
update_scopes()
Update the scope of all ConTextModifier.
For each modifier in a list of ConTextModifiers, check against each other
modifier to see if one of the modifiers should update the other.
This allows neighboring similar modifiers to extend each other's
scope and allows "terminate" modifiers to end a modifier's scope.
Source code in medspacy/context/context_graph.py
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57 | def update_scopes(self):
"""
Update the scope of all ConTextModifier.
For each modifier in a list of ConTextModifiers, check against each other
modifier to see if one of the modifiers should update the other.
This allows neighboring similar modifiers to extend each other's
scope and allows "terminate" modifiers to end a modifier's scope.
"""
for i in range(len(self.modifiers) - 1):
modifier1 = self.modifiers[i]
for j in range(i + 1, len(self.modifiers)):
modifier2 = self.modifiers[j]
# TODO: Add modifier -> modifier edges
modifier1.limit_scope(modifier2)
modifier2.limit_scope(modifier1)
|