Skip to content

medspacy.context.context_modifier

ConTextModifier

Represents a concept found by ConText in a document. An instance of this class is the result of ConTextRule matching text in a Doc.

Source code in medspacy/context/context_modifier.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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
class ConTextModifier:
    """
    Represents a concept found by ConText in a document. An instance of this class is the result of ConTextRule matching
    text in a Doc.
    """

    def __init__(
        self,
        context_rule: ConTextRule,
        start: int,
        end: int,
        doc: Doc,
        scope_start: Optional[int] = None,
        scope_end: Optional[int] = None,
        max_scope: Optional[int] = None,
    ):
        """
        Create a new ConTextModifier from a document span. Each modifier represents a span in the text and a surrounding
        window. Spans such as entities or other members of span groups that occur within this window can be modified by
        this ConTextModifier.

        Args:
            context_rule: The ConTextRule object which defines the modifier.
            start: The start token index.
            end: The end token index (non-inclusive).
            doc: The spaCy Doc which contains this span. This is needed to initialize the modifier but is not
                maintained.
            scope_start: The start token index of the scope.
            scope_end: The end index of the scope.
            max_scope: Whether to use scope values rather than sentence boundaries for modifications.
        """
        self._context_rule = context_rule
        self._start = start
        self._end = end

        self._targets = []
        self._num_targets = 0

        self._max_scope = max_scope
        self._scope_start = scope_start
        self._scope_end = scope_end
        if doc is not None and (self._scope_end is None or self._scope_start is None):
            self.__set_scope(doc)

    @property
    def modifier_span(self) -> Tuple[int, int]:
        """
        The spaCy Span object, which is a view of self.doc, covered by this match.
        """
        return self._start, self._end

    @property
    def rule(self) -> ConTextRule:
        """
        Returns the associated context rule.
        """
        return self._context_rule

    @property
    def direction(self) -> str:
        """
        Returns the associated direction.
        """
        return self.rule.direction

    @property
    def category(self) -> str:
        """
        Returns the associated category.
        """
        return self.rule.category

    @property
    def scope_span(self) -> Tuple[int, int]:
        """
        Returns the associated scope.
        """
        return self._scope_start, self._scope_end

    @property
    def allowed_types(self) -> Set[str]:
        """
        Returns the associated allowed types.
        """
        return self.rule.allowed_types

    @property
    def excluded_types(self) -> Set[str]:
        """
        Returns the associated excluded types.
        """
        return self.rule.excluded_types

    @property
    def num_targets(self) -> int:
        """
        Returns the associated number of targets.
        """
        return self._num_targets

    @property
    def max_targets(self) -> Union[int, None]:
        """
        Returns the associated maximum number of targets.
        """
        return self.rule.max_targets

    @property
    def max_scope(self) -> Union[int, None]:
        """
        Returns the associated maximum scope.
        """
        return self.rule.max_scope

    def __set_scope(self, doc: Doc):
        """
        Applies the direction of the ConTextRule which generated this ConTextModifier to define a scope. If
        self._max_scope is None, then the default scope is the sentence which it occurs in whichever direction defined by
        self.direction. For example, if the direction is "forward", the scope will be [self.end: sentence.end]. If the
        direction is "backward", it will be [self.start: sentence.start].

        If self.max_scope is not None and the length of the default scope is longer than self.max_scope, it will be
        reduced to self.max_scope.

        Args:
            doc: The spaCy doc to use to set scope.
        """
        # If ConText is set to use defined windows, do that instead of sentence splitting
        if self._max_scope:
            full_scope_span = doc[self._start : self._end]._.window(
                n=self.rule.max_scope
            )
        # Otherwise, use the sentence
        else:
            full_scope_span = doc[self._start].sent
            if full_scope_span is None:
                raise ValueError(
                    "ConText failed because sentence boundaries have not been set. Add an upstream component such as the "
                    "dependency parser, Sentencizer, or PyRuSH to detect sentence boundaries or initialize ConText with "
                    "`max_scope` set to a value greater than 0."
                )

        if self.direction.lower() == "forward":
            self._scope_start, self._scope_end = self._end, full_scope_span.end
            if (
                self.max_scope is not None
                and (self._scope_end - self._scope_start) > self.max_scope
            ):
                self._scope_end = self._end + self.max_scope

        elif self.direction.lower() == "backward":
            self._scope_start, self._scope_end = (
                full_scope_span.start,
                self._start,
            )
            if (
                self.max_scope is not None
                and (self._scope_end - self._scope_start) > self.max_scope
            ):
                self._scope_start = self._start - self.max_scope

        else:  # bidirectional
            self._scope_start, self._scope_end = (
                full_scope_span.start,
                full_scope_span.end,
            )

            # Set the max scope on either side
            # Backwards
            if (
                self.max_scope is not None
                and (self._start - self._scope_start) > self.max_scope
            ):
                self._scope_start = self._start - self.max_scope
            # Forwards
            if (
                self.max_scope is not None
                and (self._scope_end - self._end) > self.max_scope
            ):
                self._scope_end = self._end + self.max_scope

    def update_scope(self, span: Span):
        """
        Changes the scope of self to be the given spaCy span.

        Args:
            span: a spaCy Span which contains the scope which a modifier should cover.
        """
        self._scope_start = span.start
        self._scope_end = span.end

    def limit_scope(self, other: ConTextModifier) -> bool:
        """
        If self and other have the same category or if other has a directionality of 'terminate', use the span of other
        to update the scope of self. Limiting the scope of two modifiers of the same category reduces the number of
        modifiers. For example, in 'no evidence of CHF, no pneumonia', 'pneumonia' will only be modified by 'no', not
        'no evidence of'. 'terminate' modifiers limit the scope of a modifier like 'no evidence of' in 'no evidence of
        CHF, but there is pneumonia'

        Args:
            other: The modifier to check against.

        Returns:
            Whether the other modifier modified the scope of self.
        """
        if not tuple_overlaps(self.scope_span, other.scope_span):
            return False
        if self.direction.upper() == "TERMINATE":
            return False
        # Check if the other modifier is a type which can modify self
        # or if they are the same category. If not, don't reduce scope.
        if (
            (other.direction.upper() != "TERMINATE")
            and (other.category.upper() not in self.rule.terminated_by)
            and (other.category.upper() != self.category.upper())
        ):
            return False

        # If two modifiers have the same category but modify different target types,
        # don't limit scope.
        if self.category == other.category and (
            (self.allowed_types != other.allowed_types)
            or (self.excluded_types != other.excluded_types)
        ):
            return False

        orig_scope = self.scope_span
        if self.direction.lower() in ("forward", "bidirectional"):
            if other > self:
                self._scope_end = min(self._scope_end, other.modifier_span[0])
        if self.direction.lower() in ("backward", "bidirectional"):
            if other < self:
                self._scope_start = max(self._scope_start, other.modifier_span[1])
        return orig_scope != self.scope_span

    def modifies(self, target: Span) -> bool:
        """
        Checks whether the target is within the modifier scope and if self is allowed to modify target.

        Args:
            target: a spaCy span representing a target concept.

        Returns:
            Whether the target is within `modifier_scope` and if self is allowed to modify the target.
        """
        # If the target and modifier overlap, meaning at least one token
        # one extracted as both a target and modifier, return False
        # to avoid self-modifying concepts

        if tuple_overlaps(
            self.modifier_span, (target.start, target.end)
        ):  # self.overlaps(target):
            return False
        if self.direction in ("TERMINATE", "PSEUDO"):
            return False
        if not self.allows(target.label_.upper()):
            return False

        if tuple_overlaps(self.scope_span, (target.start, target.end)):
            if not self.on_modifies(target):
                return False
            else:
                return True
        return False

    def allows(self, target_label: str) -> bool:
        """
        Returns whether if a modifier is able to modify a target type.

        Args:
            target_label: The target type to check.

        Returns:
            Whether the modifier is allowed to modify a target of the specified type. True if `target_label` in
            `self.allowed_types` or if `target_label` not in `self.excluded_tupes`. False otherwise.
        """
        if self.allowed_types is not None:
            return target_label in self.allowed_types
        if self.excluded_types is not None:
            return target_label not in self.excluded_types
        return True

    def on_modifies(self, target: Span) -> bool:
        """
        If the ConTextRule used to define a ConTextModifier has an `on_modifies` callback function, evaluate and return
        either True or False.

        Args:
            target: The spaCy span to evaluate.

        Returns:
            The result of the `on_modifies` callback for the rule. True if the callback is None.
        """
        if self.rule.on_modifies is None:
            return True
        # Find the span in between the target and modifier
        start = min(target.end, self._end)
        end = max(target.start, self._end)
        span_between = target.doc[start:end]
        rslt = self.rule.on_modifies(
            target, target.doc[self._start : self._end], span_between
        )
        if rslt not in (True, False):
            raise ValueError(
                "The on_modifies function must return either True or False indicating "
                "whether a modify modifies a target. Actual value: {0}".format(rslt)
            )
        return rslt

    def modify(self, target: Span):
        """
        Add target to the list of self._targets and increment self._num_targets.

        Args:
            target: The spaCy span to add.
        """
        self._targets.append(target)
        self._num_targets += 1

    def reduce_targets(self):
        """
        Reduces the number of targets to the n-closest targets based on the value of `self.max_targets`. If
        `self.max_targets` is None, no pruning is done.
        """
        if self.max_targets is None or self.num_targets <= self.max_targets:
            return

        target_dists = []
        for target in self._targets:
            dist = min(abs(self._start - target.end), abs(target.start - self._end))
            target_dists.append((target, dist))
        srtd_targets, _ = zip(*sorted(target_dists, key=lambda x: x[1]))
        self._targets = srtd_targets[: self.max_targets]
        self._num_targets = len(self._targets)

    def __gt__(self, other: ConTextModifier):
        return self._start > other.modifier_span[0]

    def __ge__(self, other):
        return self._start >= other.modifier_span[0]

    def __lt__(self, other):
        return self._end < other.modifier_span[1]

    def __le__(self, other):
        return self._end <= other.modifier_span[1]

    def __len__(self):
        return self._end - self._start

    def __repr__(self):
        return f"<ConTextModifier> [{self._start}, {self._end}, {self.category}]"

    def serialized_representation(self):
        """
        Serialized Representation of the modifier
        """
        dict_repr = dict()
        dict_repr["context_rule"] = self.rule.to_dict()
        dict_repr["start"] = self._start
        dict_repr["end"] = self._end
        dict_repr["max_scope"] = self._max_scope
        dict_repr["scope_start"] = self._scope_start
        dict_repr["scope_end"] = self._scope_end

        return dict_repr

    @classmethod
    def from_serialized_representation(
        cls, serialized_representation
    ) -> ConTextModifier:
        """
        Instantiates the class from the serialized representation
        """
        rule = ConTextRule.from_dict(serialized_representation["context_rule"])

        serialized_representation["context_rule"] = rule
        serialized_representation["doc"] = None

        return ConTextModifier(**serialized_representation)

