GraFEHR: Graph Features for Electronic Health Records
Authors: Mirela Cazzolato1,2, Marco Antonio Gutierrez1, Caetano Traina Jr.2, Pedro Fidalgo3,4, Agma J. M. Traina2, Christos Faloutsos5
Affiliations: 1The Heart Institute (InCor) - University of São Paulo (HC-FMUSP), 2Institute of Mathematics and Computer Science - University of São Paulo (ICMC-USP), 3University Institute of Lisbon (ISCTE-IUL), 4Mobileum and ISCTE-IUL, 5Carnegie Mellon University (CMU)
This work is under review by peers.
src/
├── app.py # Streamlit entry point
├── dashboard.py # UI tabs: Feature Extraction, Input Data, EDA
├── util.py # plotting and graph-construction helpers
├── config.py # shared session state
├── tgraph/
│ ├── static_graph.py # degree, weighted degree, coreness
│ ├── temporal_graph.py # IAT and lifetime features
│ └── join_feature_files.py # merges static + temporal (or multi-dataset) feature files
└── data/
├── example_EHR.csv # toy raw input (see "Input data format" below)
└── grafehr_features.csv # toy pre-extracted features
We recommend using a virtual environment to run GraFEHR.
To setup and use a virtual environment, type in the Terminal:
python -m venv grafehr_venv
source grafehr_venv/bin/activate
Install the requirements:
$ pip install -r requirements.txt
or
$ make prep
Run the app:
$ cd src/
$ make demo
A demo video is provided (DEMO_Video.mov).
GraFEHR takes a single flat, tabular CSV as input: one row per event/tuple, with (at least)
four columns that you map to the following roles when running the tool (Feature Extraction tab,
or the source/destination/measure/timestamp arguments of TemporalGraph/StaticGraph):
| Role | Meaning | Example (data/example_EHR.csv) |
|---|---|---|
| source | the entity/attribute value to model as the graph's source node | DE_EXAME (exam name) |
| destination | the paired attribute value (self-attribute analysis reuses the same column, shifted in time; pairwise analysis uses a second column) | DE_ANALITO (analyte) |
| measure | numeric edge weight (defaults to a constant/frequency count if you don't have one) | MEASURE |
| timestamp | event time, used for IAT/lifetime features | DT_COLETA |
A few rules the code applies automatically once you've mapped these four columns (see
tgraph/temporal_graph.py, read_input_data): rows are coerced to a numeric measure and
dropped if it isn't > 0; rows where source == destination (a value transitioning to itself)
are dropped rather than kept as a self-loop; timestamp is parsed as a datetime. An optional
DATASET column lets you tag rows by source institution, so multiple hospitals/sources can be
compared side by side (as in the treatment-vs-exam comparison across hospitals in the paper).
What GraFEHR does not do automatically: if your raw data lives across multiple tables (as
in an OMOP-CDM export, or per-institution extracts with their own local codes), you need to
join/flatten it into the single-CSV format above yourself before loading it into the tool —
for example, joining condition_occurrence/drug_exposure to concept for human-readable
names, or mapping different institutions' local codes for the same concept onto a shared
vocabulary (e.g., via a SQL join against a code-mapping table, or CASE WHEN statements). This
reconciliation step is external to GraFEHR by design (see Section 4 of the paper); the tool
assumes it has already happened by the time you load your CSV.
data/example_EHR.csv is a real (anonymized) example of this flat format, derived from the
FAPESP COVID-19 Data Sharing/BR repository [1]; use it as a template for the column layout your
own data needs.
The app has three tabs, meant to be used in order:
- Feature Extraction — upload your flat CSV (or check "Use sample file" for the built-in
data/example_EHR.csvdemo), map its columns to source/destination/measure/timestamp, and click "Create graph and extract features". This runstgraph/static_graph.pyandtgraph/temporal_graph.pyand writesdata/grafehr_features.csv. - Input Data — reload the features file just generated (or check "Use sample file with
features" for the built-in
data/grafehr_features.csv), plus the same raw CSV, then pick the same source/destination/measure columns again to build the graph used for deep dives. - EDA — 1-D histograms, 2-D hexbins, the interactive scatter-plot matrix, and EgoNet deep dives (select points in the scatter matrix to inspect the underlying records).
To combine feature files from multiple runs (e.g., static + temporal features computed
separately, or several datasets/institutions you want to analyze together), use
tgraph/join_feature_files.py path_static_file path_temporal_file.
1. The Covid-19 Data Sharing Repository provides Electronic Health Record (EHR) data from hospitals of São Paulo state, Brazil. The EHRs were collected between 2020 and 2021 [1].
2. The InCor-OMOP dataset was proposed in [2], and contains two decades of EHR data from the Heart Institute (InCor) of Sao Paulo, Brazil, modeled into the common data model OMOP-CDM. The dataset is for clinical research and was anonymized by the authors. For more details, please refer to the paper.
This dataset is proprietary, since it contains sensitive data from patients. To have access to it, please contact the authors of [2].
Following, we provide the features extracted and used in our work, for both datasets.
Download:
-
1- InCor - Conditions -- Attributes: "Conditions" grouped by "Patients": link
-
2- InCor - Patients and Drug Exposure -- Attributes: Patient, Drug Exposure: link
-
3- FAPESP-Covid -- Attributes: Exams grouped by Patients: link
-
4- FAPESP-Treatments and Exams -- Attributes: Treatment, Exam: link
[1] FAPESP. FAPESP COVID-19 Data Sharing/BR, Available from https://repositoriodatasharingfapesp.uspdigital.usp.br/. Accessed on March 10th 2023. [2] de Lima DM, Jr. JFR, Traina AJM, et al (2019). Transforming two decades of ePR data to OMOP CDM for clinical research. In: MedInfo, Studies in Health Technology and Informatics, vol 264. IOS Press, pp 233–237