Generador de datos sintéticos

Descripción del componente

Desde el proyecto IMPaCT-Data, y con la intención de poder disponer de diferentes conjuntos de datos clínicos sintéticos para poder utilizar con fines demostrativos en otros componentes, se ha creado una pequeña guía para poder generarlos.

Para ello, se va a utilizar la librería SynthaTM y las herramientas de ETL (Extract, Transform and Load) proporcionadas por OHDSI para cargar cohortes de pacientes en una base de datos con un modelo de datos OMOP.

Synthea

Para generar los datos sintéticos utilizaremos SyntheaTM patient generator.

SyntheaTM genera datos sintéticos a partir de la historia clínica de los pacientes. Su objetivo es crear datos realistas y de alta calidad relacionados con los pacientes y los registros de salud asociados sin restricciones de privacidad y seguridad.

Una de las mayores cualidades de SyntheaTM es contar con más de 90 módulos diferentes, cada uno de los cuales contiene modelos para diferentes enfermedades u observaciones médicas. Sin embargo, la mayoría de estos módulos tienen dependencias entre ellos, y no se recomienda restringir la búsqueda a un subconjunto de ellos.

Documentación técnica

Generación de datos sintéticos e importación en la base de datos

Prerequisitos

  • Instalar R/RStudio

  • Instalar Java

  • Una base de datos para subir los resultados. Para estos test se utilizará un PostgreSQL instalado via Docker.

services:
  postgres:
    image: postgres:16
    container_name: postgres
    restart: always
    environment:
      POSTGRES_USER: 'postgres'
      POSTGRES_PASSWORD: 'lollypop'
      POSTGRES_DB: 'demo_omop'
    volumes:
      - ./data:/var/lib/postgresql/data
    ports:
      - "5432:5432"
    networks:
      - beacon-network
      - postgresql-network

  pgadmin:
    image: dpage/pgadmin4
    restart: always
    ports:
      - "8080:80"
    environment:
      PGADMIN_DEFAULT_EMAIL: 'pgadmin'
      PGADMIN_DEFAULT_PASSWORD: 'gnome'
    depends_on:
      - postgres
    networks:
      - postgresql-network

networks:
  beacon-network:
  postgresql-network:

  • Descargar la lista de vocabularios de OMOP-CDM

Generación de datos

Sigue las instrucciones oficiales para compilar Synthea (recomendamos v3.2.0) o descarga el fichero synthea-with-dependencies.jar

Para exportar los datos en formato CSV, deberemos modificar las propiedades de Synthea en el fichero synthea.properties (en caso de que no exista se debera crear) añadiendo la siguiente línea.

exporter.csv.export = true

El comando básico para generar los datos es el siguiente:

java -jar synthea-with-dependencies.jar -c synthea.properties -p 1000

Puedes generar diferentes tipos de datos con diferentes modulos de Synthea. Para ello se debe utilizar la opción -m con el nombre de los módulos elegidos. Comprueba la lista de módulos y algún ejemplo en la web.

Importar datos sintéticos a la base de datos relacional

Para importar los datos a la base de datos, utilizaremos el ETL Synthea repo.

Primero, instalaremos la librería einstall libcurl4-gnutls-devn R desde github. Para ello nos aseguraremos de tener instalados los siguiente paquetes:

sudo apt-get install libcurl4-gnutls-dev libfontconfig1-dev libharfbuzz-dev libfribidi-dev libtiff-dev libxml2-dev libssl-dev libpq-dev

Advertencia

Dado que ETL-Synthea requiere rJava, es posible que deba ejecutar R CMD javareconf para configurar las variables ambientales necesarias

Después abriremos la consola de R y ejecutaremos los siguientes comandos para instalar las librerías necesarias:

 install.packages('devtools')
 devtools::install_github("OHDSI/ETL-Synthea")
 devtools::install_github("rstats-db/RPostgres")
 install.packages("readr")  # To read CSV files
 install.packages("dplyr")  # To manipulate data

Y después ejecutaremos el siguiente código:

# Load library
library(ETLSyntheaBuilder)
library(readr)
library(dplyr)
 
# Download drivers
DatabaseConnector::downloadJdbcDrivers('postgresql', '.')

