Monday, July 11, 2011

Smells like I dunno...

One of the persistent questions in HL7 Structured Documents on the CDA Consolidation guide is how to say I don't know.  HL7 has flavors of NULL, and in IHE I jokingly discussed "Colors" of None (it became a section on "Distinctions of None" in the PCC TF).  Well, for this, I think we have to work with smells of unknown because the current situation stinks.

The challenge here is the distinction between Boolean logic which you learn in high school, and three-valued logic which geeks like me might have learned in college, or encountered in their first more than simple SQL query.

For medications and problems, IHE solved the challenge of unknown for medications and problems using SNOMED CT.  IHE suggested the use of "Drug Treatment Unknown" from SNOMED CT in the substanceAdministration code element for medications.  For problems it suggested that the observation value element use "Past Medical History Unknown" from SNOMED CT.  But for allergies there is no such code.  The CDA Consolidation project decided that the codes were part of the problem and that this should be manageable using the RIM.

The CDA Consolidation project has now embarked upon looking for a consistent solution using the HL7 RIM.  Ideally such a solution would also be consistent with positive and negative assertions around problems, allergies and medications.

negationInd
All acts in HL7 Version 3 had at one time a "negation indicator" which could negate the act.  So, you could readily say:  I did this or I did not do that.  This has since been subdivided because some acts (the all pervasive Observation) have both an act and a value, and sometimes you want to say that the observation wasn't performed, and other times you want to say that the value wasn't observed.  So now there is an "Action negation indicator" and a value negation indicator.  For CDA, we are still using the original negation indicator until Release 3 when we absorb the new RIM and Data Types.

The Negation Indicator (and its replacements) are indicated to be of the BL type, which supports true, false and NULL.  However, in XML representations where the BL type is used and appears as an attribute, it can ONLY have the values true or false.  So they really aren't of the BL type, rather they are of the BN (Boolean Non-NULL) type in what they can represent.  So even though BL supports three-valued reasoning with unknowns, it doesn't for structural attributes like negationInd.

I'm not sure whether that restriction was intentional (it seems to have been).  It does mean that there are things you can say in the RIM that you cannot say in an XML representation.  In the RIM you can say "I don't know whether this action occurred or did not".  But it cannot be implemented using the XML Implementation Technology specification (ITS) because that specification restricts negationInd to be only true or false and doesn't allow NULL values.  One could argue that the presence of the nullFlavor attribute on the RIM classes in the XML ITS addresses this issue, but it fails to provide a complete representation. In the RIM, you can say, I don't know if the patient received any medication between January 1 and January 12th of 2011, or you can say, I don't know if they received blood thinners between those dates, or at just about any level of detail (just like you can with negation).  I'm not clear on the way nullFlavor works on classes, but it would seem to restrict use of attributes on the class, so you cannot be "broadly uncertain" on the class.

Because the XML ITS doesn't support a nullFlavor on negationInd, CDA doesn't support it either.  So an alternate representation is needed.

uncertaintyCode
There is an alternative supported by all Act classes in the RIM to represent degrees of uncertainty.  Alas that code system only allows for statements of normal, and uncertain statements, and doesn't really get into any real quantification of uncertainty (perfect uncertainty = a true unknown).  But CDA didn't use it (probably because it didn't have much utility as specified), so that isn't even a choice, even if it had a better code system.

extensions
Extensions are right out because the rule about extensions is this:  
These extensions should not change the meaning of any of the standard data items, and receiving applications must be able to safely ignore these elements.
And adding an extension to say that X should be interpreted as being unknown would change the meaning of X, and that it couldn't safely be ignored.

Developing an Alternative
So, lets look at various kinds of I dunno...
  1. It isn't known whether the patient has any diseases.
  2. Patient is known to have a disease, but the identity of the disease is unknown.
  3. It isn't known whether the patient has (or has had) _____. (e.g., Chicken pox).
  4. It's not known whether the patient has any allergies.
  5. Patient is known to have an allergy, but the identity of the allergen is unknown.
  6. It's not known whether the patient is allergic to ____.
  7. It isn't known whether or not the patient is taking any medications.
  8. The patient is known to be on a medication, but that medication is not known.
  9. It isn't known whether a patient has taken medication _____.
There is a pattern here.
  1. I know that a disease/allergy/medication exists, but I don't know what it is.
  2. I don't know whether a specified disease/allergy/medication exists.
  3. I know nothing about diseases/allergies/medications.
Pattern #1
This is relatively easy to represent in CDA using codes.  You need to be able to indicate the type of observation produced an unknown result for problems and allergies.  For meds, that really isn't necessary because the classCode for substanceAdministration tells you that you are dealing with the administration of a substance, and the manufacturedLabledDrug indicates you are talking about a drug.

An unknown Problem
<entry>
  <observation classCode="OBS" moodCode="EVN">
    <code code="64572001" displayName="Disease" 
      codeSystem="2.16.840.1.113883.6.1"/>
    <value xsi:type="CD" nullFlavor="ASKU"/>
  </observation>
</entry>

An unknown Medication
<entry>
  <substanceAdministration moodCode="EVN" classCode="SBADM">
    <consumable>
        <manufacturedProduct>
          <manufacturedLabeledDrug>
            <code nullFlavor="ASKU"/>
          </manufacturedLabeledDrug>
        </manufacturedProduct>
      </consumable>
    </substanceAdministration>
</entry>

An unknown Allergy
<entry>
  <observation classCode="OBS" moodCode="EVN">
    <code code="106190000" displayName="Allergy" 
      codeSystem="2.16.840.1.113883.6.1"/>
    <value xsi:type="CD" nullFlavor="ASKU"/>
    <participant typeCode='CSM'>
      <participantRole classCode='MANU'>
        <playingEntity classCode='MMAT'>
          <code nullFlavor="ASKU"/>
        </playingEntity>
      </participantRole>
    </participant>
  </observation>
</entry>

Pattern #2
This is much more difficult. We want to be able to say that the "status" of the observation is unknown.  This is similar to saying it was negated.  There is a nullFlavor attribute that can be used with the observation.

A Specific Problem is unknown
<entry>
  <observation classCode="OBS" moodCode="EVN" nullFlavor="ASKU">
    <code code="64572001" displayName="Disease" 
      codeSystem="2.16.840.1.113883.6.96"/>
    <value xsi:type="CD" code="38341003" displayName="Hypertensive Disorder" 
codeSystem="2.16.840.1.113883.6.96" codeSystemName="SNOMED CT"/>
</observation>
</entry>

A Specified Medication is Unknown
<entry>
  <substanceAdministration moodCode="EVN" classCode="SBADM" nullFlavor="ASKU">
<consumable>
<manufacturedProduct>
        <manufacturedLabeledDrug>
          <code code="81839001" displayName="anticoagulant drug"
codeSystem="2.16.840.1.113883.6.96" codeSystemName="SNOMED CT"/>
</manufacturedLabeledDrug>
      </manufacturedProduct>
    </consumable>
  </substanceAdministration>
</entry>

A specified Allergy is Unknown
<entry>
  <observation classCode="OBS" moodCode="EVN" nullFlavor="ASKU">
    <code code="106190000" displayName="Allergy" 
       codeSystem="2.16.840.1.113883.6.1"/>
    <value xsi:type="CD" code="300916003" displayName="Latex Allergy"
codeSystem="2.16.840.1.113883.6.96" codeSystemName="SNOMED CT"/>
    <participant typeCode='CSM'>
      <participantRole classCode='MANU'>
        <playingEntity classCode='MMAT'>
          <code code="111088007" displayName="latex"
codeSystem="2.16.840.1.113883.6.96" codeSystemName="SNOMED CT"/>
</playingEntity> </participantRole> </participant> </observation> 
</entry>

Pattern #3
Now that we've solved pattern #2, this one is easy.  We just make the disease, allergen or medication very general to say that we don't know about diseases, medications, or allergies/allergens.

Nothing is known about Problems
<entry>
  <observation classCode="OBS" moodCode="EVN" nullFlavor="ASKU">
    <code code="64572001" displayName="Disease" 
      codeSystem="2.16.840.1.113883.6.96"/>
    <value xsi:type="CD" code='64572001' displayName='Disease'
      codeSystem='2.16.840.1.113883.6.96' codeSystemName='SNOMED CT'/>
</observation>
</entry>

Nothing is known about Medications
<entry>
  <substanceAdministration moodCode="EVN" classCode="SBADM" nullFlavor="ASKU">
<consumable>
       <manufacturedProduct>
         <manufacturedLabeledDrug>
           <code code='410942007' displayName='Drug or Medicament' 
codeSystem='2.16.840.1.113883.6.96' codeSystemName='SNOMED CT'/>
</manufacturedLabeledDrug>
       </manufacturedProduct>
     </consumable>
  </substanceAdministration>
</entry>

Nothing is Known about Allergies
<entry>
  <observation classCode="OBS" moodCode="EVN" nullFlavor="ASKU">
    <code code="106190000" displayName="Allergy" 
      codeSystem="2.16.840.1.113883.6.1"/>
    <value xsi:type="CD" nullFlavor="NA"/>
    <participant typeCode='CSM'>
      <participantRole classCode='MANU'>
        <playingEntity classCode='MMAT'>
          <code code='413477004' displayName='Allergen or Pseudoallergen' 
codeSystem='2.16.840.1.113883.6.96' codeSystemName='SNOMED CT'/>
        </playingEntity>
      </participantRole>
    </participant>
  </observation> 
</entry> 

Now we appear to have solved this problem.  The trick was recognizing that the nullFlavor attribute on the class was essentially where the missing nulls for negationInd disappeared to.  It appears that I managed to sniff out a solution ;-)  ... and it doesn't stink.  In fact, it works pretty consistently across all classes.  I could easily see how to extend it to say that it is unknown whether a patient had a particular procedure or encounter, or even family history of any particular disease.

  -- Keith

P.S.  All the XML Validates against the CDA Schema


This post was updated on July 18th to reflect adjustments made by the HL7 Structured Documents Workgroup based on feedback from Grahame Grieve. The key change was in using a general code rather than unknown for pattern 3.  A similar change was made for How to say No...

Friday, July 8, 2011

Where should the Query Go?

I had a different plan for today's post, but Wes Rishel's post on sending questions to the data deserves some comments.

He rightly points out that one of the data aggregation challenges is the assumption of a common model (or data scheme) for representing healthcare information.  But that really isn't a hard problem to solve if you build from existing work (e.g., the HL7 CCD, the HITSP C32, the CDA Consolidation project, or work coming out of existing SIFramework projects).

The CCD/C32 data model to use is straight forward, and all certified EHRs to date can handle it for most of the important data elements needed by public health (immunizations is not included, but most others are).  In the future, that model as revised by the CDA Consolidation project should be used when it is ready.  HL7 has been working on a model of the "Virtual Medical Record" for more than a year now (this is about the fourth incarnation of this project).  It seems like it has legs, and part of the reason for that is they based their work on the CCD this time around.

Don't be surprised if the S&I Framework develops a project to deal with this one.  That project has been on the table for quite some time; Wes seems to be re-socializing it now for ONC.  If you recall back in December of 2010, ONC outlined a set of 12 Initiatives that it sought feedback on with respect to prioritization and on the initiatives themselves.

One of the 12 initiatives was something called "Population Health Query".  The slide is reproduced as an image below:


Ignore the deadlines and the policy issues.  Assume that both of those will be dealt with at some other pay scale than mine; not that I'll be quiet about it later.  You can be almost certain I'll be talking about crazy schedules later, and that a flurry of debate will occur on policy issues, just as has happened with respect to The Direct project and certificates.

There are a couple of use cases here:

  • Public Health Situational Awareness -- making it possible for public health to be aware of what is going on around them.  Infection disease monitoring, food-born illnesses, et cetera.
  • Clinical Decision Support -- So broad as to be not unclear what they are talking about.  I'm assuming dash boarding applications and big data stuff.
  • Quality Monitoring -- Again, very broad because they don't talk about quality of what, and as we've learned from Meaningful Use, what you ask for with respect to measures of quality can be as much work to deal with as other functionality.  Here, they are likely talking about things like monthly rate of occurrences for hospital acquired infections, and similar stuff.  OK, these involve similar kinds of stuff as situational awareness, but different time frames.
  • Prevention -- To prevent is to act appropriately, and to do so requires awareness of what is happening so that appropriate prevention measures can be taken, so that really falls under situational awareness above or under quality monitoring.

