def test_limit_scope3(self): """Test that two modifiers of the same type limit the scope of the first modifier.""" doc = nlp("no evidence of CHF, neg for pneumonia") item = ConTextItem("no evidence of", "DEFINITE_NEGATED_EXISTENCE", "FORWARD") item2 = ConTextItem("neg for", "DEFINITE_NEGATED_EXISTENCE", "FORWARD") tag_object = TagObject(item, 0, 3, doc) tag_object2 = TagObject(item2, 5, 7, doc) assert tag_object.limit_scope(tag_object2)
def test_terminate_limit_scope_custom2(self): """Test that a modifier will be explicitly terminated by a modifier with a category in terminated_by.""" doc = nlp("flu is negative, pneumonia is positive.") item = ConTextItem("negative", "NEGATED_EXISTENCE", rule="BACKWARD") item2 = ConTextItem("positive", "POSITIVE_EXISTENCE", rule="BACKWARD", terminated_by={"NEGATED_EXISTENCE"}) tag_object = TagObject(item, 2, 3, doc) tag_object2 = TagObject(item2, 6, 7, doc) assert tag_object2.limit_scope(tag_object)
def test_terminate_limit_scope_custom(self): """Test that a modifier will be explicitly terminated by a modifier with a category in terminated_by.""" doc = nlp("negative for flu, positive for pneumonia.") item = ConTextItem("negative for", "NEGATED_EXISTENCE", rule="FORWARD", terminated_by={"POSITIVE_EXISTENCE"}) item2 = ConTextItem("positive for", "POSITIVE_EXISTENCE", rule="FORWARD") tag_object = TagObject(item, 0, 2, doc) tag_object2 = TagObject(item2, 4, 6, doc) assert tag_object.limit_scope(tag_object2)
def test_terminate_limit_scope_backward(self): """Test that a 'TERMINATE' modifier will limit the scope of a 'BACKWARD' modifier. """ doc = nlp("Pt has chf but pneumonia is ruled out") item = ConTextItem("is ruled out", "NEGATED_EXISTENCE", "BACKWARD") tag_object = TagObject(item, 6, 8, doc) item2 = ConTextItem("but", "TERMINATE", "TERMINATE") tag_object2 = TagObject(item2, 3, 4, doc) assert tag_object.limit_scope(tag_object2)
def test_overlapping_target(self): """Test that a modifier will not modify a target if it is in the same span as the modifier. """ doc = nlp("Pt presents for r/o of pneumonia.") item = ConTextItem("r/o", "UNCERTAIN", rule="BIDIRECTIONAL") tag_object = TagObject(item, 3, 4, doc) target = Span(doc, 3, 4, "TEST") assert tag_object.modifies(target) is False
def test_on_modifies_false(self): def on_modifies(target, modifier, span_between): return False item = ConTextItem("no evidence of", "NEGATED_EXISTENCE", on_modifies=on_modifies) doc = nlp("There is no evidence of pneumonia or chf.") doc.ents = (doc[5:6], doc[7:8]) tag = TagObject(item, 2, 5, doc) assert tag.modifies(doc.ents[0]) is False
def test_on_modifies_arg_types(self): def check_arg_types(target, modifier, span_between): for arg in (target, modifier, span_between): if not isinstance(arg, spacy.tokens.Span): return False return True item = ConTextItem("no evidence of", "NEGATED_EXISTENCE", on_modifies=check_arg_types) doc = nlp("There is no evidence of pneumonia or chf.") doc.ents = (doc[5:6], doc[7:8]) tag = TagObject(item, 2, 5, doc) assert tag.modifies(doc.ents[0]) is True
def test_max_scope_none(self): """Test that if max_scope is not None it will reduce the range of text which is modified. """ doc = self.create_num_target_examples() assert len(doc.ents) == 3 item = ConTextItem( "vs", category="UNCERTAIN", rule="BIDIRECTIONAL", max_scope=None ) tag_object = TagObject(item, 5, 6, doc) for target in doc.ents: if tag_object.modifies(target): tag_object.modify(target) assert tag_object.num_targets == 3
def test_on_modifies_arg_values(self): def check_arg_types(target, modifier, span_between): if target.lower_ != "chf": return False if modifier.lower_ != "no evidence of": return False if span_between.lower_ != "pneumonia or": return False return True item = ConTextItem("no evidence of", "NEGATED_EXISTENCE", on_modifies=check_arg_types) doc = nlp("There is no evidence of pneumonia or chf.") doc.ents = (doc[5:6], doc[7:8]) tag = TagObject(item, 2, 5, doc) assert tag.modifies(doc.ents[1]) is True
def context_graph(self): doc = nlp("There is no evidence of pneumonia but there is chf.") item_data1 = ConTextItem("no evidence of", "DEFINITE_NEGATED_EXISTENCE", "forward") tag_object1 = TagObject(item_data1, 2, 5, doc) item_data2 = ConTextItem("evidence of", "DEFINITE_EXISTENCE", "forward") tag_object2 = TagObject(item_data2, 3, 5, doc) item_data3 = ConTextItem("but", "TERMINATE", "TERMINATE") tag_object3 = TagObject(item_data3, 6, 7, doc) graph = ConTextGraph() graph.modifiers = [tag_object1, tag_object2, tag_object3] return doc, graph
def create_objects(self): doc = nlp( "family history of breast cancer but no diabetes. She has afib.") item = ConTextItem("family history of", "FAMILY_HISTORY", rule="FORWARD") tag_object = TagObject(item, 0, 3, doc) return doc, item, tag_object
def test_set_scope_context_window_no_sentences(self): """Test that setting the scope succeeds if sentence boundaries haven't been set but _use_context_window is True.""" doc = nlp.tokenizer( "family history of breast cancer but no diabetes. She has afib.") item = ConTextItem("family history of", "FAMILY_HISTORY", rule="FORWARD", max_scope=2) tag_object = TagObject(item, 0, 3, doc, _use_context_window=True) assert tag_object.scope == doc[3:5]
def test_set_scope_fails_no_sentences(self): """Test that setting the scope fails if sentence boundaries haven't been set.""" doc = nlp.tokenizer( "family history of breast cancer but no diabetes. She has afib.") item = ConTextItem("family history of", "FAMILY_HISTORY", rule="FORWARD") with pytest.raises(ValueError) as exception_info: # This should fail because doc.sents are None TagObject(item, 0, 3, doc) exception_info.match( "ConText failed because sentence boundaries have not been set")
def test_no_limit_scope_same_category_different_allowed_types(self): """Test that a two TagObjects of the same type but with different allowed types does not limits the scope of the tag object. """ doc = nlp("no history of travel to Puerto Rico, neg for pneumonia") item = ConTextItem( "no history of", "DEFINITE_NEGATED_EXISTENCE", "FORWARD", allowed_types={"TRAVEL"}, ) item2 = ConTextItem( "neg for", "DEFINITE_NEGATED_EXISTENCE", "FORWARD", allowed_types={"CONDITION"}, ) tag_object = TagObject(item, 0, 3, doc) tag_object2 = TagObject(item2, 8, 10, doc) assert not tag_object.limit_scope(tag_object2)
def test_set_scope_fails_no_sentences(self): """Test that setting the scope fails if sentence boundaries haven't been set.""" nlp = spacy.blank("en") assert nlp.pipeline == [] doc = nlp("family history of breast cancer but no diabetes. She has afib.") item = ConTextItem("family history of", "FAMILY_HISTORY", rule="FORWARD") with pytest.raises(ValueError) as exception_info: # This should fail because doc.sents are None TagObject(item, 0, 3, doc) exception_info.match( "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." )
def test_not_remove_modifiers_overlap_target(self): """Test that a modifier which overlaps with a target is not pruned but does not modify itself.""" doc = nlp("The patient has heart failure.") doc.ents = (Span(doc, 3, 5, "CONDITION"), ) context_item = ConTextItem("failure", "MODIFIER") tag_object = TagObject(context_item, 4, 5, doc) graph = ConTextGraph(remove_overlapping_modifiers=False) graph.modifiers = [tag_object] graph.targets = doc.ents graph.prune_modifiers() graph.apply_modifiers() assert overlap_target_modifiers(tag_object.span, doc.ents[0]) assert len(graph.modifiers) == 1
def test_no_types(self): """Test that not specifying allowed_types or excluded_types will modify all targets.""" doc = self.create_target_type_examples() item = ConTextItem( "no history of travel to", category="DEFINITE_NEGATED_EXISTENCE", rule="FORWARD", ) tag_object = TagObject(item, 0, 5, doc) tag_object.set_scope() travel, condition = doc.ents # "puerto rico", "pneumonia" assert tag_object.modifies(travel) is True assert tag_object.modifies(condition) is True
def test_max_targets_none(self): """Check that if max_targets is None it will not reduce the targets to the two closest ents. """ doc = self.create_num_target_examples() assert len(doc.ents) == 3 item = ConTextItem( "vs", category="UNCERTAIN", rule="BIDIRECTIONAL", max_targets=None ) # Set "vs" to be the modifier tag_object = TagObject(item, 5, 6, doc) for target in doc.ents: tag_object.modify(target) assert tag_object.num_targets == 3 tag_object.reduce_targets() assert tag_object.num_targets == 3
def test_max_targets_less_than_targets(self): """Check that if max_targets is not None it will reduce the targets to the two closest ents. """ doc = self.create_num_target_examples() assert len(doc.ents) == 3 item = ConTextItem( "vs", category="UNCERTAIN", rule="BIDIRECTIONAL", max_targets=2 ) # Set "vs" to be the modifier tag_object = TagObject(item, 5, 6, doc) for target in doc.ents: tag_object.modify(target) assert tag_object.num_targets == 3 tag_object.reduce_targets() assert tag_object.num_targets == 2 for target in tag_object._targets: assert target.lower_ in ("pneumonia", "copd")
def test_limit_scope2(self): doc, item, tag_object = self.create_objects() item2 = ConTextItem("but", "TERMINATE", "TERMINATE") tag_object2 = TagObject(item2, 2, 4, doc) assert not tag_object2.limit_scope(tag_object)
def test_limit_scope(self): """Test that a 'TERMINATE' TagObject limits the scope of the tag object""" doc, item, tag_object = self.create_objects() item2 = ConTextItem("but", "TERMINATE", "TERMINATE") tag_object2 = TagObject(item2, 2, 4, doc) assert tag_object.limit_scope(tag_object2)