# Connect to your database

cd <- DatabaseConnector::createConnectionDetails(
  dbms     = "postgresql", 
  server   = "localhost/demo_omop", 
  user     = "postgres", 
  password = "lollypop", 
  port     = 5432, 
  pathToDriver = "./"  
)

# Needed schemas creation
conn <- DatabaseConnector::connect(cd)
DatabaseConnector::executeSql(conn, "CREATE SCHEMA IF NOT EXISTS cdm")
DatabaseConnector::executeSql(conn, "CREATE SCHEMA IF NOT EXISTS vocabularies")
DatabaseConnector::executeSql(conn, "CREATE SCHEMA IF NOT EXISTS raw")


# OMOP-CDM Database configuration

syntheaSchema  <- "raw"
syntheaVersion <- "3.2.0"
syntheaFileLoc <- "./output/csv"

cdmSchema      <- "cdm"
cdmVersion     <- "5.4"
vocabFileLoc   <- "./vocabulary_download_v5_minimal"
vocab_path     <- "./vocabulary_download_v5_minimal/"

# Create OMOP-CDM tables

ETLSyntheaBuilder::CreateCDMTables(connectionDetails = cd, cdmSchema = cdmSchema, cdmVersion = cdmVersion)
          
# Load OMOP vocabularies

ETLSyntheaBuilder::LoadVocabFromCsv(connectionDetails = cd, cdmSchema = cdmSchema, vocabFileLoc = vocabFileLoc)
 
# Load Synthea data
                          
ETLSyntheaBuilder::CreateSyntheaTables(connectionDetails = cd, syntheaSchema = syntheaSchema, syntheaVersion = syntheaVersion)
                                       
ETLSyntheaBuilder::LoadSyntheaTables(connectionDetails = cd, syntheaSchema = syntheaSchema, syntheaFileLoc = syntheaFileLoc)

# Prepare ETL

ETLSyntheaBuilder::CreateMapAndRollupTables(connectionDetails = cd, cdmSchema = cdmSchema, syntheaSchema = syntheaSchema, cdmVersion = cdmVersion, syntheaVersion = syntheaVersion)

                                     
# Execute ETL
                                    
ETLSyntheaBuilder::LoadEventTables(connectionDetails = cd, cdmSchema = cdmSchema, syntheaSchema = syntheaSchema, cdmVersion = cdmVersion, syntheaVersion = syntheaVersion)

# Load Vocabularies into vocabularies schema

tables <- c("CONCEPT", "CONCEPT_RELATIONSHIP", "CONCEPT_ANCESTOR", "RELATIONSHIP", "VOCABULARY", "DOMAIN", "DRUG_STRENGTH", "CONCEPT_CLASS")

for (table in tables) {
  message(paste("Importando", table, "..."))
  
  data <- read_delim(paste0(vocab_path, table, ".csv"), delim = "\t", col_types = cols())
  
  DatabaseConnector::insertTable(conn, 
                                 databaseSchema = "vocabularies",
                                 tableName = table,
                                 data = data,
                                 dropTableIfExists = TRUE,
                                 createTable = TRUE,
                                 tempTable = FALSE,
                                 progressBar = TRUE)
}

# Optimize Vocabularies tables

DatabaseConnector::executeSql(conn, 
  "CREATE INDEX idx_concept_id ON vocabularies.CONCEPT (concept_id);")
DatabaseConnector::executeSql(conn, 
  "CREATE INDEX idx_concept_code ON vocabularies.CONCEPT (concept_code);")
DatabaseConnector::executeSql(conn, 
  "CREATE INDEX idx_relationship_id ON vocabularies.RELATIONSHIP (relationship_id);")
  
DatabaseConnector::executeSql(conn, "VACUUM ANALYZE vocabularies.CONCEPT;")
DatabaseConnector::executeSql(conn, "VACUUM ANALYZE vocabularies.RELATIONSHIP;")

# Close connection
DatabaseConnector::disconnect(conn)

Contacto

Para cualquier duda durante el periodo de uso y validación de los componentes de la Implementación de Referencia de IMPaCT-Data (Marzo, 2025), podéis poneros en contacto con: