Convert your FHIR JSON -> XML and back here. The CDA Book is sometimes listed for Kindle here and it is also SHIPPING from Amazon! See here for Errata.

Wednesday, July 18, 2012

Computation and HQMF

One of the things I've been struggling with in the back of my head for the past couple of weeks with HQMF is how to deal with continuous variable (computed) measures.  I nearly got it right last week when we put together the model where:
  1. The data criteria section describes data of interest.
  2. The population criteria selects items to compute with.
  3. The measure observations section defines the computations.
I said nearly.  It works for any single act where the computation is derived from attributes of that act.  But it occurred to me this evening (while working on something not quite related), that I was missing something.  Where it doesn't work is for computations dealing with two or more different acts.  I'm sure if I'd done more applicative programming at some point in my career, how to do this would have been obvious, but it wasn't last week.  I never got Prolog either.  It's weird. Somehow I can do awesome stuff in weird languages like XSLT, but some of these other twisty ways of thinking escape me (at least initially).

In any case, I realized tonight that the solution to the challenge for computations involving two or more acts is to provide a mechanism that defines a set of tuples.  Each item in tuple is associated with a "measure population criteria" which describes whether or not the item should be considered on its own merits alone, and zero or more constraints between two or more items of the tuple that must be true for the items to be considered.

This is essentially a "INNER JOIN" between the sets of results matched by the measure population criteria for each item.  Imagine the case where, in an ED encounter, you want to measure the time between the creation of an order to admit a patient, and the discharge of that patient from the ED encounter.  You have two acts:  The ED encounter itself (call it EDEncounter), and the order to admit (call it OrderToAdmit).  In the current HQMF model, these could both be represented as measure population criteria, and you could have a measure observation definition based on those criteria (e.g., EDEncounter.effectiveTime.high - OrderToAdmit.effectiveTime), and it would nearly work.  But let's assume that you have a patient who has had two ED encounters in the measure period.  For those patients, the definition of the computation would have to decide which EDEncounter to associate with which OrderToAdmit.  The only way to resolve the issue is to tell the system how the two are related.

So, with two or more variables, there needs to be at least one more criteria which specifies the relationships between the acts.  It could go a number of different places, but because this criteria is critical to the computation, I'd put it in the measure observation definition as a precondition.  The precondition would reference a relevant act from the measure population criteria, and that reference would then be related (through an act relationships) with one or more other acts through the same kinds of relationships allowed for other criteria.  Multiple precondition relationships could be specified, all of which must be true for the criteria to succeed.  These precondition relationships are like the ON clauses associated with the joins.

One thing I don't like about this is the way that references need to be used.  The challenge is that the content of the precondition is an act reference.  I've seen how having to remember the components of an identifier of an act can make things challenging for people reading specifications.  In OASIS ebXML specifications, identifers are UUIDs or names.  Names are locally unique within the message, and can later be given globally unique identifiers assigned by the system.  We don't have a similar capability in HL7, but I just realized something about the long forgotten RUID type.  The HL7 II data type is made up of two components, the root (of type UID), and the extension.  The root typically identifies the namespace from which the identifier comes from, and the identifier itself is stored in the extension component.  The root attribute is often a UUID or OID, but it can also be of the RUID type.  That's simply a type reserved by HL7 in balloted specifications.

What I'd like to do in HQMF is say that the RUID that is represented by the string "local" represents a namespace defined by the message or document in which the content appears.  Then, rather than having to remember an OID or UUID for each act that is referenced, we could just identify it by saying "local", and giving it some sort of locally unique identifier (such as a name) in the extension portion.  We could further define this namespace as being the same as the namespace used by the <localVariableName> element found in act relationship elements.

The ED Encounter example above would appear as follows:

    EDEncounter.effectiveTime.high - OrderToAdmit.effectiveTime 
  <methodCode code='COUNT'/>

  <methodCode code='SUM'/>
      <id root='local' extension='EDEncounter'/>
           <id root='local' extension='OrderToAdmit'/>

Arguably, the XML should include derivation relationship from the measureObservationDefinition to the criteria which defines the variables EDEncounter and OrderToAdmit.  However, these acts are already defined and named in the context of the HQMF Document, and I see no need to provide additonal XML just for the sake of "completeness".

Translated, it means
  1. For the measure populations defined by the variable names in the derivation expression (EDEncounter and OrderToAdmit),
  2. for each EDEncounter, OrderToAdmit pair 
  3. if an OrderToAdmit is related to EDEncounter by the "component" relationship (which is to say that the order occured in the encounter)
  4. compute the derivation expression
  5. and report the sum and count over all terms.
This turns into executable code pretty well.  An example translation into SQL appears below (assuming that population are created as views as in my prototype of 8 months ago)

 SUM(EDEncounter.effectiveTime.high - OrderToAdmit.effectiveTime),
 COUNT(EDEncounter.effectiveTime.high - OrderToAdmit.effectiveTime)
 FROM EDEncounter
 JOIN OrderToAdmit
 ON OrderToAdmit.EncounterID = EDEncounter.ID

It is too late to make this correction for the content that went out to ballot, but this is a ballot comment that I will make.  After all the purpose of balloting is to detect and correct for stuff like this.

Now that inspiration has been satisfied, I need to get back to what I should have been doing...