The pattern that Wes describes is this:

  1. A system responsible for the care of a population (call it a care manager for now) sends a query to it's subscribers.
  2. The subscribers, EHR systems, HIEs, other Health IT solutions (call them clinical data sources) receives the query.
  3. Periodically (depending on how the query is structured and how often the data gets updated), the clinical data source sends back a response containing the "summarized" response.
Now let me share with you a picture of an Actor/Transaction diagram from an IHE profile called Care Management.

PCC-9 is the query from the Care management system, and PCC-10 and PCC-11 are the responses back.  This profile has had little uptake for one key reason:  A standardized representation of the data to gather needs to be readily available.  IHE used the Guideline construct from the HL7 Care Record DSTU to try to indicate what data needed to be gathered, but it doesn't quite work because the Guideline portion of that DSTU was not quite done yet.

So, the challenge won't be the model (although I almost certainly dread spending months in SIFramework meetings establishing "the model").  My hope they use the model developed by the Transfers of Care project and found in the standard selected for that project.  We'll see. ONC always seems to want to reestablish consensus around problems already solved once or twice rather than forcing a choice that will be unpopular with one minority or another.

Coding and agreeing to a model are certainly critical to success.  But even more critical is coding the queries  that public health is using and starting to provide the machine readable content that developers need to use to get the right outputs from the right inputs.  And when I say queries, what I really mean is that public health needs to go back and code the guidelines that those queries are being generated for.

The proposed solution that Wes wrote about is "already decided" and furthermore is assumed to be the right model, it just needs the details filled in (e.g., the standards to use).  I knew a guy who used to end his sentences about really cool ideas with "it's just a matter of writing the code."  OK, this might be a good idea, but the devil is definitely in the details, and it presumes the solution is the right one.  Engineers know how that sort of thinking really traps innovation and can back you into a corner.

I'm not totally convinced that pushing the mining of the data onto the provider systems is the right answer (See Taking cost out of the system).  It may be useful as a method of avoiding certain policy issues (by returning aggregated rather than patient identifiable data).  But, it pushes the cost of processing these queries onto the provider IT systems, with the concomitant issues of varying interpretations of how to compute the results.  Aggregating metrics from multiple systems which have different implementations for computing them could result in some pretty unreliable statistics.   

My favorite quality measure/public health kind of numerator or denominator definition goes something like this:  Patients who not been given DRUG within TIME of EVENT, except where DRUG is contraindicated over all patients seen for EVENT.  Fill in the details with your favorite guideline and/or quality measure.  Tell me how you computed "contraindicated".  Compare your results with those of others.  And please don't make contraindicated a data element just to solve your problem.  I watched the HITSP quality measures group do just that.  But providers don't capture contraindication for a medication in their EHR.  That's the whole point.  They shouldn't have to, because the contraindication is already computable in the EHR (it might be the patient's age, other medications, allergies, or other diagnoses).

I think a more cost effective way would be to enable public health to gather the raw data needed to generate the dashboard from providers.  That gets rapidly complicated due to policy, rather than technology.  And unlike some, who worry about the communication of PHI, it's not the need for consent to access the PHI that is the big issue for public health in these systems.  They are already exempted under HIPAA for that access (unless overridden by the state, which is a problem).  The real issue for them is the amount of security they have to put into place on their end when they have PHI.  So they don't want it in many cases.

A slight modification of ONC's proposal occurs to me.  That is to support a "registry" model, something like what is already supported by some for quality measurement.  This is where providers can send pseudonomized data to a trusted third party (maybe an HIE) that public health could then query against.  That enables an either/or situation.  A provider can choose to use the trusted third party to respond to public health queries on their behalf, or they can do it themselves.  And it enables public health to be the trusted third party if they choose to do so.

Think about it.  From a public health perspective, early warnings come NOT from the ED, where you often have patients in advanced stages of disease, but from the GP where patients are in early stages. It may be easier to collect data from the ED, but the reason for that is size and volume.  From the GP perspective, adding computation to manage queries from public health A) doesn't provide them with much value, and B) isn't something they would want to invest in without it that value.  Is public health going to pay them anything to send data?  Probably not, so we need to make it feasible for them to send data to a trusted third party who has the resources do to the aggregation and computation.  That's a lot simpler for those providers.