allowed_types property

Returns the associated allowed types.

category property

Returns the associated category.

direction property

Returns the associated direction.

excluded_types property

Returns the associated excluded types.

max_scope property

Returns the associated maximum scope.

max_targets property

Returns the associated maximum number of targets.

modifier_span property

The spaCy Span object, which is a view of self.doc, covered by this match.

num_targets property

Returns the associated number of targets.

rule property

Returns the associated context rule.

scope_span property

Returns the associated scope.

__init__(context_rule, start, end, doc, scope_start=None, scope_end=None, max_scope=None)

Create a new ConTextModifier from a document span. Each modifier represents a span in the text and a surrounding window. Spans such as entities or other members of span groups that occur within this window can be modified by this ConTextModifier.

Parameters:

Name Type Description Default
context_rule ConTextRule

The ConTextRule object which defines the modifier.

required
start int

The start token index.

required
end int

The end token index (non-inclusive).

required
doc Doc

The spaCy Doc which contains this span. This is needed to initialize the modifier but is not maintained.

required
scope_start Optional[int]

The start token index of the scope.

None
scope_end Optional[int]

The end index of the scope.

None
max_scope Optional[int]

Whether to use scope values rather than sentence boundaries for modifications.

None
Source code in medspacy/context/context_modifier.py
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
def __init__(
    self,
    context_rule: ConTextRule,
    start: int,
    end: int,
    doc: Doc,
    scope_start: Optional[int] = None,
    scope_end: Optional[int] = None,
    max_scope: Optional[int] = None,
):
    """
    Create a new ConTextModifier from a document span. Each modifier represents a span in the text and a surrounding
    window. Spans such as entities or other members of span groups that occur within this window can be modified by
    this ConTextModifier.

    Args:
        context_rule: The ConTextRule object which defines the modifier.
        start: The start token index.
        end: The end token index (non-inclusive).
        doc: The spaCy Doc which contains this span. This is needed to initialize the modifier but is not
            maintained.
        scope_start: The start token index of the scope.
        scope_end: The end index of the scope.
        max_scope: Whether to use scope values rather than sentence boundaries for modifications.
    """
    self._context_rule = context_rule
    self._start = start
    self._end = end

    self._targets = []
    self._num_targets = 0

    self._max_scope = max_scope
    self._scope_start = scope_start
    self._scope_end = scope_end
    if doc is not None and (self._scope_end is None or self._scope_start is None):
        self.__set_scope(doc)

