Skip to content

Standardized Glossary Design Doc

James Cancilla edited this page Apr 28, 2017 · 3 revisions

Standardized Glossary

The platform currently supports a number of ingest adapters for ingesting data from different sources. These adapters parse the incoming data and output each sample as an Observation type. The Observation type contains an object attribute called Reading that holds the value, timestamp, unit of measure and type of the signal being represented by the Observation. The string attribute Reading.readingType, is intended to hold the type of the Observation (i.e. ECG Lead I, ABP Systolic, Temperature, etc).

Problem Statement

The current method for populating the Reading.readingType attribute is to use the naming convention provided by the original source that the data is being ingested from. This makes it difficult for downstream applications to determine the specific vital sign or waveform that is being represented by the Observation type. This problem is further exacerbated when multiple sources from different ingest adapters are being used, as the downstream application now needs to know about each of those sources.

Goals

The goals that this design achieves are as follows:

  • Provide a standardized set of codes that should be used to populate the Reading.readingType attribute in the Observation tuple.
  • Define an interface for a type resolver library so that developers building analytic applications can determine the type of data represented by an Observation tuple without needing to reference the standardized codes directly.
  • Propose a change to the Reading.readingType attribute in order to support this standardized coding system

High-level Design

The following provides a high-level overview of this design:

  • A standardized set of codes will be produced and documented
  • The Reading.readingType attribute, which is currently a string attribute will be changed to an object that allows for specifying both the code as well as the coding system
  • Each ingest adapter will be responsible for determining how to map the vendor-specific codes to the standardized set of codes
    • In the cases where there is no appropriate mapping, the vendor code should be used to populate the Reading.readingType attribute. With the proposed changes to the Reading.readingType attribute, there will be space provided to indicate that the vendor code has been used instead.
  • Downstream analytic applications that ingest the Observation tuple should use the Observation type resolver library to determine the type of reading

Architecture

Detailed Design

Standardized codes