Some might say that the value would be in their meaningful use reimbursement. If ONC/CMS were to try to put that value into MU Stage 3 reimbursement, I might remind them that providers will already gotten the biggest share of value out of stage 1 and 2 already.  And don't even think about making this a Stage 2 requirement.  That train is on its way out of the station and this effort will not be ready by the time it has vanished into the distance.

So, send the query to the data might be right, but it may be different queries to different data sources to support providers large and small.  Aggregate queries to systems that can support it; finer grained queries for systems that cannot, but are happy to let you have (and secure) their data. 

With respect to standards, I sure hope we don't try to invent anything.  There are already some pretty good standards for aggregate queries.  I'll bet everyone reading this can think of at least one query language suitable for generating aggregate results, and NO, it's not GELLO.  HINT:  It's been around for three decades.

Thursday, July 7, 2011

Is Social Media in Need of Standards?

Imagine that we had three or more phone companies in the country, and that to talk to friends who subscribed to one of them, you needed to own a phone designed to work with their network, and they all worked differently.  Yeccch!  Imagine that to get good national coverage your phone actually needed to speak six or seven different protocols but still presented you with the same interface.  Still pretty yucky, but at least as far as you are concerned, it works (in fact, that is how mobile phones work).

At least on the platform side, it's a bit frustrating.  If you've ever been part of an e-mail discussion across two (or more) mailing lists where one or the other half of the list doesn't have access, it can be quite difficult to follow the full conversation.

The same is true across Twitter, Linked In, FaceBook and now Google+.  I have four (or five or six, depending on how you count them) different social media accounts (yes, I finally got into Google+ last night when they opened it up again).

Apps help.  At least with TweetDeck I can follow conversations on Twitter and LinkedIn, and with FlipBoard, FaceBook, Twitter and Google Reader.  I've also been able to cross link my Twitter posts to Google Buzz, Linked In and FaceBook.  And with TwitterFeed and Google's FeedBurner I can link blog postings to Twitter (and thence everywhere else twitter is connected).

But right now it's a hodge-podge of sticks, twine and duct tape that I'm using to glue all of this together.

There isn't yet one application that lets me follow and interact with the entire social media stream, though I expect that will happen soon (just like my cell phone today lets be talk through 3G, GSM, and CDMA networks)

As for standards, it really isn't hard.  Just give me an RSS feed to each, and allow each to be populated through an RSS or Atom feed.  Then let me pick the application that organizes things the way I want, and I'd be really happy.  Given the state of competition right now, it probably won't happen anytime soon, and it will be up to App developers to create more duct tape.

Speaking of duct tape, anyone have a good app to post from Twitter to Google Plus?

   Keith

Wednesday, July 6, 2011

Getting to Know HealthIT Standards

You can find today's post over at Hospital Impact.

In other news:

I've been working on templates some more and have nearly completed a prototype generating the template content from an XML representation.  More on that later this week.  I've also been fighting with XSLT transforms of XHTML dealing with the positional problems I mentioned last week.  More on that also when I have it working.

Back to work...

Monday, July 4, 2011

Feeling Rebelious

The beginning of July always makes me feel just a bit rebelious.  If you happen to be around this AM and are bored looking for something to read, here are some of my more rebelious posts from this month in previous years (going back to the year this blog was born, in 2008):



Now go out there and blow something up.  Or at least watch.  And enjoy your holiday (for those of you in the US), or our holiday for those of you who are not.

   Keith


Friday, July 1, 2011

IHE Patient Care Device and IHE Cardiology supplements published



IHE Community,

IHE Patient Care Device supplements published for Public Comment and Trial Implementation

The IHE Patient Care Device Technical Committee has published the following supplement to the forthcoming IHE Patient Care Device Technical Framework for public comment in the period from July 1 to August 1, 2011:

·       Infusion Pump Event Communication (IPEC) 

The document is available for download at http://www.ihe.net/Technical_Framework/public_comment.cfm. Comments submitted by August 1, 2011, will be considered by the Patient Care Device Technical Committee in developing the trial implementation version of the supplement.  Comments should be submitted at http://www.ihe.net/pcd/pcdcomments.cfm.

The Committee has also published the following supplements to the forthcoming IHE Patient Care Device Technical Framework for Trial Implementation as of July 1, 2011:
·       Alarm Communication Management (ACM)
·       Waveform Content Message (WCM)
These profiles will be available for testing at subsequent IHE Connectathons.  The documents are available for download at http://www.ihe.net/Technical_Framework.  Comments should be submitted at http://www.ihe.net/pcd/pcdcomments.cfm.