__set_scope(doc)

Applies the direction of the ConTextRule which generated this ConTextModifier to define a scope. If self._max_scope is None, then the default scope is the sentence which it occurs in whichever direction defined by self.direction. For example, if the direction is "forward", the scope will be [self.end: sentence.end]. If the direction is "backward", it will be [self.start: sentence.start].

If self.max_scope is not None and the length of the default scope is longer than self.max_scope, it will be reduced to self.max_scope.

Parameters:

Name Type Description Default
doc Doc

The spaCy doc to use to set scope.

required
Source code in medspacy/context/context_modifier.py
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
def __set_scope(self, doc: Doc):
    """
    Applies the direction of the ConTextRule which generated this ConTextModifier to define a scope. If
    self._max_scope is None, then the default scope is the sentence which it occurs in whichever direction defined by
    self.direction. For example, if the direction is "forward", the scope will be [self.end: sentence.end]. If the
    direction is "backward", it will be [self.start: sentence.start].

    If self.max_scope is not None and the length of the default scope is longer than self.max_scope, it will be
    reduced to self.max_scope.

    Args:
        doc: The spaCy doc to use to set scope.
    """
    # If ConText is set to use defined windows, do that instead of sentence splitting
    if self._max_scope:
        full_scope_span = doc[self._start : self._end]._.window(
            n=self.rule.max_scope
        )
    # Otherwise, use the sentence
    else:
        full_scope_span = doc[self._start].sent
        if full_scope_span is None:
            raise ValueError(
                "ConText failed because sentence boundaries have not been set. Add an upstream component such as the "
                "dependency parser, Sentencizer, or PyRuSH to detect sentence boundaries or initialize ConText with "
                "`max_scope` set to a value greater than 0."
            )

    if self.direction.lower() == "forward":
        self._scope_start, self._scope_end = self._end, full_scope_span.end
        if (
            self.max_scope is not None
            and (self._scope_end - self._scope_start) > self.max_scope
        ):
            self._scope_end = self._end + self.max_scope

    elif self.direction.lower() == "backward":
        self._scope_start, self._scope_end = (
            full_scope_span.start,
            self._start,
        )
        if (
            self.max_scope is not None
            and (self._scope_end - self._scope_start) > self.max_scope
        ):
            self._scope_start = self._start - self.max_scope

    else:  # bidirectional
        self._scope_start, self._scope_end = (
            full_scope_span.start,
            full_scope_span.end,
        )

        # Set the max scope on either side
        # Backwards
        if (
            self.max_scope is not None
            and (self._start - self._scope_start) > self.max_scope
        ):
            self._scope_start = self._start - self.max_scope
        # Forwards
        if (
            self.max_scope is not None
            and (self._scope_end - self._end) > self.max_scope
        ):
            self._scope_end = self._end + self.max_scope