The following table is being proposed as an initial set of standardized codes to use with the platform. These codes are based on the LOINC coding system (http://loinc.org). A best effort is made to reuse the LOINC codes whenever possible. In cases where no suitable LOINC numeric code can be found to represent a vital or waveform, a custom code will be used instead. According to the LOINC manual, user-defined codes should contain a leading alphabetic "X" character. Therefore, in keeping with the spirit of the LOINC documentation, all custom codes will contain a leading "X" character.

The following is the proposed set of standardized codes, labels, units and descriptions.

Code Label
8867-4 Heart Rate
9279-1 Respiratory Rate
76270-8 Impedance Respiratory wave (Resp)
8310-5 Temperature
2710-2 SpO2
8480-6 Blood Pressure - Systolic
8462-4 Blood Pressure - Diastolic
X200-6 Pleth
X201-4 Pulse
X100-8 ECG Lead I
X101-6 ECG Lead II
X102-4 ECG Lead III
X103-2 ECG Lead V1
X104-0 ECG Lead V2
X105-7 ECG Lead V3
X106-5 ECG Lead V4
X107-3 ECG Lead V5
X108-1 ECG Lead V6
X109-9 ECG Lead V7
X110-7 ECG Lead V8
X111-5 ECG Lead V9
X112-3 ECG Lead AVF
X113-1 ECG Lead AVL
X114-9 ECG Lead AVR

Observation type resolver library

This design is proposing that a type resolver class be implemented in order to abstract out the specific codes used to represent signals so that developers do not need to reference the standardized codes directly.

This design is proposing the following API for determining the readingType of an Observation object:

ObservationTypeResolver.java:

public class ObservationTypeResolver {

	/**
	 * Returns `true` if the given Observation object 
	 * represents an ECG Lead I waveform.
	 * @return `true` if the Observation object represents 
	 * an ECG Lead I waveform, `false` otherwise
	 */
	public static boolean isECGLeadI(Observation observation) { /* TODO */ }

	/**
	 * Returns `true` if the given Observation object 
	 * represents an ECG Lead II waveform.
	 * @return `true` if the Observation object represents 
	 * an ECG Lead II waveform, `false` otherwise
	 */
	public static boolean isECGLeadII(Observation observation) { /* TODO */ }
	
	/**
	 * Returns `true` if the given Observation object 
	 * represents an ECG Lead III waveform.
	 * @return `true` if the Observation object represents 
	 * an ECG Lead III waveform, `false` otherwise
	 */
	public static boolean isECGLeadIII(Observation observation) { /* TODO */ }
	
	/**
	 * Returns `true` if the given Observation object 
	 * represents an ECG Lead V1 waveform.
	 * @return `true` if the Observation object represents 
	 * an ECG Lead V1 waveform, `false` otherwise
	 */
	public static boolean isECGLeadV1(Observation observation) { /* TODO */ }
	
	/**
	 * Returns `true` if the given Observation object 
	 * represents an ECG Lead V2 waveform.
	 * @return `true` if the Observation object represents 
	 * an ECG Lead V2 waveform, `false` otherwise
	 */
	public static boolean isECGLeadV2(Observation observation) { /* TODO */ }
	
	/**
	 * Returns `true` if the given Observation object 
	 * represents an ECG Lead V3 waveform.
	 * @return `true` if the Observation object represents 
	 * an ECG Lead V3 waveform, `false` otherwise
	 */
	public static boolean isECGLeadV3(Observation observation) { /* TODO */ }
	
	/**
	 * Returns `true` if the given Observation object 
	 * represents an ECG Lead V4 waveform.
	 * @return `true` if the Observation object represents 
	 * an ECG Lead V4 waveform, `false` otherwise
	 */
	public static boolean isECGLeadV4(Observation observation) { /* TODO */ }
	
	/**
	 * Returns `true` if the given Observation object 
	 * represents an ECG Lead V5 waveform.
	 * @return `true` if the Observation object represents 
	 * an ECG Lead V5 waveform, `false` otherwise
	 */
	public static boolean isECGLeadV5(Observation observation) { /* TODO */ }
	
	/**
	 * Returns `true` if the given Observation object 
	 * represents an ECG Lead V6 waveform.
	 * @return `true` if the Observation object represents 
	 * an ECG Lead V6 waveform, `false` otherwise
	 */
	public static boolean isECGLeadV6(Observation observation) { /* TODO */ }
	
	/**
	 * Returns `true` if the given Observation object 
	 * represents an ECG Lead V7 waveform.
	 * @return `true` if the Observation object represents 
	 * an ECG Lead V7 waveform, `false` otherwise
	 */
	public static boolean isECGLeadV7(Observation observation) { /* TODO */ }
	
	/**
	 * Returns `true` if the given Observation object 
	 * represents an ECG Lead V8 waveform.
	 * @return `true` if the Observation object represents 
	 * an ECG Lead V8 waveform, `false` otherwise
	 */
	public static boolean isECGLeadV8(Observation observation) { /* TODO */ }
	
	/**
	 * Returns `true` if the given Observation object 
	 * represents an ECG Lead V9 waveform.
	 * @return `true` if the Observation object represents 
	 * an ECG Lead V9 waveform, `false` otherwise
	 */
	public static boolean isECGLeadV9(Observation observation) { /* TODO */ }

	/**
	 * Returns `true` if the given Observation object 
	 * represents an ECG Lead AVF waveform.
	 * @return `true` if the Observation object represents 
	 * an ECG Lead AVF waveform, `false` otherwise
	 */
	public static boolean isECGLeadAVF(Observation observation) { /* TODO */ }
	
	/**
	 * Returns `true` if the given Observation object 
	 * represents an ECG Lead AVL waveform.
	 * @return `true` if the Observation object represents 
	 * an ECG Lead AVL waveform, `false` otherwise
	 */
	public static boolean isECGLeadAVL(Observation observation) { /* TODO */ }
	
	/**
	 * Returns `true` if the given Observation object 
	 * represents an ECG Lead AVR waveform.
	 * @return `true` if the Observation object represents 
	 * an ECG Lead AVR waveform, `false` otherwise
	 */
	public static boolean isECGLeadAVR(Observation observation) { /* TODO */ }
	
	/**
	 * Returns `true` if the given Observation object 
	 * represents a Heart Rate vital.
	 * @return `true` if the Observation object represents 
	 * a Heart Rate vital, `false` otherwise
	 */
	public static boolean isHeartRate(Observation observation) { /* TODO */ }
	
	/**
	 * Returns `true` if the given Observation object 
	 * represents a Respiratory Rate vital.
	 * @return `true` if the Observation object represents 
	 * an Respiratory Rate vital, `false` otherwise
	 */
	public static boolean isRespiratoryRate(Observation observation) { /* TODO */ }
	
	/**
	 * Returns `true` if the given Observation object 
	 * represents a Temperature vital.
	 * @return `true` if the Observation object represents 
	 * a Temperature vital, `false` otherwise
	 */
	public static boolean isTemperature(Observation observation) { /* TODO */ }
	
	/**
	 * Returns `true` if the given Observation object 
	 * represents an SpO2 vital.
	 * @return `true` if the Observation object represents 
	 * an SpO2 vital, `false` otherwise
	 */
	public static boolean isSpO2(Observation observation) { /* TODO */ }
	
	/**
	 * Returns `true` if the given Observation object 
	 * represents a Systolic Blood Pressure vital.
	 * @return `true` if the Observation object represents 
	 * a Systolic Blood Pressure vital, `false` otherwise
	 */
	public static boolean isBPSystolic(Observation observation) { /* TODO */ }
	
	/**
	 * Returns `true` if the given Observation object 
	 * represents a Diastolic Blood Pressure vital.
	 * @return `true` if the Observation object represents 
	 * a Diastolic Blood Pressure vital, `false` otherwise
	 */
	public static boolean isBPDiastolic(Observation observation) { /* TODO */ }
	
	/**
	 * Returns `true` if the given Observation object 
	 * represents a Pleth waveform.
	 * @return `true` if the Observation object represents 
	 * a Pleth waveform, `false` otherwise
	 */
	public static boolean isPleth(Observation observation) { /* TODO */ }
	
	/**
	 * Returns `true` if the given Observation object 
	 * represents a Pulse vital.
	 * @return `true` if the Observation object represents 
	 * a Pulse vital, `false` otherwise
	 */
	public static boolean isPulse(Observation observation) { /* TODO */ }

	/**
	 * Returns the human-readable label of the reading type 
	 * for the given Observation object 
	 * @return the human readable label of the reading type
	 */
	public static String getLabel(Observation observation) { /* TODO */ }
	
}

Proposed changes to Reading.readingType

The following is the current schema for the Reading object attribute in the Observation type:

Reading : {
  readingType : "string",
  ts : "string",
  value : Numeric,
  uom : "string"
}

As seen in the above schema, the Reading.readingType attribute is a string attribute. This design is proposing that the Reading.readingType attribute be changed to an object with the following schema:

Reading: {
  readingType : {
    system : "string",
    code : "string"
  },
  ts : "string", 
  value : Numeric,
  uom : "string"  
}

The following table provides a description for the attributes found in the proposed Reading.readingType object:

_Samantha: For standardized code, I propose the value for the Streams coding system should be "streamsx.health.readingType.v1". We need a namespace to identify our codes. I am wondering if we should consider having a version of the coding system. What if we need to evolve our codes, how do we tell what version of coding system we are using?_

_James: I agree that we should include a namespace and version. I recommend we try to keep the name of the system short as this name will appear in every single Observation tuple. I would also like to have a clear separator between the namespace and the version. Here is the pattern I am proposing: "streamsx.health/X.x", where X.x is the version. For example:

streamsx.health/1.0, streamsx.health/1.1, etc

With this scheme, the string can easily be split into tokens allowing the user to check either system name or version number (or both). If it ever became necessary to add additional tokens, this schema allows us to do that without breaking compatibility._

Name Type Description
Reading
  readingType rstring
    system rstring Specifies the coding system being used. If the standardized coding system proposed by this document is used, then this attribute will be populated with a value of streamsx.health/1.0. In the case where there is no mapping from vendor code to a standardized code, then this attribute should be populated with the name of the originating vendor (i.e. "vines", "physionet", etc).
    code rstring Populated with the code that indicates the type of signal being represented by this Observation tuple. This attribute should be populated with one of the standardized codes or with the vendor code in the case where no mapping could be performed.
...

Naming convention for Reading.readingType.system

When the value of Reading.readyingType.code is set using one of the platform-specific codes, the system attribute should be set to a value of: streamsx.health/X.x, where X.x is the version of the system. In the initial release that implements this proposal, the system attribute will have a value of: streamsx.health/1.0.

In the case where there is no mapping from vendor code to a standardized code, then the system attribute should be populated with the name of the originating vendor (i.e. "vines", "physionet", etc). A version does not need to be specified in this case.