Skip to content

medspacy.context.context_graph

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.

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
92
93
94
95
96
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)