allows(target_label)

Returns whether if a modifier is able to modify a target type.

Parameters:

Name Type Description Default
target_label str

The target type to check.

required

Returns:

Type Description
bool

Whether the modifier is allowed to modify a target of the specified type. True if target_label in

bool

self.allowed_types or if target_label not in self.excluded_tupes. False otherwise.

Source code in medspacy/context/context_modifier.py
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
def allows(self, target_label: str) -> bool:
    """
    Returns whether if a modifier is able to modify a target type.

    Args:
        target_label: The target type to check.

    Returns:
        Whether the modifier is allowed to modify a target of the specified type. True if `target_label` in
        `self.allowed_types` or if `target_label` not in `self.excluded_tupes`. False otherwise.
    """
    if self.allowed_types is not None:
        return target_label in self.allowed_types
    if self.excluded_types is not None:
        return target_label not in self.excluded_types
    return True

from_serialized_representation(serialized_representation) classmethod

Instantiates the class from the serialized representation

Source code in medspacy/context/context_modifier.py
379
380
381
382
383
384
385
386
387
388
389
390
391
@classmethod
def from_serialized_representation(
    cls, serialized_representation
) -> ConTextModifier:
    """
    Instantiates the class from the serialized representation
    """
    rule = ConTextRule.from_dict(serialized_representation["context_rule"])

    serialized_representation["context_rule"] = rule
    serialized_representation["doc"] = None

    return ConTextModifier(**serialized_representation)