IHE Cardiology supplement published for Trial Implementation
The IHE Cardiology Technical Committee has published the following supplement to the IHE Cardiology Technical Framework for Trial Implementation as of July 1, 2011:
  • Cardiac Imaging Report Content (CIRC)
 This profile will be available for testing at subsequent IHE Connectathons.  The document is available for download at http://www.ihe.net/Technical_Framework/. Comments should be submitted to the online forums at http://forums.rsna.org.


Accessing Positional XML Elements in XSLT

One of the most frustrating things about XHTML and some other document XML formats is that they don't deal with document structure very well.  An H1 tag simply creates a level 1 heading, and so on for H2 through H6.  These tags only "introduce" a new section, they don't really create the appropriate section structure in the XML document.  This creates nightmare for organizations trying to manage structured documentation because it is rather difficult to deal with section structures declaratively in languages like XSLT.  Even the Apple PLIST format is frustrating (prompting this tweet) because the key and value (or dictionary) are arranged positionally rather than through containment.

The same sort of problem shows up when processing HL7 Version 2 messages (say to convert them to HL7 CDA) when manipulating often proprietary XML translations of HL7 Version 2 supported by different interface engines.  HL7 does have a standard Version 2 XML format (see Version 2.x Schemas on this page), but it is not widely supported in products.  So, if you want to process a Version 2 ORU message , you will often find XML that contains one or more OBR tags followed by several OBX tags without proper containment.

The general structure of the ORU includes the following definition:

{ [ORC] Order common
   OBR Observations Report ID
  { [NTE] } Notes and comments
  { [OBX] Observation/Result
    {[NTE]} Notes and comments
  }
}
But most commonly when this is translated into XML, instead of the OBX being contained within the preceding OBR as would be expected, it follows it. So you wind up with this:

OBR
OBX
OBX

Instead of this:

OBR
  OBX
  OBX

Just like in XHTML where you wind up with this:

H1
P
P
H2
P
P

Instead of:
H1
 P
 P
 H2
  P
  P

To process either of these in XSLT can be very challenging, because often you want to be able to relate the processing of each OBX (or P) to its OBR (or H#) in the hierarchy.

So how do you process this sort of XML using XSLT?  And how can you make the processing efficient?

There are several different tricks you can use:
The first trick is a little dicey sometimes but can be pretty efficient:  Use a two-pass transform where the first pass creates the appropriate structure and the second pass can do the real work.  It makes use of the xsl:text element with the disable-output-escaping attribute set to yes.  The basic details of this trick are:

  1. For the first heading, you generate some sort of section XML tag, inside the xsl:text element
  2. For every heading thereafter, you close the previous section tag, and open a new one using the same mechanims.
  3. At the end of processing, you close the last open section tag. 
This works just fine for the OBR/OBX example, because there is one level of nesting.  It doesn't work very well for the H1/H2/P example because of multiple nesting levels.  Two pass processing is OK, but I like to keep it inside one XSLT.  There is a way to do that using the EXSLT node-set() function:

  1. Create a variable containing the XML generated during the first pass.
  2. Convert it to a node-set using the EXSLT node-set() function.
  3. Apply templates to a selection from that node-set.

The skeleton below shows how you would do this:

‹xsl:variable name='pass1xml'› .. Generate the first pass XML ... ‹/xsl:variable›
‹xsl:apply-templates select='exslt:node-set($pass1xml)'/›

This will work with SOME XSLT processors, but not others.  The way that disable-output-escaping often works is by inserting an XML processing instruction in the XML output that will write the appropriate text when it is output to a file or stream.  This processing instruction is understood internally by the XSLT processor as a special processing instruction, and the XML output is not usually reparsed. So, normally you need a two-stage pipeline instead of being able to handle it all in one stage.  [Note: This technique isstill very useful for creating a two stage processing pipeline inside one XSLT when the disable-output-escaping feature is not used, and almost all XSLT processors support the node-set() function.]

Another way to deal with this problem is by using the sibling axes in XSLT.  This is more complex, and requires a good deal more explanation of how it works.  I'll save that discussion for next week.