R
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Data Structures
-
Section 4: Data Manipulation
-
Section 5: Visualization and Statistics
-
Section 6: Advanced R
-
Section 7: Practical Projects
-
Section 8: Interview Practice
-
Section 9: More Practice Exercises
-
Section 10: Shiny Apps in Depth
-
Section 11: More Data Wrangling
-
Section 12: Tidyverse Deep Dive
-
Section 13: Statistical Modeling Deep Dive
-
Section 14: Machine Learning in R
-
Section 15: R Visualization Deep Dive
-
Section 16: R Package Development Deep Dive
-
Section 17: R for Reproducible Research
-
Section 18: R and Databases
-
Section 19: R Performance Optimization
-
Section 20: Bioinformatics and Specialized R
-
Section 21: More Shiny Practice
-
Section 22: More Practice Exercises
-
Section 23: R Data Structures Deep Dive
-
Section 24: More Interview and Review
-
Section 25: R for Business Analytics
-
Section 26: R Text Mining and NLP
-
Section 27: R Spatial Data Analysis
-
Section 28: R Deep Learning
-
Section 29: Advanced Statistical Techniques
-
Section 30: R Object Systems Deep Dive
-
Section 31: R Environments and Metaprogramming
-
Section 32: R for Finance
-
Section 33: R for Clinical and Health Data
-
Section 34: More Shiny Advanced Practice
-
Section 35: R Data Cleaning Deep Dive
-
Section 36: R Reporting Automation
-
Section 37: More Practical Projects Round 2
-
Section 38: R Ecosystem and Career
-
Section 39: More Visualization Practice
-
Section 40: R for Experimentation
-
Section 41: R for Genomics and Bioinformatics Deep Dive
-
Section 42: R for Marketing Analytics
-
Section 43: R Data Import/Export Deep Dive
-
Section 44: R String Processing Deep Dive
-
Section 45: R for Actuarial and Insurance Analytics
-
Section 46: R Testing and Quality Assurance Deep Dive
-
Section 47: R Version Control and Collaboration
-
Section 48: R Advanced Functional Programming
-
Section 49: R for Supply Chain and Operations
-
Section 50: More Practice Exercises Round 3
-
Section 51: R Dashboards and BI Integration
-
Section 52: R Data Governance and Ethics
-
Section 53: More Modeling Practice
-
Section 54: R Final Capstone Projects
-
Section 55: R for Sports Analytics
-
Section 56: More Interview Practice Round 2
-
Section 57: R Networking and APIs
-
Section 58: R for Environmental Science
-
Section 59: R Notebook and Documentation Practices
-
Section 60: More Data Wrangling Mastery
-
Section 61: R for A/B Testing at Scale
-
Section 62: R Package Ecosystem Deep Dive
135: Working with Electronic Health Record Data Formats
Imagine you're trying to organize a massive community library, but every donor sent their books in a different way. Some sent a modern digital spreadsheet (JSON), some sent hand-written ledger cards with cryptic shorthand (HL7 v2), and others sent a formal, printed catalog that looks like a legal document (CCDA). If you want to know how many medical textbooks you have in total, you can't just "count" them; you first have to translate three entirely different languages into one master list.
Working with Electronic Health Record (EHR) data is exactly like that. In the real world, you'll rarely be handed a clean CSV of patient vitals. Instead, you'll be staring at a mix of nested structures and legacy strings. Here is how that library analogy maps to the R code you'll actually be writing:
- The Digital Spreadsheet (FHIR): This is the modern standard. It's usually JSON. It's structured, but deeply nested. You'll use
jsonliteto parse it, but you'll spend most of your time "flattening" the lists. - The Handwritten Ledger (HL7 v2): This is the "old school" way. It's pipe-delimited (
|). It looks like gibberish at first, but it follows a strict positional logic. You'll usestringrorstrsplitto chop these into pieces. - The Master List (OMOP CDM): This is the goal. The Observational Medical Outcomes Partnership (OMOP) Common Data Model is where you move all those disparate formats so you can actually run a regression or a T-test without losing your mind.
Wrestling with FHIR's Nested JSON
FHIR (Fast Healthcare Interoperability Resources) is the gold standard now, but it's a pain for R users because R prefers rectangles (data frames), and FHIR is a tree. A single "Patient" resource might have a list of names, and each name might have a list of prefixes.
library(jsonlite)
library(dplyr)
# A simplified FHIR Patient resource
fhir_json <- '{
"resourceType": "Patient",
"id": "123",
"name": [{"family": "Smith", "given": ["John", "Jacob"]}],
"gender": "male",
"birthDate": "1980-01-01"
}'
patient_data <- fromJSON(fhir_json)
# Notice that name is a data frame inside a list.
# To get the family name, you can't just call patient_data$name.
family_name <- patient_data$name$family[1]
print(family_name) # "Smith"
I've spent hours debugging code because I forgot that FHIR arrays (like name) can contain multiple entries. Always check the length of your lists before assuming the first element is the only one.
Decoding the Pipe-Delimited Maze of HL7 v2
If you're working with older hospital systems, you'll encounter HL7 v2. It doesn't use keys like "gender" or "birthDate." Instead, it uses positions. For example, in a PID (Patient Identification) segment, the 5th field is usually the patient's name.
library(stringr)
# A raw HL7 PID segment
hl7_message <- "PID|1||12345^^^MRN||Smith^John||19800101|M"
# Split by the pipe character
fields <- str_split(hl7_message, "\\|")[[1]]
# The 5th field (index 5) contains the name, but it's further split by carets (^)
patient_name_raw <- fields[5]
name_parts <- str_split(patient_name_raw, "\\^")[[1]]
family_name_hl7 <- name_parts[1]
given_name_hl7 <- name_parts[2]
cat("Patient:", family_name_hl7, given_name_hl7) # "Patient: Smith John"
It feels primitive, right? It is. But this is how a huge portion of the world's healthcare data still moves. The trick here is to build a "parser" function that takes these strings and converts them into a tidy data frame immediately so you never have to look at the pipes again.
Flattening Data for Analysis
The final hurdle is taking these different sources and making them look the same. In clinical informatics, we call this "ETL" (Extract, Transform, Load). Your goal is to move from the "Resource" mindset (one file per patient) to the "Analytical" mindset (one row per observation).
If you have a list of 1,000 FHIR JSON files, don't try to loop through them with a for loop and rbind. It'll be painfully slow. Instead, use purrr::map_df to parse the JSON and bind it into a tibble in one go. This converts the "tree" of the EHR into the "table" that R loves.
📋 Practical Task
Exercise: Extracting Patient Demographics from a FHIR Bundle
You have been given a small "Bundle" of FHIR data represented as a JSON string. This bundle contains two different patient resources. Your task is to parse this JSON and create a clean R data frame with three columns: patient_id, family_name, and gender.
library(jsonlite)
library(dplyr)
library(purrr)
# The raw data bundle
fhir_bundle <- '{
"resourceType": "Bundle",
"entry": [
{
"resource": {
"resourceType": "Patient",
"id": "p001",
"name": [{"family": "Doe", "given": ["Jane"]}],
"gender": "female"
}
},
{
"resource": {
"resourceType": "Patient",
"id": "p002",
"name": [{"family": "Brown", "given": ["Charlie"]}],
"gender": "male"
}
}
]
}'
# YOUR CODE HERE:
# 1. Use fromJSON to parse the bundle.
# 2. Use map_df or a similar approach to extract the id,
# the first element of the family name, and the gender
# from each entry in the bundle.
# 3. Return a data frame named 'patient_summary'.
There are no comments for now.