limit_scope(other)

If self and other have the same category or if other has a directionality of 'terminate', use the span of other to update the scope of self. Limiting the scope of two modifiers of the same category reduces the number of modifiers. For example, in 'no evidence of CHF, no pneumonia', 'pneumonia' will only be modified by 'no', not 'no evidence of'. 'terminate' modifiers limit the scope of a modifier like 'no evidence of' in 'no evidence of CHF, but there is pneumonia'

Parameters:

Name Type Description Default
other ConTextModifier

The modifier to check against.

required

Returns:

Type Description
bool

Whether the other modifier modified the scope of self.

Source code in medspacy/context/context_modifier.py
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
def limit_scope(self, other: ConTextModifier) -> bool:
    """
    If self and other have the same category or if other has a directionality of 'terminate', use the span of other
    to update the scope of self. Limiting the scope of two modifiers of the same category reduces the number of
    modifiers. For example, in 'no evidence of CHF, no pneumonia', 'pneumonia' will only be modified by 'no', not
    'no evidence of'. 'terminate' modifiers limit the scope of a modifier like 'no evidence of' in 'no evidence of
    CHF, but there is pneumonia'

    Args:
        other: The modifier to check against.

    Returns:
        Whether the other modifier modified the scope of self.
    """
    if not tuple_overlaps(self.scope_span, other.scope_span):
        return False
    if self.direction.upper() == "TERMINATE":
        return False
    # Check if the other modifier is a type which can modify self
    # or if they are the same category. If not, don't reduce scope.
    if (
        (other.direction.upper() != "TERMINATE")
        and (other.category.upper() not in self.rule.terminated_by)
        and (other.category.upper() != self.category.upper())
    ):
        return False

    # If two modifiers have the same category but modify different target types,
    # don't limit scope.
    if self.category == other.category and (
        (self.allowed_types != other.allowed_types)
        or (self.excluded_types != other.excluded_types)
    ):
        return False

    orig_scope = self.scope_span
    if self.direction.lower() in ("forward", "bidirectional"):
        if other > self:
            self._scope_end = min(self._scope_end, other.modifier_span[0])
    if self.direction.lower() in ("backward", "bidirectional"):
        if other < self:
            self._scope_start = max(self._scope_start, other.modifier_span[1])
    return orig_scope != self.scope_span

modifies(target)

Checks whether the target is within the modifier scope and if self is allowed to modify target.

Parameters:

Name Type Description Default
target Span

a spaCy span representing a target concept.

required

Returns:

Type Description
bool

Whether the target is within modifier_scope and if self is allowed to modify the target.

