OHDSI Home | Forums | Wiki | Github

Handling procedures that use parameterized devices

Hi,
Has there been any thought or experience about storing details of Procedure and Device used for its performance ?

I have access to RedCap database containing clinical trial data to investigate the essential tremor treatment with focused ultrasound. I need to convert dataset into OMOP CDM. I have already found that I need to provide my own concepts for many symptoms and observations (Fahn-tolosa-Marin score, some sensory symptoms for given locations etc.).

However, I do not have any clue how to handle storing information about procedure details and parameters of device.

Suppose that I can use the following concept for FUS procedure:
37204162 MRI guided high intensity focused ultrasound ablation of thalamus

How can I store information about the following parameters of procedure:

  • number of sonications (as integer number), eg. 3,
  • power per sonication (array of real numbers), eg. [185.2,185.2,185.3]
  • number of targets (as integer number), eg.3
  • temperature pe sonication (as array of real numbers), eg. [42.6, 44.7, 42.4]
    etc.

I face two problems with these data.

  1. Storing parameters of procedure.
    Shall I use some variant of solution with multiple Procedure records having different modifiers and connected through Fact Relationship Table (as described here: Multiple Modifier with Single Procedure - #5 by MPhilofsky).
    Or maybe there is way a to use DEVICE_EXPOSURE to that? Does it make sense to create my own table PROCEDURE_DETAILS, by analogy to VISIT_OCCURRENCE and VISIT_DETAIL.

  2. Handling answers containing arrays.
    No clue about that - maybe they should be different records of Procedure?

I will appreciate any suggestion, thank you.

@Andrzej_Marciniak:

Fully understand your problem, and it is a legitimate one. We need to make this happen.

With respect to the solution space:

  1. Bad idea. Multiple procedure records mean multiple procedures. None of the standard analytic tools will follow and interpret the FACT_RELATIONSHIP records (of which I am no fan of, as many people already found out.) Essentially, it will be an entirely local solution rather than a standard data model.

  2. The OMOP CDM is a simple RDBS model. No arrays allowed. Again, no standard analytic package will even know you are trying to do something.

The problem is that the OMOP CDM is a closed world model, which means, everything that happened has a record with a pre-defined meaning, and if there is no record nothing happened. Your stuff, is open world. Anything can happen, and any attribute is possible.

The solution is an OMOP add-on, as we decided to call it. We don’t have it yet, but want to develop it as part of the CDM Working Group this year. You could be one of the Guinea pigs. @clairblacketer will invite you to our CDM meetings.

@Christian_Reich
Thank you very much for an immediate and concrete response (as always!) - It’s good to know where I stand.

I am glad that you intend to develop schema in this direction. I apply for being a Guinea pig here, if this can be a help for you :wink:

Ad. 1 As for now, I think I will design a new, non-standard table with procedure parameters, just to keep the data, even if the data will not be accessible in OHDSI analytic tools - later I can just migrate the content to the new schema that will be handling procedure parameters.

Ad.2. Since my schema uses Postgres and the proposed table won’t be compatible with OHDSI tools anyway, I can freely use arrays in it - Postgress handles arrays of every type (no need to worry about portability of CDM).

Thanks again and please let me know if you have any comments on the above findings.

@Andrzej_Marciniak:

  1. Yes please, but would be nice if we could also develop some standard conventions and rules for the non-standard add-on tables or fields. So that adjusting would be easy for folks who want to implement the solution. So, let’s figure that out in the WG.

  2. Well, same thing. Yes, you can, but we still want to have a wide appeal and reduce the adoption learning curve. So, I would abstain from funny non-standard arrays, if you can. (Even though they are nice in Postgress). I know forcing facts into a RDBS structure can be pain, but once you got it it makes life easier.

Thanks again,
I will see what I can do to make my solutions re-usable and in accordance with OHDSI Design Principles

@Christian_Reich I implemented the non-standard table for procedure details and I am publishing it here so anyone can comment on it, use it or just tell me that it does not make sense at all.

The proposed solution combines elements of VISIT_OCCURRENCE <-> VISIT_DETAIL relationship with fields from MEASUREMENT and OBSERVATION tables.

Reason for Extension

The procedure occurrence record can contain only one modifier that can be implemented as concept only. Therefore it is not convenient to store more parameters of PROCEDURE_OCCURRENCE, as for example number of sonications, sonications power in case of MRI guided high intensity focused ultrasound ablation thalamotomy. Theoretically, they could be stored in the OBSERVATION table, but since their domain would be Procedure, this would require creating corresponding records in the PROCEDURE_OCCURENCE table. Moreover, still remains the problem with representing structures of the procedure parameters, eg. arrays of “sonication power”, aggregates as eg. “maximum power in series of sonications” or hierarchical.

Table Description

The PROCEDURE_DETAIL table is an optional table used to represent additional details and parameters of existing PROCEDURE_OCCURRENCE records. For every record in the PROCEDURE_OCCURRENCE table there may be 0 or more records in the PROCEDURE_DETAIL table with a 1:n relationship where n may be 0. The PROCEDURE_DETAIL table is composed of some parts of PROCEDURE_OCCURRENCE, MEASUREMENT and OBSERVATION tables and belongs to the procedure domain. Using parent_procedure_detail_id and preceding_procedure_detail_id allows us to compose flat or hierarchical structures of related parameters.

User Guide

The configuration defining the Procedure Detail is described by Concepts in the Procedure Domain, which form a hierarchical structure. The Procedure Detail record will have an associated to the Procedure Occurrence record in two ways:

  1. The Procedure Detail record will have the PROCEDURE_OCCURRENCE_ID it is associated with
  2. The PROCEDURE_DETAIL_CONCEPT_ID will be a descendant of the PROCEDURE_CONCEPT_ID for the Procedure.

Sequence of parameters for complex procedures can be represented with preceding_procedure_detail_id. For example, if there was performed FUS ablation thalatomy with 5 sonications, power of each sonication can be represented as Power of Sonication detail, where each record (except for the first one) will have id of the previous sonication power.
To represent hierarchical structure of records, please use parent_procedure_detail_id.

ETL Conventions

It is not mandatory that the PROCEDURE_DETAIL table be filled in, but if you find that the parameters of the performed procedure can have an impact on the cause-effect relationships in your statistical analysis, it is a good idea to use PROCEDURE_DETAIL. For example, the number of sonications in MRI guided high intensity focused ultrasound ablation thalamotomy on essential tremor condition degree expressed with FTM score.

CDM Field User Guide ETL Conventions Datatype Required PK Foreign Key FK Table FK Domain
procedure_detail_id The unique key given to a procedure detail record for a person. Each instance of a procedure detail in the source data should be assigned this unique key. integer Yes Yes No
person_id The PERSON_ID of the PERSON for whom the procedure is recorded. This may be a system generated code. integer Yes No Yes PERSON
procedure_detail_concept_id This field is recommended for primary use in analyses, and must be used for network studies. This is the standard concept mapped from the source value which represents a procedure detail. The CONCEPT_ID that the PROCEDURE_SOURCE_VALUE maps to. Only records whose source values map to standard concepts with a domain of “Procedure” should go in this table. Accepted Concepts. integer Yes No Yes CONCEPT Procedure
procedure_detail_type_concept_id This field can be used to determine the provenance of the Procedure record, as in whether the procedure was from an EHR system, insurance claim, registry, or other sources. Choose the concept id that best represents the provenance of the record, for example whether it came from an EHR record or billing claim. integer Yes No Yes CONCEPT Type Concept
provider_id The provider associated with the procedure record, e.g. the provider who performed the Procedure. The ETL may need to make a choice as to which PROVIDER_ID to put here. integer No No Yes PROVIDER
procedure_detail_source_value This field houses the verbatim value from the source data representing the procedure detail. For example, this could be an CPT4 or OPCS4 code. Use this value to look up the source concept id and then map the source concept id to a standard concept id. varchar(50) No No No
procedure_detail_source_concept_id If the PROCEDURE_DETAIL_SOURCE_VALUE is coded in the source data using an OMOP supported vocabulary put the concept id representing the source value here. Integer No No Yes CONCEPT
preceding_procedure_detail_id Use this field to find the detail that occurred for the procedure prior to the given detail record. The PRECEDING_PROCEDURE_DETAIL_ID can be used to link a Details, so they can be treated as arrays (implementation of array with use of uni-directional list of references). Note this is not symmetrical, and there is no such thing as a “following_visit_id”. integer No No Yes PROCEDURE_DETAIL
parent_procedure_detail_id Use this field to find the detail that subsumes the given detail record. This is used for nesting relationship. If there are multiple nested levels to how details are represented in the source, this field can be used to record this relationship. integer No No Yes PROCEDURE_DETAIL
procedure_occurrence_id Use this field to link the PROCEDURE_DETAIL record to its PROCEDURE_OCCURRENCE. Put the PROCEDURE_OCCURRENCE_ID that subsumes the PROCEDURE_DETAIL record here. integer Yes No Yes PROCEDURE_OCCURRENCE
operator_concept_id The meaning of Concept 4172703 for ‘=’ is identical to omission of a OPERATOR_CONCEPT_ID value. See Measurement table for more info. Operators are =, > and these concepts belong to the ‘Meas Value Operator’ domain. Accepted Concepts. integer No No Yes CONCEPT
value_as_number This is the numerical value of the Detail of the Procedure, if applied. float No No No
value_as_concept_id If the detail values can be categorized, use those values mapped to standard concepts in the ‘Meas Value’ domain. The continuous value should go in the VALUE_AS_NUMBER field and the categorical value should be mapped to a standard concept in the ‘Meas Value’ domain and put in the VALUE_AS_CONCEPT_ID field. This is also the destination for the ‘Maps to value’ relationship. integer No No Yes CONCEPT
unit_concept_id To be used, if the recorded value of procedure detail (parameter) requires a unit, There is currently no recommended unit for individual measurements, i.e. it is not mandatory to represent Hemoglobin a1C measurements as a percentage. UNIT_SOURCE_VALUES should be mapped to a Standard Concept in the Unit domain that best represents the unit as given in the source data. There is no standardization requirement for units associated with PROCEDURE_DETAIL_CONCEPT_IDs, however, it is the responsibility of the ETL to choose the most plausible unit. integer No No Yes CONCEPT Unit
range_low Ranges have the same unit as the VALUE_AS_NUMBER. These ranges are provided by the source and should remain NULL if not given. If reference ranges for upper and lower limit of normal as provided (typically by a laboratory) these are stored in the RANGE_HIGH and RANGE_LOW fields. This should be set to NULL if not provided. float No No No
range_high Ranges have the same unit as the VALUE_AS_NUMBER. These ranges are provided by the source and should remain NULL if not given. If reference ranges for upper and lower limit of normal as provided (typically by a laboratory) these are stored in the RANGE_HIGH and RANGE_LOW fields. This should be set to NULL if not provided. float No No No
unit_source_value This field houses the verbatim value from the source data representing the unit of the PROCEDURE_DETAIL value that occurred. This code is mapped to a Standard Condition Concept in the Standardized Vocabularies and the original code is stored here for reference. varchar(50) No No No
unit_source_concept_id This is the concept representing the UNIT_SOURCE_VALUE and may not necessarily be standard. This field is discouraged from use in analysis. For more info see Measurement table. If the UNIT_SOURCE_VALUE is coded in the source data using an OMOP supported vocabulary put the concept id representing the source value here. integer No No Yes CONCEPT
value_source_value This field houses the verbatim result value of the Procedure detail value from the source data . If both a continuous and categorical result are given in the source data such that both VALUE_AS_NUMBER and VALUE_AS_CONCEPT_ID are both included, store the verbatim value that was mapped to VALUE_AS_CONCEPT_ID here. varchar(50) No No No

The full script here:
– Table: public.procedure_detail

DROP TABLE IF EXISTS public.procedure_detail;


-- Table structure. ETL: Populate field procedure_detail_concept_id with concept in domain Procedure.
CREATE TABLE public.PROCEDURE_DETAIL (
			procedure_detail_id integer NOT NULL,
			person_id integer NOT NULL,
			procedure_detail_concept_id integer NOT NULL,
			procedure_detail_type_concept_id integer NOT NULL,
			provider_id integer NULL,
			procedure_detail_source_value varchar(50) NULL,
			procedure_detail_source_concept_id Integer NULL,
			preceding_procedure_detail_id integer NULL,
			parent_procedure_detail_id integer NULL,
			procedure_occurrence_id integer NOT NULL,
			operator_concept_id integer NULL,
			value_as_number NUMERIC NULL,
			value_as_concept_id integer NULL,
			unit_concept_id integer NULL,
			range_low NUMERIC NULL,
			range_high NUMERIC NULL,
			unit_source_value varchar(50) NULL,
			unit_source_concept_id integer NULL,
			value_source_value varchar(50) NULL			
			);
			
			
-- PRIMARY KEY
ALTER TABLE public.PROCEDURE_DETAIL ADD CONSTRAINT xpk_PROCEDURE_DETAIL PRIMARY KEY (procedure_detail_id);

-- INDICES (by person, concept and occurrence)
CREATE INDEX idx_procedure_det_person_id_1  ON public.procedure_detail  (person_id ASC);
CLUSTER public.procedure_detail  USING idx_procedure_det_person_id_1 ;

CREATE INDEX idx_procedure_det_concept_id_1 ON public.procedure_detail (procedure_detail_concept_id ASC);
CREATE INDEX idx_procedure_det_occ_id ON public.procedure_detail (procedure_occurrence_id ASC);

			
-- CONSTRAINTS			
ALTER TABLE public.PROCEDURE_DETAIL ADD CONSTRAINT fpk_PROCEDURE_DETAIL_person_id FOREIGN KEY (person_id) REFERENCES public.PERSON (PERSON_ID);
ALTER TABLE public.PROCEDURE_DETAIL ADD CONSTRAINT fpk_PROCEDURE_DETAIL_procedure_detail_concept_id FOREIGN KEY (procedure_detail_concept_id) REFERENCES public.CONCEPT (CONCEPT_ID);
ALTER TABLE public.PROCEDURE_DETAIL ADD CONSTRAINT fpk_PROCEDURE_DETAIL_procedure_detail_type_concept_id FOREIGN KEY (procedure_detail_type_concept_id) REFERENCES public.CONCEPT (CONCEPT_ID);
ALTER TABLE public.PROCEDURE_DETAIL ADD CONSTRAINT fpk_PROCEDURE_DETAIL_provider_id FOREIGN KEY (provider_id) REFERENCES public.PROVIDER (PROVIDER_ID);
ALTER TABLE public.PROCEDURE_DETAIL ADD CONSTRAINT fpk_PROCEDURE_DETAIL_procedure_detail_source_concept_id FOREIGN KEY (procedure_detail_source_concept_id) REFERENCES public.CONCEPT (CONCEPT_ID);

ALTER TABLE public.PROCEDURE_DETAIL ADD CONSTRAINT fpk_PROCEDURE_DETAIL_preceding_procedure_detail_id FOREIGN KEY (preceding_procedure_detail_id) REFERENCES public.PROCEDURE_DETAIL (PROCEDURE_DETAIL_ID);
ALTER TABLE public.PROCEDURE_DETAIL ADD CONSTRAINT fpk_PROCEDURE_DETAIL_parent_procedure_detail_id FOREIGN KEY (parent_procedure_detail_id) REFERENCES public.PROCEDURE_DETAIL (PROCEDURE_DETAIL_ID);
ALTER TABLE public.PROCEDURE_DETAIL ADD CONSTRAINT fpk_PROCEDURE_DETAIL_procedure_occurrence_id FOREIGN KEY (procedure_occurrence_id) REFERENCES public.PROCEDURE_OCCURRENCE (PROCEDURE_OCCURRENCE_ID);

ALTER TABLE public.PROCEDURE_DETAIL ADD CONSTRAINT fpk_PROCEDURE_DETAIL_operator_concept_id FOREIGN KEY (operator_concept_id) REFERENCES public.CONCEPT (CONCEPT_ID);
ALTER TABLE public.PROCEDURE_DETAIL ADD CONSTRAINT fpk_PROCEDURE_DETAIL_value_as_concept_id FOREIGN KEY (value_as_concept_id) REFERENCES public.CONCEPT (CONCEPT_ID);
ALTER TABLE public.PROCEDURE_DETAIL ADD CONSTRAINT fpk_PROCEDURE_DETAIL_unit_concept_id FOREIGN KEY (unit_concept_id) REFERENCES public.CONCEPT (CONCEPT_ID);
ALTER TABLE public.PROCEDURE_DETAIL ADD CONSTRAINT fpk_PROCEDURE_DETAIL_unit_source_concept_id FOREIGN KEY (unit_source_concept_id) REFERENCES public.CONCEPT (CONCEPT_ID);
t