Spaces:
Sleeping
Sleeping
Commit
·
2981176
1
Parent(s):
e81f16d
refactor
Browse files
README.md
CHANGED
@@ -11,38 +11,115 @@ pinned: false
|
|
11 |
|
12 |
# Medical Knowledge Graph Construction (medKGC)
|
13 |
|
14 |
-
##
|
|
|
15 |
|
16 |
-
|
17 |
|
18 |
-
###
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
4. Interactive Visualization: Utilizes streamlit-agraph to create an interactive graph representation of entities and their relationships.
|
23 |
-
5. Labeled Text Display: Shows the input text with highlighted entities using streamlit_text_label.
|
24 |
|
25 |
-
###
|
26 |
-
|
27 |
-
|
28 |
-
|
|
|
|
|
|
|
29 |
|
30 |
-
|
|
|
|
|
|
|
31 |
|
32 |
-
|
|
|
|
|
|
|
33 |
|
34 |
-
|
35 |
|
36 |
-
###
|
37 |
-
1.
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
42 |
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
|
|
|
|
|
|
|
|
|
|
47 |
|
48 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
|
12 |
# Medical Knowledge Graph Construction (medKGC)
|
13 |
|
14 |
+
## Overview
|
15 |
+
medKGC is a medical text knowledge graph construction and review system. It supports entity recognition, relation extraction, and visualization of medical reports, providing a convenient review interface.
|
16 |
|
17 |
+
## Core Features
|
18 |
|
19 |
+
### 1. Data Processing
|
20 |
+
- **Position Conversion**: Support word-level and char-level position conversion
|
21 |
+
- **Entity Conversion**: Convert between JSON format and Selection objects
|
22 |
+
- **Relation Extraction**: Entity ID-based relation mapping and reconstruction
|
|
|
|
|
23 |
|
24 |
+
### 2. Entity Annotation
|
25 |
+
- **Label Types**:
|
26 |
+
- OBS-DP: Observation definitely present (Red)
|
27 |
+
- ANAT-DP: Anatomy definitely present (Cyan)
|
28 |
+
- OBS-U: Observation uncertain (Yellow)
|
29 |
+
- OBS-DA: Observation definitely absent (Gray)
|
30 |
+
- **Interactive Annotation**: Support entity selection and annotation
|
31 |
|
32 |
+
### 3. Relation Visualization
|
33 |
+
- **Node Merging**: Automatically merge entities with same text
|
34 |
+
- **Color Coding**: Different colors for different entity types
|
35 |
+
- **Dynamic Updates**: Support real-time graph updates
|
36 |
|
37 |
+
### 4. Review Process
|
38 |
+
- **Report Selection**: Display pending and reviewed reports separately
|
39 |
+
- **Status Saving**: Automatically save review status and modifications
|
40 |
+
- **Batch Processing**: Support continuous review of multiple reports
|
41 |
|
42 |
+
## Technical Implementation
|
43 |
|
44 |
+
### Data Structures
|
45 |
+
1. **Entity Data**
|
46 |
+
```json
|
47 |
+
{
|
48 |
+
"entities": {
|
49 |
+
"1": {
|
50 |
+
"tokens": "entity text",
|
51 |
+
"label": "entity type",
|
52 |
+
"start_ix": "word-level start position",
|
53 |
+
"end_ix": "word-level end position",
|
54 |
+
"relations": [["relation type", "target entity ID"]]
|
55 |
+
}
|
56 |
+
}
|
57 |
+
}
|
58 |
+
```
|
59 |
|
60 |
+
2. **Selection Object**
|
61 |
+
```python
|
62 |
+
@dataclass
|
63 |
+
class Selection:
|
64 |
+
start: int # char-level start position
|
65 |
+
end: int # char-level end position
|
66 |
+
text: str # entity text
|
67 |
+
labels: List[str] # entity type list
|
68 |
+
```
|
69 |
|
70 |
+
### Core Algorithms
|
71 |
+
1. **Position Conversion**
|
72 |
+
```python
|
73 |
+
def word_to_char_span(text, start_ix, end_ix):
|
74 |
+
"""Convert word-level position to character-level range"""
|
75 |
+
```
|
76 |
+
|
77 |
+
2. **Relation Reconstruction**
|
78 |
+
```python
|
79 |
+
def find_relations_with_entities(entities, entities_data):
|
80 |
+
"""Rebuild relations based on entity text matching"""
|
81 |
+
```
|
82 |
+
|
83 |
+
## Deployment
|
84 |
+
|
85 |
+
### Requirements
|
86 |
+
- Python 3.7+
|
87 |
+
- Streamlit 1.39.0+
|
88 |
+
- streamlit_text_label
|
89 |
+
- streamlit_agraph
|
90 |
+
|
91 |
+
### Installation
|
92 |
+
1. Clone repository
|
93 |
+
```bash
|
94 |
+
git clone https://github.com/your-repo/medKGC.git
|
95 |
+
```
|
96 |
+
|
97 |
+
2. Install dependencies
|
98 |
+
```bash
|
99 |
+
pip install -r requirements.txt
|
100 |
+
```
|
101 |
+
|
102 |
+
3. Run application
|
103 |
+
```bash
|
104 |
+
streamlit run app.py
|
105 |
+
```
|
106 |
+
|
107 |
+
## Future Plans
|
108 |
+
1. [ ] Add relation editing functionality
|
109 |
+
2. [ ] Support custom entity types
|
110 |
+
3. [ ] Add data export functionality
|
111 |
+
4. [ ] Integrate machine learning models
|
112 |
+
5. [ ] Add annotation functionality
|
113 |
+
|
114 |
+
## Contributing
|
115 |
+
Welcome to contribute through:
|
116 |
+
1. Submit Issues for bug reports or suggestions
|
117 |
+
2. Submit Pull Requests to improve code
|
118 |
+
3. Improve documentation and comments
|
119 |
+
|
120 |
+
## License
|
121 |
+
MIT License
|
122 |
+
|
123 |
+
---
|
124 |
+
|
125 |
+
[Chinese version above]
|
app.py
CHANGED
@@ -1,91 +1,275 @@
|
|
1 |
import streamlit as st
|
|
|
2 |
from streamlit_text_label import label_select, Selection
|
3 |
from streamlit_agraph import agraph, Node, Edge, Config
|
4 |
-
|
5 |
-
# Import Relation class
|
6 |
from dataclasses import dataclass
|
7 |
|
|
|
8 |
@dataclass
|
9 |
class Relation:
|
10 |
source: Selection
|
11 |
target: Selection
|
12 |
label: str
|
13 |
|
14 |
-
def mock_entity_recognition(text):
|
15 |
-
# Simulate entity recognition functionality
|
16 |
-
entities = [
|
17 |
-
Selection(start=0, end=12, text="FINAL REPORT",
|
18 |
-
labels=["REPORT_TYPE"]),
|
19 |
-
Selection(start=22, end=33, text="Pseudomonas", labels=["DISEASE"]),
|
20 |
-
Selection(start=39, end=49, text="intubation", labels=["PROCEDURE"]),
|
21 |
-
Selection(start=62, end=67, text="study", labels=["EXAM"]),
|
22 |
-
Selection(start=116, end=126, text="Substantial", labels=["SEVERITY"]),
|
23 |
-
Selection(start=127, end=145, text="bilateral pleural",
|
24 |
-
labels=["ANATOMY"]),
|
25 |
-
Selection(start=146, end=155, text="effusions", labels=["OBSERVATION"])
|
26 |
-
]
|
27 |
-
return entities
|
28 |
|
|
|
|
|
|
|
|
|
29 |
|
30 |
-
def mock_relation_extraction(entities):
|
31 |
-
# Simulate relation extraction functionality
|
32 |
-
relations = [
|
33 |
-
Relation(source=entities[0],
|
34 |
-
target=entities[1], label="DISEASE_CAUSE"),
|
35 |
-
Relation(source=entities[1], target=entities[2],
|
36 |
-
label="PROCEDURE_EFFECT"),
|
37 |
-
Relation(source=entities[2], target=entities[3], label="EXAM_RESULT"),
|
38 |
-
Relation(source=entities[3], target=entities[4],
|
39 |
-
label="SEVERITY_LEVEL"),
|
40 |
-
Relation(source=entities[4], target=entities[5],
|
41 |
-
label="ANATOMY_LOCATION"),
|
42 |
-
]
|
43 |
|
44 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
|
46 |
|
47 |
def create_graph(entities, relations):
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
return agraph(nodes=nodes, edges=edges, config=config)
|
53 |
|
54 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
55 |
def main():
|
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 |
-
for relation in relations:
|
84 |
-
st.write(f"{relation.source.text} --{relation.label}--> {relation.target.text}")
|
85 |
|
86 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
87 |
st.subheader("Entity Relationship Graph:")
|
88 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
89 |
|
90 |
if __name__ == "__main__":
|
91 |
main()
|
|
|
1 |
import streamlit as st
|
2 |
+
import json
|
3 |
from streamlit_text_label import label_select, Selection
|
4 |
from streamlit_agraph import agraph, Node, Edge, Config
|
|
|
|
|
5 |
from dataclasses import dataclass
|
6 |
|
7 |
+
|
8 |
@dataclass
|
9 |
class Relation:
|
10 |
source: Selection
|
11 |
target: Selection
|
12 |
label: str
|
13 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
|
15 |
+
def load_data():
|
16 |
+
"""Load data from dev.json"""
|
17 |
+
with open('dev.json', 'r') as f:
|
18 |
+
return json.load(f)
|
19 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
|
21 |
+
def save_data(data):
|
22 |
+
"""Save data to dev.json"""
|
23 |
+
with open('dev.json', 'w') as f:
|
24 |
+
json.dump(data, f, indent=4)
|
25 |
+
|
26 |
+
|
27 |
+
def get_label_color(label):
|
28 |
+
"""Return color based on label type"""
|
29 |
+
color_map = {
|
30 |
+
'OBS-DP': '#FF6B6B', # Red - Observation definitely present
|
31 |
+
'ANAT-DP': '#4ECDC4', # Cyan - Anatomy definitely present
|
32 |
+
'OBS-U': '#FFD93D', # Yellow - Observation uncertain
|
33 |
+
'OBS-DA': '#95A5A6', # Gray - Observation definitely absent
|
34 |
+
}
|
35 |
+
return color_map.get(label, '#666666') # Default color
|
36 |
|
37 |
|
38 |
def create_graph(entities, relations):
|
39 |
+
"""Create entity relationship graph, merge nodes with same text"""
|
40 |
+
# Track created nodes using dict, key is entity text
|
41 |
+
nodes_dict = {}
|
42 |
+
nodes = []
|
43 |
+
|
44 |
+
# First create all unique nodes
|
45 |
+
for entity in entities:
|
46 |
+
if entity.text not in nodes_dict:
|
47 |
+
# Create new node
|
48 |
+
node = Node(
|
49 |
+
id=entity.text,
|
50 |
+
label=f"{entity.text}\n({entity.labels[0]})",
|
51 |
+
size=25,
|
52 |
+
color=get_label_color(entity.labels[0])
|
53 |
+
)
|
54 |
+
nodes.append(node)
|
55 |
+
nodes_dict[entity.text] = node
|
56 |
+
|
57 |
+
# Create edges using node text as source and target
|
58 |
+
edges = []
|
59 |
+
for relation in relations:
|
60 |
+
# Check if source and target exist
|
61 |
+
if relation.source.text in nodes_dict and relation.target.text in nodes_dict:
|
62 |
+
edge = Edge(
|
63 |
+
source=relation.source.text,
|
64 |
+
target=relation.target.text,
|
65 |
+
label=relation.label,
|
66 |
+
color="#666666" # Unified edge color
|
67 |
+
)
|
68 |
+
edges.append(edge)
|
69 |
+
|
70 |
+
config = Config(
|
71 |
+
width=750,
|
72 |
+
height=500,
|
73 |
+
directed=True,
|
74 |
+
physics=True,
|
75 |
+
hierarchical=False,
|
76 |
+
nodeHighlightBehavior=True,
|
77 |
+
highlightColor="#F7A7A6",
|
78 |
+
)
|
79 |
+
|
80 |
return agraph(nodes=nodes, edges=edges, config=config)
|
81 |
|
82 |
|
83 |
+
def word_to_char_position(text, word_index):
|
84 |
+
"""Convert word position to character position"""
|
85 |
+
words = text.split()
|
86 |
+
char_start = 0
|
87 |
+
|
88 |
+
# If word_index out of range, return text end
|
89 |
+
if word_index >= len(words):
|
90 |
+
return len(text)
|
91 |
+
|
92 |
+
# Traverse all words before target word
|
93 |
+
for i in range(word_index):
|
94 |
+
char_start += len(words[i]) + 1 # +1 for space
|
95 |
+
|
96 |
+
return char_start
|
97 |
+
|
98 |
+
|
99 |
+
def word_to_char_span(text, start_ix, end_ix):
|
100 |
+
"""Convert word start and end positions to character span"""
|
101 |
+
char_start = word_to_char_position(text, start_ix)
|
102 |
+
# If start equals end, it's a single word
|
103 |
+
if start_ix == end_ix:
|
104 |
+
char_end = char_start + len(text.split()[start_ix])
|
105 |
+
else:
|
106 |
+
# If multiple words, calculate to end position
|
107 |
+
char_end = word_to_char_position(
|
108 |
+
text, end_ix) + len(text.split()[end_ix])
|
109 |
+
return char_start, char_end
|
110 |
+
|
111 |
+
|
112 |
+
def entities2Selection(text, entities_data):
|
113 |
+
"""Convert entities data to Selection objects list"""
|
114 |
+
selections = []
|
115 |
+
for entity_id, entity in entities_data.items():
|
116 |
+
# Convert word positions to char positions
|
117 |
+
char_start, char_end = word_to_char_span(
|
118 |
+
text,
|
119 |
+
entity['start_ix'],
|
120 |
+
entity['end_ix']
|
121 |
+
)
|
122 |
+
|
123 |
+
selection = Selection(
|
124 |
+
start=char_start,
|
125 |
+
end=char_end,
|
126 |
+
text=entity['tokens'],
|
127 |
+
labels=[entity['label']],
|
128 |
+
)
|
129 |
+
selections.append(selection)
|
130 |
+
return selections
|
131 |
+
|
132 |
+
|
133 |
+
def selection2entities(selections):
|
134 |
+
"""Convert Selection objects list to entities data"""
|
135 |
+
entities = {}
|
136 |
+
for i, selection in enumerate(selections, 1):
|
137 |
+
entities[str(i)] = {
|
138 |
+
"tokens": selection.text,
|
139 |
+
"label": selection.labels[0],
|
140 |
+
"start_ix": selection.start,
|
141 |
+
"end_ix": selection.end,
|
142 |
+
"relations": [] # Initialize empty relations list
|
143 |
+
}
|
144 |
+
return entities
|
145 |
+
|
146 |
+
|
147 |
+
def find_relations_with_entities(entities, entities_data):
|
148 |
+
"""Find relations between current entities based on original entities_data"""
|
149 |
+
# Create text to entity mapping
|
150 |
+
text_to_entity = {e.text: e for e in entities}
|
151 |
+
|
152 |
+
# Create tokens to entity_id mapping
|
153 |
+
tokens_to_id = {entity['tokens']: entity_id
|
154 |
+
for entity_id, entity in entities_data.items()}
|
155 |
+
|
156 |
+
# Create id to tokens mapping
|
157 |
+
id_to_tokens = {entity_id: entity['tokens']
|
158 |
+
for entity_id, entity in entities_data.items()}
|
159 |
+
|
160 |
+
relations = []
|
161 |
+
# Iterate through each entity in current entities
|
162 |
+
for source_text, source_entity in text_to_entity.items():
|
163 |
+
# Find corresponding entity ID in original data
|
164 |
+
for entity_id, entity in entities_data.items():
|
165 |
+
if entity['tokens'] == source_text:
|
166 |
+
# Iterate through all relations of this entity
|
167 |
+
for relation in entity.get('relations', []):
|
168 |
+
target_id = relation[1]
|
169 |
+
# Get target entity text
|
170 |
+
target_text = id_to_tokens.get(target_id)
|
171 |
+
# If target entity exists in current entities
|
172 |
+
if target_text and target_text in text_to_entity:
|
173 |
+
relations.append(Relation(
|
174 |
+
source=source_entity,
|
175 |
+
target=text_to_entity[target_text],
|
176 |
+
label=relation[0]
|
177 |
+
))
|
178 |
+
|
179 |
+
return relations
|
180 |
+
|
181 |
+
|
182 |
def main():
|
183 |
+
"""Main application"""
|
184 |
+
st.title("Medical Report Review System")
|
185 |
+
|
186 |
+
# Load data
|
187 |
+
if 'data' not in st.session_state:
|
188 |
+
st.session_state.data = load_data()
|
189 |
+
|
190 |
+
# Create two columns layout
|
191 |
+
col1, col2 = st.columns(2)
|
192 |
+
|
193 |
+
with col1:
|
194 |
+
st.subheader("Reports to Review")
|
195 |
+
# Get unreviewed reports list
|
196 |
+
unreviewed_reports = [
|
197 |
+
report_id for report_id, content in st.session_state.data.items()
|
198 |
+
if 'reviewed' not in content
|
199 |
+
]
|
200 |
+
selected_report = st.selectbox(
|
201 |
+
"Select Report",
|
202 |
+
unreviewed_reports,
|
203 |
+
key="unreviewed"
|
204 |
)
|
205 |
|
206 |
+
with col2:
|
207 |
+
st.subheader("Reviewed Reports")
|
208 |
+
# Get reviewed reports list
|
209 |
+
reviewed_reports = [
|
210 |
+
report_id for report_id, content in st.session_state.data.items()
|
211 |
+
if content.get('reviewed', False)
|
212 |
+
]
|
213 |
+
st.selectbox(
|
214 |
+
"Completed Reports",
|
215 |
+
reviewed_reports if reviewed_reports else ['None'],
|
216 |
+
key="reviewed"
|
217 |
+
)
|
218 |
+
|
219 |
+
if selected_report:
|
220 |
+
report_data = st.session_state.data[selected_report]
|
221 |
+
|
222 |
+
# Display report text
|
223 |
+
st.subheader("Report Content:")
|
224 |
+
st.markdown(report_data['text'])
|
225 |
|
226 |
+
# Display entities and relations
|
227 |
+
entities_data = report_data['entities']
|
228 |
|
229 |
+
# Convert entities data to Selection objects
|
230 |
+
entities = entities2Selection(report_data['text'], entities_data)
|
|
|
|
|
231 |
|
232 |
+
# Display annotation results
|
233 |
+
st.subheader("Entity Annotation:")
|
234 |
+
selections = label_select(
|
235 |
+
body=report_data['text'],
|
236 |
+
labels=list(set(e.labels[0] for e in entities)),
|
237 |
+
selections=entities,
|
238 |
+
)
|
239 |
+
# Display selections in text format
|
240 |
+
st.write(selections)
|
241 |
+
|
242 |
+
# Display relationship graph
|
243 |
st.subheader("Entity Relationship Graph:")
|
244 |
+
|
245 |
+
# Add update graph button
|
246 |
+
if st.button("Update Graph"):
|
247 |
+
# Update relations using current selections
|
248 |
+
relations = find_relations_with_entities(selections, entities_data)
|
249 |
+
create_graph(selections, relations)
|
250 |
+
else:
|
251 |
+
# Display original graph
|
252 |
+
relations = find_relations_with_entities(entities, entities_data)
|
253 |
+
create_graph(entities, relations)
|
254 |
+
|
255 |
+
# Add review functionality
|
256 |
+
if st.button("Mark as Reviewed"):
|
257 |
+
# Convert selections back to entities data
|
258 |
+
updated_entities = selection2entities(selections)
|
259 |
+
|
260 |
+
# Keep original relations
|
261 |
+
for entity_id, entity in updated_entities.items():
|
262 |
+
if entity_id in entities_data:
|
263 |
+
entity['relations'] = entities_data[entity_id]['relations']
|
264 |
+
|
265 |
+
st.session_state.data[selected_report]['reviewed'] = {
|
266 |
+
'entities': updated_entities
|
267 |
+
}
|
268 |
+
|
269 |
+
save_data(st.session_state.data)
|
270 |
+
st.success("Review status saved!")
|
271 |
+
st.rerun()
|
272 |
+
|
273 |
|
274 |
if __name__ == "__main__":
|
275 |
main()
|
dev.json
ADDED
The diff for this file is too large to render.
See raw diff
|
|
mockData.json
DELETED
@@ -1,138 +0,0 @@
|
|
1 |
-
{'data_source': 'MIMIC-CXR',
|
2 |
-
'data_split': 'train',
|
3 |
-
'entities': {'1': {'end_ix': 29,
|
4 |
-
'label': 'OBS-DP',
|
5 |
-
'relations': [['modify', '4']],
|
6 |
-
'start_ix': 29,
|
7 |
-
'tokens': 'Substantial'},
|
8 |
-
'10': {'end_ix': 44,
|
9 |
-
'label': 'OBS-DP',
|
10 |
-
'relations': [['suggestive_of', '16']],
|
11 |
-
'start_ix': 44,
|
12 |
-
'tokens': 'configuration'},
|
13 |
-
'11': {'end_ix': 47,
|
14 |
-
'label': 'OBS-DP',
|
15 |
-
'relations': [['modify', '12']],
|
16 |
-
'start_ix': 47,
|
17 |
-
'tokens': 'collection'},
|
18 |
-
'12': {'end_ix': 49,
|
19 |
-
'label': 'OBS-DP',
|
20 |
-
'relations': [['located_at', '14'],
|
21 |
-
['suggestive_of', '16']],
|
22 |
-
'start_ix': 49,
|
23 |
-
'tokens': 'opacification'},
|
24 |
-
'13': {'end_ix': 52,
|
25 |
-
'label': 'ANAT-DP',
|
26 |
-
'relations': [['modify', '14']],
|
27 |
-
'start_ix': 52,
|
28 |
-
'tokens': 'left'},
|
29 |
-
'14': {'end_ix': 53,
|
30 |
-
'label': 'ANAT-DP',
|
31 |
-
'relations': [],
|
32 |
-
'start_ix': 53,
|
33 |
-
'tokens': 'base'},
|
34 |
-
'15': {'end_ix': 59,
|
35 |
-
'label': 'OBS-U',
|
36 |
-
'relations': [['modify', '16']],
|
37 |
-
'start_ix': 59,
|
38 |
-
'tokens': 'loculated'},
|
39 |
-
'16': {'end_ix': 60,
|
40 |
-
'label': 'OBS-U',
|
41 |
-
'relations': [],
|
42 |
-
'start_ix': 60,
|
43 |
-
'tokens': 'fluid'},
|
44 |
-
'17': {'end_ix': 67,
|
45 |
-
'label': 'OBS-DP',
|
46 |
-
'relations': [['modify', '20']],
|
47 |
-
'start_ix': 67,
|
48 |
-
'tokens': 'increased'},
|
49 |
-
'18': {'end_ix': 68,
|
50 |
-
'label': 'ANAT-DP',
|
51 |
-
'relations': [['modify', '19']],
|
52 |
-
'start_ix': 68,
|
53 |
-
'tokens': 'pulmonary'},
|
54 |
-
'19': {'end_ix': 69,
|
55 |
-
'label': 'ANAT-DP',
|
56 |
-
'relations': [],
|
57 |
-
'start_ix': 69,
|
58 |
-
'tokens': 'venous'},
|
59 |
-
'2': {'end_ix': 30,
|
60 |
-
'label': 'ANAT-DP',
|
61 |
-
'relations': [['modify', '3']],
|
62 |
-
'start_ix': 30,
|
63 |
-
'tokens': 'bilateral'},
|
64 |
-
'20': {'end_ix': 70,
|
65 |
-
'label': 'OBS-DP',
|
66 |
-
'relations': [['located_at', '19']],
|
67 |
-
'start_ix': 70,
|
68 |
-
'tokens': 'pressure'},
|
69 |
-
'21': {'end_ix': 79,
|
70 |
-
'label': 'ANAT-DP',
|
71 |
-
'relations': [['modify', '23']],
|
72 |
-
'start_ix': 79,
|
73 |
-
'tokens': 'left'},
|
74 |
-
'22': {'end_ix': 80,
|
75 |
-
'label': 'ANAT-DP',
|
76 |
-
'relations': [['modify', '23']],
|
77 |
-
'start_ix': 80,
|
78 |
-
'tokens': 'upper'},
|
79 |
-
'23': {'end_ix': 81,
|
80 |
-
'label': 'ANAT-DP',
|
81 |
-
'relations': [],
|
82 |
-
'start_ix': 81,
|
83 |
-
'tokens': 'zone'},
|
84 |
-
'24': {'end_ix': 87,
|
85 |
-
'label': 'ANAT-DP',
|
86 |
-
'relations': [],
|
87 |
-
'start_ix': 87,
|
88 |
-
'tokens': 'cavitary'},
|
89 |
-
'25': {'end_ix': 88,
|
90 |
-
'label': 'OBS-DP',
|
91 |
-
'relations': [['located_at', '24']],
|
92 |
-
'start_ix': 88,
|
93 |
-
'tokens': 'process'},
|
94 |
-
'3': {'end_ix': 31,
|
95 |
-
'label': 'ANAT-DP',
|
96 |
-
'relations': [],
|
97 |
-
'start_ix': 31,
|
98 |
-
'tokens': 'pleural'},
|
99 |
-
'4': {'end_ix': 32,
|
100 |
-
'label': 'OBS-DP',
|
101 |
-
'relations': [['located_at', '3']],
|
102 |
-
'start_ix': 32,
|
103 |
-
'tokens': 'effusions'},
|
104 |
-
'5': {'end_ix': 35,
|
105 |
-
'label': 'OBS-DP',
|
106 |
-
'relations': [['located_at', '6']],
|
107 |
-
'start_ix': 35,
|
108 |
-
'tokens': 'prominent'},
|
109 |
-
'6': {'end_ix': 38,
|
110 |
-
'label': 'ANAT-DP',
|
111 |
-
'relations': [['modify', '3']],
|
112 |
-
'start_ix': 38,
|
113 |
-
'tokens': 'right'},
|
114 |
-
'7': {'end_ix': 40,
|
115 |
-
'label': 'ANAT-DP',
|
116 |
-
'relations': [],
|
117 |
-
'start_ix': 40,
|
118 |
-
'tokens': 'bibasilar'},
|
119 |
-
'8': {'end_ix': 41,
|
120 |
-
'label': 'OBS-DP',
|
121 |
-
'relations': [['located_at', '6'], ['located_at', '7']],
|
122 |
-
'start_ix': 41,
|
123 |
-
'tokens': 'atelectasis'},
|
124 |
-
'9': {'end_ix': 43,
|
125 |
-
'label': 'OBS-DP',
|
126 |
-
'relations': [['modify', '10']],
|
127 |
-
'start_ix': 43,
|
128 |
-
'tokens': 'Unusual'}},
|
129 |
-
'text': 'FINAL REPORT HISTORY : Pseudomonas with intubation . FINDINGS : In '
|
130 |
-
'comparison with the study of ___ , there is little change in the '
|
131 |
-
'monitoring and support devices . Substantial bilateral pleural '
|
132 |
-
'effusions , more prominent on the right with bibasilar atelectasis . '
|
133 |
-
'Unusual configuration to the collection of opacification at the left '
|
134 |
-
'base raises the possibility of some loculated fluid . There is again '
|
135 |
-
'evidence of increased pulmonary venous pressure . Overlapping '
|
136 |
-
'structures somewhat obscure visualization of the left upper zone and '
|
137 |
-
'simulate the appearance of cavitary process . This area should be '
|
138 |
-
'closely checked on subsequent radiographs .'}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|