Views
No views yet
pip install -U transformers1import re
2
3from transformers import pipeline
4
5prefix = """Extract SDTM domain entities from: """
6
7unstructured_text = """
8This previously healthy gentleman presented three days after swallowing a fishbone, reporting subsequent odynophagia, right-sided neck pain, and referred otalgia to the right ear.
9Extensive diagnostic imaging, including a soft-tissue neck X-ray, barium swallow, and CT of the neck, showed no evidence of a radiopaque foreign body or esophageal perforation. Furthermore, an ORL endoscopy and a follow-up EGD confirmed the absence of any foreign objects, though the EGD did identify a soft palate ulcer and an antral nodule.
10Following these procedures, the patient was able to tolerate soft foods without further discomfort. It is highly probable that the fishbone caused localized mucosal micro-trauma before being naturally dislodged and passed through the gastrointestinal tract.
11The patient was discharged with a prescription for viscous lidocaine and ibuprofen 400mg as needed for pain, with a documented maximum daily limit of 1200mg."""
12
13generator = pipeline(
14 "text-generation", model="ClinicalIntelligence/saama_gemma", device="cuda"
15)
16output = generator(
17 [{"role": "user", "content": prefix + unstructured_text}],
18 return_full_text=False,
19 max_new_tokens=16000,
20)[0]
21llm_output = output["generated_text"]
22
23
24def extract_entities(text):
25 """
26 Extracts entities, domains, and justifications from the given text
27 and returns them as a list of dictionaries.
28 """
29 # The regex pattern looks for:
30 # 1. Content inside <think> and </think> tags
31 # 2. Domain prefixed with ~ (ignoring whitespace/special characters like non-breaking spaces)
32 # 3. Extracted entity prefixed with ~~ (up to the next <think> tag or end of string)
33 pattern = r"(?s)<think>\s*(.*?)\s*</think>\s*~([A-Z0-9]+)\s*~~(.*?)(?=<think>|$)"
34
35 # Find all matches in the text
36 matches = re.findall(pattern, text)
37
38 extracted_data = []
39
40 for justification, domain, entity in matches:
41 extracted_data.append(
42 {
43 "domain": domain.strip(),
44 "extracted_entity": entity.strip(),
45 "justification": justification.strip(),
46 }
47 )
48
49 return extracted_data
50
51
52extracted_entities_list = extract_entities(llm_output)
53
54for extracted_entity in extracted_entities_list:
55 print(extracted_entity)1{'domain': 'AE', 'extracted_entity': 'swallowing a fishbone', 'justification': "This is an adverse event (AE) because it is an untoward medical occurrence that happened to the patient. The timing 'three days after' indicates it is a current event that precipitated the visit, not a pre-existing condition from the patient's medical history (MH)."}
2{'domain': 'AE', 'extracted_entity': 'odynophagia', 'justification': 'This is an adverse event (AE) because it is a new symptom reported by the patient, occurring after the inciting event (swallowing the fishbone). It is an untoward medical occurrence and is temporally associated with the current visit, not a historical condition (MH).'}
3{'domain': 'AE', 'extracted_entity': 'neck pain', 'justification': 'This is an adverse event (AE) because it is a new symptom reported by the patient, occurring after the inciting event. It is an untoward medical occurrence and is temporally associated with the current visit, not a historical condition (MH).'}
4{'domain': 'AE', 'extracted_entity': 'otalgia', 'justification': 'This is an adverse event (AE) because it is a new symptom reported by the patient, occurring after the inciting event. It is an untoward medical occurrence and is temporally associated with the current visit, not a historical condition (MH).'}
5{'domain': 'PR', 'extracted_entity': 'soft-tissue neck X-ray', 'justification': 'This is a procedure (PR) because it represents a diagnostic intervention performed on the patient to investigate their symptoms. It is an action taken, not an observation of a spontaneous event (AE) or a pre-existing condition (MH).'}
6{'domain': 'PR', 'extracted_entity': 'barium swallow', 'justification': 'This is a procedure (PR) because it is a diagnostic intervention performed on the patient. It is an action taken, not an observation of a spontaneous event (AE) or a pre-existing condition (MH).'}
7{'domain': 'PR', 'extracted_entity': 'CT of the neck', 'justification': 'This is a procedure (PR) because it is a diagnostic intervention performed on the patient. It is an action taken, not an observation of a spontaneous event (AE) or a pre-existing condition (MH).'}
8{'domain': 'PR', 'extracted_entity': 'ORL endoscopy', 'justification': 'This is a procedure (PR) because it is a diagnostic intervention performed on the patient to evaluate the oropharynx. It is an action taken, not an observation of a spontaneous event (AE) or a pre-existing condition (MH).'}
9{'domain': 'PR', 'extracted_entity': 'EGD', 'justification': 'This is a procedure (PR) because it is a diagnostic intervention (Esophagogastroduodenoscopy) performed on the patient. It is an action taken, not an observation of a spontaneous event (AE) or a pre-existing condition (MH).'}
10{'domain': 'AE', 'extracted_entity': 'soft palate ulcer', 'justification': 'This is an adverse event (AE) because it is an untoward medical occurrence identified during the current visit. The timing is current, not historical (MH). It is not a planned observation like a physical exam (PE) or vital sign (VS), but a newly identified pathological condition.'}
11{'domain': 'AE', 'extracted_entity': 'antral nodule', 'justification': 'This is an adverse event (AE) because it is an untoward medical occurrence identified during the current visit. The timing is current, not historical (MH). It is not a planned observation like a physical exam (PE) or vital sign (VS), but a newly identified pathological condition.'}
12{'domain': 'AE', 'extracted_entity': 'localized mucosal micro-trauma', 'justification': "This is an adverse event (AE) because it is the pathological event diagnosed as the cause of the patient's symptoms. It is an untoward medical occurrence that happened to the patient, not a historical condition (MH)."}
13{'domain': 'CM', 'extracted_entity': 'viscous lidocaine', 'justification': 'This is a concomitant medication (CM) because it is a therapeutic agent prescribed to the patient for a current condition (pain). It is not a historical medication (MH) and is not a procedural agent (AG).'}
14{'domain': 'CM', 'extracted_entity': 'ibuprofen', 'justification': 'This is a concomitant medication (CM) because it is a therapeutic agent prescribed to the patient for a current condition (pain). It is not a historical medication (MH) and is not a procedural agent (AG).'}
15{'domain': 'AE', 'extracted_entity': 'pain', 'justification': 'This is an adverse event (AE) because it is a symptom for which the patient is receiving treatment. The timing is current, not historical (MH).'}
16{'domain': 'DS', 'extracted_entity': 'discharged', 'justification': "This entity describes the patient's disposition (DS) at the end of the encounter. It indicates the outcome of the visit and the patient's status relative to the clinical setting."}
17{'domain': 'DM', 'extracted_entity': 'gentleman', 'justification': 'This entity describes the sex of the patient, which is a fundamental demographic characteristic. It is not a medical event, finding, or intervention, thus it belongs in the Demographics (DM) domain.'}
18{'domain': 'DM', 'extracted_entity': 'previously healthy', 'justification': "This entity describes the patient's age, which is a fundamental demographic characteristic. It is not a medical event, finding, or intervention, thus it belongs in the Demographics (DM) domain."}
19{'domain': 'DS', 'extracted_entity': 'discharged', 'justification': "This entity describes the patient's disposition (DS) at the end of the encounter. It indicates the outcome of the visit and the patient's status relative to the clinical setting."}