Source code in medspacy/context/context_modifier.py
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
def modifies(self, target: Span) -> bool:
    """
    Checks whether the target is within the modifier scope and if self is allowed to modify target.

    Args:
        target: a spaCy span representing a target concept.

    Returns:
        Whether the target is within `modifier_scope` and if self is allowed to modify the target.
    """
    # If the target and modifier overlap, meaning at least one token
    # one extracted as both a target and modifier, return False
    # to avoid self-modifying concepts

    if tuple_overlaps(
        self.modifier_span, (target.start, target.end)
    ):  # self.overlaps(target):
        return False
    if self.direction in ("TERMINATE", "PSEUDO"):
        return False
    if not self.allows(target.label_.upper()):
        return False

    if tuple_overlaps(self.scope_span, (target.start, target.end)):
        if not self.on_modifies(target):
            return False
        else:
            return True
    return False

modify(target)

Add target to the list of self._targets and increment self._num_targets.

Parameters:

Name Type Description Default
target Span

The spaCy span to add.

required
Source code in medspacy/context/context_modifier.py
321
322
323
324
325
326
327
328
329
def modify(self, target: Span):
    """
    Add target to the list of self._targets and increment self._num_targets.

    Args:
        target: The spaCy span to add.
    """
    self._targets.append(target)
    self._num_targets += 1

on_modifies(target)

If the ConTextRule used to define a ConTextModifier has an on_modifies callback function, evaluate and return either True or False.

Parameters:

Name Type Description Default
target Span

The spaCy span to evaluate.

required

Returns:

Type Description
bool

The result of the on_modifies callback for the rule. True if the callback is None.

Source code in medspacy/context/context_modifier.py
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
def on_modifies(self, target: Span) -> bool:
    """
    If the ConTextRule used to define a ConTextModifier has an `on_modifies` callback function, evaluate and return
    either True or False.

    Args:
        target: The spaCy span to evaluate.

    Returns:
        The result of the `on_modifies` callback for the rule. True if the callback is None.
    """
    if self.rule.on_modifies is None:
        return True
    # Find the span in between the target and modifier
    start = min(target.end, self._end)
    end = max(target.start, self._end)
    span_between = target.doc[start:end]
    rslt = self.rule.on_modifies(
        target, target.doc[self._start : self._end], span_between
    )
    if rslt not in (True, False):
        raise ValueError(
            "The on_modifies function must return either True or False indicating "
            "whether a modify modifies a target. Actual value: {0}".format(rslt)
        )
    return rslt

reduce_targets()

Reduces the number of targets to the n-closest targets based on the value of self.max_targets. If self.max_targets is None, no pruning is done.

Source code in medspacy/context/context_modifier.py
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
def reduce_targets(self):
    """
    Reduces the number of targets to the n-closest targets based on the value of `self.max_targets`. If
    `self.max_targets` is None, no pruning is done.
    """
    if self.max_targets is None or self.num_targets <= self.max_targets:
        return

    target_dists = []
    for target in self._targets:
        dist = min(abs(self._start - target.end), abs(target.start - self._end))
        target_dists.append((target, dist))
    srtd_targets, _ = zip(*sorted(target_dists, key=lambda x: x[1]))
    self._targets = srtd_targets[: self.max_targets]
    self._num_targets = len(self._targets)

serialized_representation()

Serialized Representation of the modifier

Source code in medspacy/context/context_modifier.py
365
366
367
368
369
370
371
372
373
374
375
376
377
def serialized_representation(self):
    """
    Serialized Representation of the modifier
    """
    dict_repr = dict()
    dict_repr["context_rule"] = self.rule.to_dict()
    dict_repr["start"] = self._start
    dict_repr["end"] = self._end
    dict_repr["max_scope"] = self._max_scope
    dict_repr["scope_start"] = self._scope_start
    dict_repr["scope_end"] = self._scope_end

    return dict_repr

update_scope(span)

Changes the scope of self to be the given spaCy span.

Parameters:

Name Type Description Default
span Span

a spaCy Span which contains the scope which a modifier should cover.

required
Source code in medspacy/context/context_modifier.py
193
194
195
196
197
198
199
200
201
def update_scope(self, span: Span):
    """
    Changes the scope of self to be the given spaCy span.

    Args:
        span: a spaCy Span which contains the scope which a modifier should cover.
    """
    self._scope_start = span.start
    self._scope_end = span.end