Showing posts with label Query Health. Show all posts
Showing posts with label Query Health. Show all posts

Thursday, January 31, 2019

A QueryHealth for AllOfUs

Those of you who have been reading this blog for years are already familiar with what I've written in the past about Query Health.  For those of you who haven't check the link.

Recently, I've been looking into All of Us, a precision medicine research program that takes the ideas of Query Health to the next level.  The original thinking on Query Health was about taking the question to the data.  All of Us has a similar approach, but instead of querying data in possibly thousands of information systems, it uses a raw data research repository to collect data, and a cloud-based infrastructure to support research access to the curated data that is prepared from the raw data sourced from thousands of information systems.  I find the best detailed description today to be found in the All of Us Research Operational Protocol.

There's a lot to be learned from Query Health, and the first thing that any group putting together a large repository of curated and anonymized data is certainly going to be security and confidentiality.  Anonymization itself is a difficult process, and given the large data sets being considered, there's no real way to fully make the data anonymous.

Numerous studies and articles have shown that you don't need much to identify a single individual from a large collection of data collected over time.  A single physician may see 3-6 thousand patients in a year.  Put data from two of them together an the overlap is going to be smaller.  Add other data elements, and pretty soon you get down to a very small group of people, perhaps a group of one that combined with other data can pretty easily get you to the identity of a patient.

For Query Health, we had discussed this in depth, and regarded counts and categories smaller than 5 as being something that needs special attention (e.g., masking of results for small counts).  There was a whole lot of other discussion, and unfortunately my memory of that part of the project (over 8 years old now), is rather limited (especially since it wasn't my primary focus).

Another area of interest is patient consent, and how that might related to "authorization" to access data via APIs from other external sources.  A lot of this can be automated today using technologies like OAuth2, OpenID Connect, and for EHR data, SMART on FHIR.  But as you look at the variety of health information data repositories that might be connected to All of Us through APIs, you wind up with a lot of proprietary APIs with a variety of OAuth2 implementations.  That's another interesting standards challenge, probably not on the near-term horizon for All of Us, considering their present focus.

It's interesting how everything comes back eventually, only different.  One of my ongoing roles seems to be "standards historian", something I never actually thought about.  I'm guessing if you hang around long enough, that becomes one of the roles you wind up adopting by default.

Tuesday, May 21, 2013

Temporal Relationships Again

Today I spent a bit of time with the HQMF editorial team.  One of the issues that we spent quite a bit of time on was temporal relationships.  In the RIM, temporal relationships are represented by vocabulary terms, such as SBS or EAE (Starts before Start, Ends after End).  SBS implies that X.effectiveTime.low < Y.effectiveTime.low.  So now the question of how to deal with <= comes up, and we didn't have a good answer.

To deal with relationships such as "X occured within one hour of the start of Y", you have to deal with what the HL7 RIM calls the pauseQuantity associated with the act relationship.  The pauseQuanity is defined as being of type PQ.TIME (a physical quantity using units of time).  This is incredibly complicated to get right when you have multiple relationships between two different acts and the starting times and ending times associated with them.  If you set the pauseQuantity to 1 hour, and use the SBS act relationship type between act X and act Y, you are saying that X, if paused by one hour, still starts before the start of Y.  Since that is the exact opposite of what you want, what you would do would be to set pauseQuantity to -1 hour, and then use SAS.  That means that X, if moved backwards one hour in time, starts after the start of Y.  Are you confused?  I certainly still am.


<actCriteria>
  ... Criteria for Act X ...
  <temporallyPertains typeCode='SAS'>
    <pauseQuantity value='-1' unit='h'/>
    <actCriteria>

  ... Criteria for Act Y ...
    </actCriteria>

  </temporallyPertains>

</actCriteria>


One of the participants (Marc Hadley from MITRE) proposed that we change pauseQuantity from PQ.TIME to IVL_PQ.TIME, so that we could say from 1 to 2 hours.  I started to say that doesn't work to say what we want it to say, because ... and then realized that depending on what we are comparing, it certainly could work.  The key here are four vocabulary terms covering temporal relationships, SCW, SCWE, ECW and ECWS (Starts Concurrent With Start, Starts Concurrent with End, Ends Conncurrent with End, and Ends Concurrent with Start).

Now lets look at how you could say this:  If you want act Y if it started within 0-1 hours from X, you would have X be related to Y via SCW and would set the pause quantity from 0 - 1 hour.  X.start delayed by 0 to 1 hours is concurrent with Y.start means exactly what we want.

<actCriteria>
  ... Criteria for Act X ...
  <temporallyPertains typeCode='SCW'>
    <pauseQuantity xsi:type="IVL_PQ">
       <low value='0' unit='h'/>
       <high vaue='1' unit='h'/>
    </pauseQuantity>
    <actCriteria>

  ... Criteria for Act Y ...
    </actCriteria>

  </temporallyPertains>

</actCriteria>

The latter means the same as the former, is more flexible, and also has the advantage of allowing the use of the inclusive attribute on the low and high bounds, so now we can also get into the finer details of less than vs. less than or equal to.

The problem is that pauseQuantity is defined in the HL7 RIM to be of type PQ.  We cannot just magically change it to a different type like IVL_PQ.  However, we can certainly state that the pause quantity is some value within a particular range by using the uncertainRange capability of the HL7 Version 3 Datatypes Release 2.  That gives us:


<actCriteria>
  ... Criteria for Act X ...
  <temporallyPertains typeCode='SCW'>
    <pauseQuantity>
      <uncertainRange>
        <low value='0' unit='h'/>
        <high vaue='1' unit='h'/>
      </uncertainRange>
    </pauseQuantity>
    <actCriteria>

  ... Criteria for Act Y ...
    </actCriteria>

  </temporallyPertains>

</actCriteria>


And so, we have a much simpler representation for dealing with temporal relationships, without any RIM changes.  The next step is to restrict the allowed vocabulary when the uncertainRange portion appears to be SCW, SCWE, ECW, and ECWS.  In fact, by limiting to just those four terms we can address any kind of relationship needed between the end points of two events.

We have to bounce this past the Structured Documents workgroup, because it is a variation on the original solution, and we don't want to be making stuff up that the workgroup didn't agree to in reconciliation.  I think this will be pretty straight-forward.

Thursday, April 4, 2013

It means what I want it to

We struggled for the longest time today trying to figure out how to say the following in HQMF:

Find me the first instance of this medication (e.g., Aspirin), in these kinds of encounters (e.g., ED visits where DX = MI).

The challenge wasn't the medication, or the type of encounter, or even encounters where DX = MI, but rather how to apply FIRST and DURING operations at the same time so that FIRST returned more than one item.  E.g., it was the first item of its type in the time period expressed by the temporal relationship.

We struggled with it for quite a while, and I'd written something on the board, and then Dragon pointed out that we'd added the <subsetCode> attribute to the <temporallyRelated> act relationship and it all came together.

I don't know what that means I said, but we can define it to mean what we want it to.  Marc said "Wait, let me see if I can make this work before I agree." and he started digging into code.  The rest of use looked at it some more to poke more holes in it.  "Does it still work if we change LAST to FIRST?" I asked, and yes it did.  "Does it work if we use a different temporal relationship?" I asked again, "Like BEFORE?" Chimed in Dragon.  Yep, that worked too.  And Marc said, "Yes, I can make it work.  I like this."

And so, even though we weren't sure what it meant when we looked at it at first, by the time we were done, it meant exactly what we wanted it to, and it wasn't even a stretch to believe that someone had actually designed it to work that way.

So now, if you want to know what the average time is for a patient who needs it to be given Aspirin in the ED if they are having a heart attack, we know how to A) specify that measure, and B) compute it from the specification.  Being able to do A is nice, but the biggest change in HQMF Release 2 is that we can do so much more of B from A.

   Keith

Wednesday, April 3, 2013

Back to QueryHealth and HQMF and a gratuitous Star Trek reference



We are trying to finish up HQMF Release 2.0 this week, seven of us, meeting in MITRE's offices in Bedford, MA.  Initially this meeting was scheduled so that we could complete the big push to get the standard out in time to be referenced in Meaningful Use stage 3.  With CMS' announcement that Stage 3 will not come out this year, I don't see the need to rush, rather I'd like to get it done well.

One of the challenges we worked out today was how to combine and reuse different criteria.  We worked out a fairly elegant solution that allows a data criterion to be further refined, or be used with other criteria in union and/or intersection operations.  We also worked out some simplifications around excerpts, realizing that we could just limit ourselves to FIRST, LAST, MIN and MAX and use that to be able to support Nth, or Nth from the end, without having to support the entire value set possible.

There are a few other simplifications in the works, and the combine and reuse solution also seems to allow us to address what I had quickly relabeled the 7 of 9 problem.  The problem is that some measure developers want to be able to create numerator or denominator criteria where what is being counted meets N out of M other criteria.  This is a pattern than is often used in certain kinds of assessment instruments, and rather than using an assessment result, you could just compute it based on the known data.  Using the combination solution, there's a way we can say that.

Wednesday, February 13, 2013

Simple Math Expressions

Time to get back to the technical stuff.  One of my outstanding work items is to finish up Simple Math for inclusion in the QDM-based HQMF guide as we agreed to at the HL7 Working Group meeting.  I spent some time with Marc Hadley on the phone a few weeks back, and we firmed up what it would consist of, which I report here:

Simple Expressions

Simple expressions is a language for expressions based on JavaScript, whose purpose is to enable simple computations in HL7 specifications such as HQMF.  It allows elements of the model found in a standard or implementation guide to be accessed and computed with.  The language is based on JavaScript to formalize the behaviors of the computation model, but it need not be implemented using a JavaScript interpreter.

Note: We should likely be more specific as to what version of JavaScript it is based upon.  I'd propose ECMAScript 5.1 would be a good starting point.

Note: Strings needed to be added to support dynamic construction of messages for HeD, so this is now "Simple Expressions" instead of Simple Math.

Identifiers

Identifiers in Simple Math follow the rules of JavaScript identifiers with a few simplifications to make them more usable in other implementation environments.

identifier ::= [a-zA-Z][a-zA-Z0-9_]*

Data Types

The data types of Simple Math are the Date and String objects and numbers and Booleans.  Literals for null, strings, numbers and Boolean values are represented as in JavaScript.  Regular expressions and function declarations are not supported.  The Date object will be wrapped to support additional member operations supporting date arithmetic, and we'll need to evaluate that further.

Regular expressions are not supported.

We will add support for a limited set of HL7 data types: IVL_TS, IVL_PQ, PQ and perhaps CD seem to be the most likely.

Operations

The language does not support assignments or side effects, thus, there are no assignment operators, nor post- or pre-increment/decrement operators, nor a delete operator.  Bit operators (~, &, |, ^, <<, >> or >>>) are also not supported.

The strict equivalence === and !== are not supported.

Given the lack of side effects in this language, there is no need for a comma operator, since it serves to order expressions, and evaluates to the last expression.  If you would write an expression as expr1, expr2 in JavaScript, then in Simple Expressions, all you need write is expr2.

The instanceof and typeof operators are allowed.

Note: We hadn't come to a conclusion on the ternary operator ?:.  This is rather useful, but not readily supported in other implementation environments.  One of the challenges is that according to the rules of ECMAScript, only one of the two expressions after the ? is evaluated, and this may not be readily implemented elsewhere.  I think we allow it, and simply note that some implementations may not be able to fully implement the semantics as specified.  It's therefore a warning to users to use the ternary operator sparingly if they want their expressions to be computable on the widest number of platforms.

Note: We also haven't come to a conclusion on whether arrays are needed or not.

Miscellaneous

  • Comments within expressions aren't supported because they can be included in the surrounding XML where the expression is used.
  • There are no multi-step expressions.  Line breaks are treated like white space in an expression, and there is no need to terminate an expression with a ; because it is terminated by context.
  • String expressions force conversion of numbers to strings, just as in Java Script.

Implementing the Language

One of my design goals for implementing this language was that common expressions in the language should be readily converted to another implementation language through an appropriate sequence of regular expression substitutions.   As the language is currently specified, this amounts in C, C++, C#, Java and JavaScript to being an identity transform (or nearly so).  

My test case for success would be to build an implementation that converts common expressions in Simple Expressions to SQL.

Tuesday, December 18, 2012

The year in review


If I had to sum up this year quickly, I'd say for me it was about Quality Measurement, Patient Engagement, and Meaningful Use Stage 2.  Most of my posts have been related to these topics.  This was also the year I took serial writing seriously.

January started year 2 of Meaningful Use.  In January, I didn't go to the IHE Connectathon for the first time in nine years.  I'll be back again this year.  For fun, I wrote The Characters in a HealthIT Standards Meeting.  In January, I was deep into planning and designing transforms for HQMF and Query Health.

February started the month that Consolidated CDA 1.1 was finalized by HL7, after several months of work with IHE, HL7 and S&I Framework.  Early in the month was an IHE meeting in Paris.  My family came along for the ride, and we learned quite a bit about communicating with French speakers.  Of course there was HIMSS, and the almost anti-climactic announcement on the Meaningful Use regulation the day after most everybody has already gone home.  ONC and HHS, stuck in their process, couldn't say much until too late.  I coded, and subsequently demonstrated transforms for HQMF and Query Health at HIMSS.  I spent a good bit of time reviewing the rules.

In March, I started a four-part post which starts here and is completed here about having too many definitions of summary care record in the rules.  It got fixed in the final rule, but there is still yet room for improvement (maybe in the 2016 edition).  In case you weren't aware, the data required for "data portability", and for transfers of care is defined identically in the rule.  HL7 began forming its Mobile Health Workgroup

I began my series on Health IT Standards 101 in April (and still have to finish it, although I expect it to be a never ending story).  For fun I wrote Four Career Lessons from the Incredible Hulk.  I issued an Ad Hoc Harley to Jeff Klann for his ability to pick up where I left off on Query Health.

May is the month for one of three regularly scheduled HL7 Working Group Meetings.  We spent a lot of time talking about FHIR, CDA, Templates and the NwHIN RFI at that meeting.  The NwHIN RFI resulted in nothing significant* happening with respect to governance out of ONC in 2012, but there were some good discussions going on here and elsewhere.  It remains to be seen what will happen in the US.

June is another good month to read some more posts about HQMF and Query Health.  At the beginning of the month, I got to take my daughter to a not so secret White House meeting called the Patient Access Summit.  And my daughter joined The Walking Gallery.  She also wrote her first post for me for this blog.  June begins the summer of standards work that I typically engage in.

July was a slow month for the blog, which usually means a heavy one for other activities.  July is when we meet to deal with the public comments and produce the trial implementation content for IHE Profiles.  I finally got around to starting my series on moving from C32 to C-CDA at the end of the month.  A good friend and colleague, Glen Marshall, retired this month.  He got the first lifetime achievement Ad Hoc Harley.

In August, ONC finally kicked off the ABBI Project, which has become one of my bigger "volunteer" efforts.  Of course, I love the acronym, and so does my daughter.  We often joke that ONC gave it that name to suck us in.  She and her younger sister even did a 180 second ad for it.  I continued my series on moving from C32 to C-CDA in this month.  And of course, we got the final rules this month, which led to another series of posts.  In August, I attended the HL7 board meeting where we approved making the IP free.  Only the rest of the world wasn't to know until nearly a month later.

September was kicked off by this huge announcement from HL7.  I've been working towards that since I was elected to the board, and will continue working on it in my second term.  I began playing quite a bit with OAuth in this month, even implementing an OAuth 1.0 Provider.  The test methods for Meaningful Use Stage 2 began to show up this month in waves.

October is the month that my ABBI prototype was moved to the cloud.  I spent a lot of time on ABBI in this month. Just for fun, I built a go-cart in a couple of days.

In November, continuing with one of this year's themes, I created a slide called Hashtag Soup: Relating QDM, HQMF, eMeasures, QueryHealth, QRDA, SIFramework and MeaningfulUse Stage2.  That slide got picked up an used by an ONC staffer for an educational slide deck.  Elsewhere, I spent half a day listening to, and responding to testimony to a House subcommittee on Meaningful Use and Meaningful Results.

December isn't over yet, but I've already been to an IHE Meeting, the mHealth Summit, and the ONC Stakeholder Meeting this month, including the ABBI Town Hall Meeting.  IHE USA announced it's new certification program, and well, there's just a lot going on.

* Just days before the year ended, ONC announced a grant on Governance.

Tuesday, November 27, 2012

Hashtag Soup: Relating QDM, HQMF, eMeasures, QueryHealth, QRDA, SIFramework and MeaningfulUse Stage2

This showed up in my inbox yesterday:
Hello Keith, 
I am trying to figure out how the following abbreviations are connected - NQF's QDM, HQMF, eMeasures, Query Health, QRDA, QDM based QRDAs, others.
Rather than individual definitions, I am a bit in the dark around how each of these are interconnected.

Appreciate your help.
Warm Regards, Shyam 
It's a question worthy of a full post, rather than a brief answer, so here goes.


QDM is the National Quality Forum's (NQF) Quality Data Model.  It is an information model representing the essential data needed to generate quality measures.  Because it is an information model, it doesn't necessarily go into the level of detail needed in an implementation, but it certainly describes the high level structures that an implementation needs to compute quality measures.

eMeasures is a term describing the electronic representation of quality measures.  In common use, it often refers to the electronic measures that NQF developed to represent the quality measures required under the ONC & CMS Meaningful Use regulations.  It is also used to refer to the HL7 HQMF.

HQMF stands for Health Quality Measure Format.  This is an HL7 Draft Standard for Trial Use (DSTU).  The DSTU is presently being reballoted by HL7 for a second release.  This is an electronic format for the representation of quality measures.  Release 1 is currently used by NQF to deliver eMeasures for Meaningful Use.  Release 2 was developed in large part based on pilot work being developed by Query Health.

Query Health is an ONC Standards and Interoperability Framework project whose purpose is to develop standards to enable sending the questions to the data.  Its key goal is to enable clinical research.  We used HQMF in query health because the kinds of questions that Quality Measures need answers too are often the same kinds of questions that show up in Clinical Research.  HQMF is a declarative format for expressing those questions.  We revised and prototyped a new schema for HQMF that is simpler, easier to read, and able to be computed in a variety of programming environments.  I've written quite a bit about Query Health on this blog.

QRDA stands for Quality Reporting Data Architecture.  If HQMF/Query Health/eMeasures represent the question, then QRDA represents the answers.  QRDA is an HL7 implementation guide on CDA Release 2 that describes the format for reporting quality data on a single patient (Category I), or aggregate results on multiple patients (Category III).  The former is a DSTU, the latter nearly so.  There is also an implementation guide showing how data modeled using the QDM can be represented in a QRDA.  Both Category I and Category III specifications have been identified as being required standard formats for reporting quality measures under the Meaningful Use 2014 Certification Criteria.

MAT is the Measure Authoring Tool.  This is a tool for creating eMeasures currently being maintained by NQF, but which will be transitioned to a new maintainer in early 2013.

VSAC is the NLM Value Set Authority Center, where value sets used for eMeasures and other standards used in Meaningful Use regulation are published.

If you want a poster-sized PDF of the content, you can get it via Google Drive:


Wednesday, November 7, 2012

Continuous Variable Measures and QueryHealth

If you thought I was done with Query Health, clearly I'm not.  I've been spending the last three days reviewing the HL7 Ballot for HQMF, reissued because the R-MIM Diagram hadn't been included during the previous ballot.  Today, BTW, is the last day to register to vote on this reissued ballot.  Voting closes in a week.

One of the challenges I knew I would face is continuous variable measures.  The challenge of continuous variable measures is being able to compute with more than one value.  I though I was done with this, but it appears that I'm not yet.

The first challenge is average wait time.  Suppose that for each visit, I have captured the patient arrival time, and the time that they are seen by the physician.  Suppose that each of these is a separate observation.  Say we label one observation with "Arrival", and the other with "Seen" (i.e., using localVariableName).  What I'd like to compute is the difference, for each visit, between Seen and Arrival.


The DataCriteriaSection identifies the data of interest.  Each criterion describes a set of acts that might be computed from (and can be labeled with a localVariableName).  

<entry>
  <localVariableName>Arrival</localVariableName>
  <actCriteria>
    ...
     <code code="441968004" codeSystem="2.16.840.1.113883.6.96"
      displayName="time of arrival at healthcare facility" />
  </actCriteria>
</entry>
<entry>
  <localVariableName>Seen</localVariableName>
  <actCriteria>
    ...
    <code code="308930007" codeSystem="2.16.840.1.113883.6.96"
      displayName="seen by health professional" />
  </actCriteria>
</entry>

So, what I'd think should work would be something like:

<measureObservation...>
  ...
  <derivationExpr>Seen.effectiveTime - Arrival.effectiveTime</derivationExpr>
  ...
</measureObservation>

Where it gets complicated is that there's an implicit JOIN here on Arrival and Seen.  Each of these observations is related to the same visit.  It wouldn't make any sense to subtract the Seen of one visit from the Arrival of another.

As specified, the measureObservation above doesn't express the implicit join.  I'm still struggling with how this needs to work.  I can see a way to introduce a higher level criterion that would rectify this case, but it doesn't work for other cases where the JOIN criteria isn't as simple.  For example, in the case where the measure is the time from placing an order for something, to the time that order was fulfilled.  I could also see expressing the JOIN as a precondition on the measureObservation, but that gets into other challenges when dealing with relationships between acts and representing them in an expression.

While I know what is wrong, what I'd like to do in my ballot comment is identify what's right.

Thursday, August 16, 2012

Is that an Active Problem?

One of the questions recently raised for Query Health is how to determine whether a problem is active or not.  This post applies to CCDA, and to Query Health, so listen up ;-)

There are a lot of ways to address reporting of the problem status:

In CCD and CCDA, there's the problem status observation, which evolved out of the problem status field in CCR.  This observation allowed one of several SNOMED CT codes to be attached to an observation associated with a problem.  I never liked the CCD value set, because it contained problem statuses like "Rule Out" and "Ruled Out", "Chronic", "Intermittent", and "Recurrent", in addition to "Active", "Inactive" or "Resolved".  That value set suffers from the Vehicle Type problem I've discussed before.  So, the HITSP C32 restricted that to Active, Inactive or Resolved, and clarified the difference between inactive and resolved.

So, that's one way you could do it.

But there's also the Problem (or Allergy) observation itself.  If there's an end date on the observation, that indicates when it stopped being a problem.  If there's no end date however, you don't know if it has stopped yet.  The assumption that the problem is still active is probably reasonable based on the documentation given, but knowledge outside of the document might tell a provider otherwise (e.g., for self-limiting problems).

Finally, though, there's the Problem Concern Act (and the Allergy Concern Act).  These acts wrap the problem (or allergy), and indicate when the problem (or allergy) became a concern to the provider (e.g., were added to the problem (or allergy) list, and when they stopped being a concern.  The concern act represents the Concern class, and was originally discussed in the HL7 Patient Care Workgroup back in early 2005 (and perhaps even earlier).  Back then, it was called Condition, but since then has been renamed Concern.  Concern is perhaps the most critical class in HL7 V3 that supports problem oriented medical records.

The concern is described in the Care Provision Domain, in the Care Structures topic, and was included in the Care Record DSTU back in 2006 if I remember correctly.  The material is currently being revised by HL7.  The Concern Act structure was adopted in CCD and CCDA, but never quite explained as well as in the original Patient Care work. The easiest place to find it (for HL7 Members) is in the 2008 Ballot Archives.

One of the key statements in that work was on the use of statusCode in the concern act. These are the meanings of the values in statusCode for that act:

new - draft (not messaged, we use these internally though)
active - currently being concerned
completed - resolved concern
aborted - a concern which we decide to drop without resolution
suspended - a concern which is active but we are setting it aside
nullified - a concern which was created in error
obsolete - a concern which has been replaced by another concern

In CCDA, the only values allowed are active, completed, aborted, and suspended.

Also, in CCDA, there are rules for the Allergy Concern Act which SHOULD be copied to the Problem Concern Act.

SHALL contain exactly one [1..1] statusCode, which SHALL be selected from ValueSet ProblemAct statusCode 2.16.840.1.113883.11.20.9.19 STATIC 2011-09-09 (CONF:7485).
SHALL contain exactly one [1..1] effectiveTime (CONF:7498).
If statusCode="active" Active, then effectiveTime SHALL contain [1..1] low (CONF:7504).If statusCode="completed" Completed, then effectiveTime SHALL contain [1..1] high (CONF:10085).
So, if you want to know my preferred way to indicate whether a problem is active or not, it's using statusCode on the concern act.  It's far simpler than any other mechanism.  If the concern act wrapping the problem or allergy has a statusCode of active, then the problem is active, and if it say's completed, then it's resolved.  Any states beyond that invoke electron-microscopy and angel detectors.

The next trick will be to express this in HQMF.  This is actually not difficult at all.  We add an inner actCriteria where the classCode = CONC (to represent the related concern for this problem), and where statusCode has the value Active.



      <entry>
        <localVariableName>HasDiabetes</localVariableName>
        <observationCriteria>
          <id root="0" extension="HasDiabetes"/>
          <statusCode code="completed"/>
          <value xsi:type="CD" valueSet="2.16.840.1.113883.3.464.1.37"/>
          <definition>
            <observationReference moodCode="DEF">
              <id root="0" extension="Problem"/>
            </observationReference>
          </definition>
          <sourceOf typeCode="SUBJ" inversionInd="true">
            <actCriteria classCode="CONC">
              <statusCode code="active"/>
            </actCriteria>                  
          </sourceOf>
        </observationCriteria>
      </entry>



-- Keith


Monday, July 23, 2012

What is HQMF?

John Moehrke asked for this one.  I've written nearly a double dozen posts on HQMF, and more than three dozen on Query Health, but never stopped to explain what HQMF is.

The Health Quality Measure Format (HQMF for short), is an HL7 standard format for documenting the content and structure of a quality measure.  It is intended to represent quality measures used in a healthcare setting.  It is an XML document format based on the HL7 Reference Information Model (RIM), just like CDA is, but instead of describing what happens in a patient encounter, the HQMF standard describes how to compute a quality measure.

There are six or eight key components of a quality measure (depending upon measure type and level of detail).  These are structured at three different levels of detail.  At the first level of detail is metadata describing the quality measure.  This goes into the header of the document, and describes the who wrote it, the dates over which it is valid, who validated it, and other details about how the measure works or is used.  The metadata makes it easy to find the quality measure.  You can write a valid HQMF document and only include this level of detail.  The body of the document can be written in a PDF or any other multimedia format.  This level of detail is intended to support legacy documentation on quality measures.

The second level of detail provides a human narrative description of the quality measure in three (or four) sections:
  1. Measure Description
  2. Data Criteria
  3. Measure Population
  4. Measure Observations
The measure description simply contains human readable narrative describing the measure, it's purpose, how it works, et cetera.  The data criteria section describes the "data of interest" to the measure.  This is where you find descriptions of the events, statuses and attributes of those events that need to be captured to make the measure effective.  The measure population section describes the major components of the measure, including the initial population, the numerator, the denominator, and various special cases (known as exclusions or exceptions depending upon how they are treated).

A typical ratio measure has criteria describing the initial population, and criteria for numerator and denominator.  These criteria explain how to counts of patients (or other items) that match the specified criteria.

Other measures (e.g., a continuous variable measure), need to describe not just what to count, but how to compute from the items selected in the initial population (e.g., to compute average ED wait times).  How those  observations are computed is described in the measure observations section.

The third level of detail needed to automate measure computation is provided in machine readable entries in the last three sections.  These entries appear when the measure is specified at the highest level of detail.   These entries provide  the computer with instructions on how to count and compute the results of the measure.  For ratio measures, these entries describe how to combine the data of interest using Boolean logic to select the items to count.  For measure observations, these entries describe what items need to be computed with, and the measure observation definitions then describe the computation.

HQMF Release 1.0 was developed and balloted in 2009 by HL7 as a Draft Standard for Trial Use (DSTU), and was published in March 2010 as a DSTU (and has now expired).  During the two year DSTU period, the standard was used by NQF to retool more than 100 existing quality measures into electronic format.  NQF also developed the Measure Authoring Tool to create HQMF documents.  The ONC S&I Framework adopted and adapted the standard for use in its Query Health project, designed to allow health data to be queried by external sources.  (See my series of posts on Query Health).

The Query Health adapted version of HQMF became the foundation of Release 2.0 of the HQMF standard, currently being balloted by HL7.  This will also become a Draft Standard for Trial Use for two years.  MITRE has developed an experimental transform that converts HQMF R1 to HQMF R2 format, with some limitations.  I've heard reports that they've been able to use it successfully on about 80% of the NQF retooled measures.

HQMF can do more than just define "how to count".  The HQMF standard can be used to describe what the output of counting should look like.  When used in combination with the QRDA specification (an implementation guide on the HL7 CDA), HQMF can tell you what data needs to go into the QRDA.  It's also possible to use an HQMF document with just a data criteria section to describe what needs to be sent over an interface, or to describe the entries that should be present in a CCDA document (a variation on the use already designed into HQMF and QRDA).  So HQMF is the "query", and QRDA is (or can be) the output of that query.  There are three different categories of QRDA.  As related to HQMF, QRDA Category I is patient level data for a single patient.  A collection of QRDA Category I documents can be used as the data inputs to a measure calculation.  QRDA Category II can be used to report on the patient data actually used to calculate the measure, enumerating all patients and their data in a single document.  Finally, QRDA Category III can be used to report the aggregated results.  QRDA Category I is fully described in the HL7 QRDA DSTU.  Category II and Category III are alluded to, but not described in detail.  It's easy to figure out that QRDA Category II would be very similar to Category I, but just contain more patient data.  The format for QRDA Category III is currently being worked on (in fact, I missed a meeting today because I'm teaching about HQMF and QRDA).

Given current developments and implementation efforts that have been going on in Query Health and for retooled measures, I expect HQMF to be on the short list of standards to be considered for Meaningful Use Stage 3.

This post was updated on Tuesday, July 24th, to address comments below. I clarified what HQMF is, added more information about the third level of detail, and described a bit more about QRDA.

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:

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

  <methodCode code='SUM'/>
  <precondition>
    <encounterReference>
      <id root='local' extension='EDEncounter'/>
      <component>
         <actReference>
           <id root='local' extension='OrderToAdmit'/>
         </actReference>
      </component>
    </encounterReference>
  </precondition>
</measureObservationDefinition>

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)

SELECT 
 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...

Saturday, June 30, 2012

An Odd-Essay through Time

I've been working on trying to clean up HL7's temporal vocabulary for HQMF.  The problem is with boundary conditions as it is relevant to time relationships.  We have two boundaries, the start and end of an event, and three comparison operations (less than, equal to and greater than), which gets me to twelve different vocabulary terms, which gives me all the atoms needed, right?

Wrong.  We are dealing with intervals, and intervals can be open or closed. While < and = together make up <=,  we cannot use two vocabulary terms to specify a SINGLE relationship.  So in order to describe the <= relationship between two items, we actually need to have five different comparators, <, <=, =, >= and >.  This is still NOT a closed set of relationships over the inverse operator, because we are missing <> (or != if you prefer that notation).

So we now have two choices from the source of the relationship, either the start or the end, six comparison operators, and two choices for the target of the relationship (again, start or end) = 2 x 6 x 2 = 24 different terms.


One of my desires is to avoid complexity for implementers.  Twenty-four vocabulary terms seems to be too complex.  Do we really need to support >=, <= and != ?  After all, these are just the same as NOT <, NOT > and NOT =, and we could achieve NOT with the negationInd attribute in the RIM.  But using negationInd doesn't make it any simpler, and in fact, divides the problem up across two different attributes (negationInd and typeCode).  We already have existing vocabulary supporting >= and <=, because we have terms for OVERLAP which needs to deal with these operation.


So, I decided to look at it a different way.  In a time range, there are five discrete parts.  Before the start, the start, after the start and before the end, the end, and after the end.  We need to be able to talk about any of these five parts in sequence.  If we label the parts A, B, C, D and E, we need to be able to talk about:

  1. A
  2. AB
  3. ABC
  4. ABCD
  5. ABCDE
  6. B
  7. BC
  8. BCD
  9. BCDE
  10. C
  11. CD
  12. CDE
  13. D
  14. DE
  15. E
The range identified by ABCDE is "all time" and so can be dropped.  It is unnecessary because all acts are related to all other acts with respect to all time, and so this is a meaningless relationship.

I'm going to reorganize the remaining acts, and show how they relate to temporal comparisons in the following diagram:


Each of these defines a time range related to a target act.  We can indicate that the start of the source act occurs within each of these ranges, or the end of the source act occurs within them to define some useful relationships.  The names of these relationships would be something like SAS or EAS to represent Start after Start, or End after Start.  That's nice because those are already HL7 vocabulary terms.  So, now we can apply S and E to each of the above, and get 28 relationships.  Hmm, this is a dead end isn't it.  After all, didn't we want something smaller than 24?


It gets worse.  If you've been swift, you might also note that there are some tests where you want to test both start and end of the source act.  The existing OVERLAP, DURING and CONCURRENT are examples of temporal relationships that do this.  So now we are up to 31 or more.  Yech.


I'm sorry to report, it doesn't get better (at least yet).  After some more digging, I found a paper (which I should have looked for first).  James F. Allen published a paper in 1983 that reports that there are 213 = 8192 possible relationships that can be described between two definite intervals.  Dr. Thomas Alspaugh provides a great explanation of Allen's paper.  You should probably read that summary to understand the rest of this post.


Dr. Alspaugh explains that there are 13 basic relationships between two intervals.  These 13 basic relationships are distinct (meaning each can be distinguished from the other), exhaustive (because the relationship between any two intervals A and B can always be identified as following one of these patterns), and is purely qualitative.


Where things get interesting in Allen's algebra is when Alspaugh produces a table that shows what happens when you "compose" a relationship.   Composition of a relationship describes the how to compute the relationship r.s that between A and C, when A r B and B s C.   As it turns out, there are 27 "composite" relations when you perform composition over the original set of 13 basic relationships.

So, I looked over the 27 different relationships, and this is what I found:

  1. One of the resulting relationships (full), is true for all intervals, and so is not worth addressing.
  2. Ten of the relationships already existed in the HL7 ActRelationshipTemporallyPertains Value Set.
  3. Only one of the relationships in the HL7 ActRelationshipTemporallyPertains Value set (Ends After Start) doesn't appear in the list of 27 relationships generated through composition (its inverse doesn't appear in either place).
Then I went back to the original discussion to see whether I'd been able to, using this method, match the requirements.  There are two terms in NQF's Quality Data Model which aren't covered in the current vocabulary.

Starts before or during
A relationship in which the source act's effective time starts before the start of the target or starts during the target’s effective time. An Act is defined by HL7 as: “A record of something that is being done, has been done, can be done, or is intended or requested to be done.”

  • A pacemaker is present at any time starts before or during the measurement period: [Diagnosis active: pacemaker in situ] starts before or during [measurement period]
  • A condition [diagnosis] that starts before or during [measurement end date], that means the diagnosis occurred any time before the measurement end date including the possibility that the diagnosis was established on the measurement end date itself.

Ends before or during
A relationship in which the source act terminates before the target act terminates.

  • To state that intravenous anticoagulant medication is stopped before inpatient hospital discharge: [Medication administered: anticoagulant medication (route = IV)] ends before or during [Encounter: encounter inpatient]

The challenge with these two is that they alter the meaning of during in the use of the term for "Starts Before or During". According to HL7, DURING as a vocabulary term means wholly contained within the time period of the target.  So, Starts During would mean that the start time is bounded by the range of (target.start through target.end), using the non-inclusive forms of the boundaries.

What NQF did was redefine during so that an event (e.g., pacemaker present or diagnosis occured) would be considered to be in the measure period even if the event occured on 20121231.  Why?  Because, like just about everyone else (including me until Grahame corrected us all), we didn't know how to record the time boundary correctly.  Remember the proper way to bound a time expression lasting one year is [20120101, 20130101).  This means: Starting on January 1st of 2012 [inclusive], up to, but no including January 1st of the following year.


So, let's go back and fix the definition of Starts before or During so that it could be changed.  Now it is simply "Starts before End."  Surely that code is present?  Actually, it isn't.  And similarly, Ends before or during becomes Ends before End (EBE).


And so, the two codes we need to add to the HL7 Vocabulary to support everything that's been asked of for HQMF are SBE (Starts before End) and EBE (Ends before End).  Which puts us back to the original 12 operators that I started with in this post.  And the realization that there are 8180 more relationships that we cannot handle simply, and likely don't need to, and that there are quite a few different ways to look at temporal relationships.


It seems that we don't need to worry to much about the differences between < and <= after all.  Especially if we can readily control one of the boundaries to make sure it is open or closed as necessary.


There's probably a whole post in this on the proper handling of intervals in software in general.  But I'll save that for a later date.  I have a harmonization proposal to finish.

  -- Keith


Friday, June 29, 2012

Continuous Variable Measures - The Final Solution?

We are honing on on a model for how to perform Counting in HQMF and QueryHealth, which I discussed earlier this week.  I had been working on the Harmonization proposal for dealing with time relationships, but that got messy.  It turns out there is a whole algebra for dealing with intervals, and the more I looked at it, the closer I got to having my brain explode.

So, I'll go back to that after dinner (because I promised that tonight), and finish up this because it is also relevant for harmonization (and my brain will remain intact).

Essentially what we are proposing is a new <MeasureCriteria> element similar to <PopulationCriteria>, <NumeratorCriteria>, and <DenominatorCriteria>.  That element will define the SET of objects over which an aggregation computation is performed.  The computation will be defined within a <MeasureObservation> element that references the <MeasureCriteria> element.

My first crack at how <MeasureObservation> would look like was this:


<MeasureObservationDefinition>
  <id .../>
  <code code='aggregationFunction ' codeSystem='...'/>
  <derivationExpr>computation</derivationExpr>
  <sourceOf typeCode='DRIV'>
    <localVariableName>variableNameInComputation</localVariableName>
    <measurePopulationReference>
      <id .../>
    </measurePopulationReference>
  </sourceOf>
</MeasureObservationDefinition>

Inside, the computation could use Simple Math or another expression language, and the expression could be based on a specific <measurePopulationCriteria> defined in the <PopulationCriteriaSection>.

As I looked at this though, I wondered if I really even needed to reference the <measurePopulationCriteria>, because when it is defined, it already has a <localVariableName>.  Would it not in fact be simpler to just say:

<MeasureObservationDefinition>
  <id .../>
  <code code='aggregationFunction' codeSystem='...'/>
  <derivationExpr>computation</derivationExpr>
</MeasureObservationDefinition>

In the HQMF, the local variable for the <measurePopulationCriteria> element is already defined earlier in the same document, and thus could be inferred from the document context.  Channeling Marc's annoyance with unnecessary XML (and my own I might add), I got rid of it.

So the remaining piece here was to define the aggregation functions allowed in this act.  The ones that I can come up with include COUNT, AVERAGE, SUM, MIN, MAX, MEDIAN and MODE.  Everything else can be computed from these.  In fact, AVERAGE is readily computable from SUM and COUNT, but it is done often enough to merit inclusion in the set.  Another issue here though is that we often want to compute several of these results, for example, the AVERAGE, and the range (MIN and MAX), and we might also want to compute STDEV and VARIANCE.  Unfortunately, I can only associate one <code> with the observation.


After a bit of digging around, what I realized was that what I was doing was aggregating, and applying one or more methods during the aggregation.  So now I have a slight variation on the previous model, where code is fixed to AGGREGATE (a new value that I now need to add to ActCode), and the clone name is changed to reflect what this has become.  I've added methodCode to indicate what aggregation methods are to be used, and you can repeat it to indicate that you want to use more than one (e.g., AVERAGE, STDEV and COUNT).

<measureAggregateDefinition>
  <id .../>
  <code code='AGGREGATE' codeSystem='2.16.840.1.113883.5.4'/>
  <derivationExpr>computation</derivationExpr>
  <methodCode code='aggregationFunction' codeSystem='...'
    codeSystemName='ObservationMethodAggregate'/> (...)
</measureAggregateDefinition>

Now the only thing left to do was define the value set for ObservationMethodAggregate (a value set I just made up to appear in ObservationMethod).  As I was going through this list, I realized that I either needed to define how standard deviation and variance are computed (over a population or a sample), or allow for both methods.  I figured it would be easier to include both, providing greater clarity about what was meant in each code.

CodePrint NameDefinition
COUNTCountCount of non-null values in the referenced set of values
SUMSumSum of non-null values in the referenced set of values
AVERAGEAverageAverage of non-null values in the referenced set of values
STDEV.SSample Standard DeviationStandard Deviation of the values in the referenced set of values, computed over a sample of the population.
VARIANCE.SSample VarianceVariance of the values in the referenced set of values, computed over a sample of the population.
STDEV.PPopulation Standard DeviationStandard Deviation of the values in the referenced set of values, computed over the population.
VARIANCE.PPopulation VarianceVariance of the values in the referenced set of values, computed over the population.
MINMinimaSmallest of all non-null values in the referenced set of values.
MAXMaximaLargest of all non-null values in the referenced set of values.
MEDIANMedianThe median of all non-null values in the referenced set of values.
MODEModeThe most common value of all non-null values in the referenced set of values.

Fortunately, if my memory serves, that leaves me with nothing more to do on this topic (given that I've already updated the R-MIM in the Visio Diagram to support this).

So, how does this address continuous variable measures.  Let's take a simple example:  Average ED visit time.  This is pretty straight-forward.

  1. Define a measure over encounters (recall that you need to specify this in the measureAttribute element of the QualityMeasureDocument).
  2. Create an encounterCriteria that selects only ED encounters in the dataCriteriaSection.  
  3. Now create a measurePopulationCriteria with the localVariableName EDVisits in the populationCriteriaSection.
  4. Finally, create the measureAggregateDefinition in the measureObservationsSection, and add this XML inside it: 
  5. <derivationExpr>EDVisits.effectiveTime.high - EDVisits.effectiveTime.low</derivationExpr>

We'll need to indicate that when AGGREGATE is computed, and no aggregation method is specified, that the implementation can determine what it does, and suggest that it at least compute the COUNT and SUM, but may produce other aggregate statistics.

Am I done with this?  Probably not, but hopefully enough to get us through the ballot.

OK, off to dinner and then back for some more stuff.

Tuesday, June 26, 2012

Computing in HQMF and QueryHealth

This post begins to address continuous variable measures and similar kinds of computations in Query Health and HQMF.

Most of what HQMF does is allow you to specify how things are being counted, and as I mentioned yesterday, what you are counting is a function of your implementation model.  Change your implementation model (or enter it at a different point), and what you count can go from being patients to encounters, or even something else.

An HQMF counting diabetic patients who have an A1C result greater than 9% is similar to the SQL COUNT() function in a select statement with a complex join and criteria.

SELECT COUNT(P.pid) FROM Patients P 
JOIN Conditions C on C.pid = P.id 
JOIN Results R on R.pid = P.pid
WHERE C.Condition = 'Diabetes' 
AND R.type = 'HgA1C' 
AND R.value > 9
AND R.unit = '%'

Other measures need more than counting.  Suppose you wanted to compute a quality measure for the average number of days of stay for delivery of a newborn.  In order to compute that, you would need to find all inpatient encounters for delivery of a newborn, and then take the average over the lengthOfStayQuantity attribute of each of these encounters.  In SQL, this would look something like this:

SELECT AVG(E.lengthOfStayQuantity) FROM Patients P
JOIN Encounters E ON E.pid = P.pid
JOIN ReasonForVisit R ON E.eid = R.eid
WHERE R.reason = 'Delivery'

SQL provides the AVG, SUM, MIN, MAX and COUNT aggregate statistic functions.  In HQMF, to get to SUM(), what you do is create a <measureObservation> element that contains a <derivationExpr> element describing what should be accumulated, and references to the appropriate criteria elements from which the observation is computed.  I've used the criteria reference elements in the example below.  In the original HQMF this would just have been an encounter element.

<observation classCode="OBS" moodCode="DEF">
  <id root="b421c8a3-7949-11de-8a39-0800200c9a66"/>
  <derivationExpr>
    Encounter.lengthOfStayQuantity.value  
  </derivationExpr>
<sourceOf typeCode="DRIV">
    <localVariableName>PhysicalDepartureFromED</localVariableName>
    <encounterReference>
      <id root="b421c8a9-7949-11de-8a39-0800200c9a66"/>
    </encounterReference>
  </sourceOf>
</observation>

In the example above, for Query Health and HQMF, we can completely drop the sourceOf element.  That's because the HQMF document supports definitions of local variables for all data criteria elements.  I'd also change the name from observation to something else with more meaningful name.  Perhaps accumulatedValue, with classCode fixed to OBS, and moodCode fixed to DEF.

<accumulatedValue>
  <id root='b421c8a3-7479-11de-8a39-0900200c9a66'/>
  <derivationExpr>Encounter.lengthOfStayQuantity.value</derivationExpr>
</accumulatedValue>

This is greatly simplified.  Now, if you want to do more than count things, you can do so.  One of the challenges that I tried to address in Simple Math is what kind of expression should go into derivationExpr. HQMF doesn't actually address the language syntax.  The HL7 preferred language for this kind of stuff is GELLO, but frankly, I have a hard time with GELLO.  I have to translate this expression into something that is executable in Java, C#, SQL or XQuery.  I don't have the luxury of being able to install a GELLO interpreter for that purpose (can you imagine trying to convince a data center team that it is OK to install a GELLO intepreter in a data center that has to have 5 9's availability?).  The same is true for any other complex language, what we really need is Simple Math.


The next issue that comes up is what if you want to sum things only if certain conditions are met, just as you want to count things where certain conditions are met.  To do that, you need to add a <precondition> element to the <accumulatedValue> element, and that needs to support the same kinds of preconditions that we count with. This lets me attach the precondition to the encounter that says "it must be an encounter for a newborn delivery."


So now, I could also compute the number of central line days and the number of central line infections for a time interval by computing the sum of central line days for each patient, and the number of separate central line associated infections for the patient for a one month period.  The results of the HQMF would give the necessary data to compute the CLABSI infection rate.


It seems obvious how to compute when there is one variable.  Even the case above is simply two different summations over a single patient related variable.  Moving up the scale, you might even want to compute standard deviation and variance.  That's also straightforward.  If you have COUNT(x), SUM(x) and SUM(x2), you can compute standard deviation and variance, by simply expanding the formula:  VAR(x) = SUM(x - AVG(x))/(n-1)


But as soon as you move into a case where you have multiple variables (necessary for dealing with regression statistics) it gets more challenging. The reason for that is because you aren't necessarily working in the same scale.  What would happen if you had two variables in the expression?


The short answer is that I haven't figured that out yet.  But I will, and when I do, I'll report on that as well.

Monday, June 25, 2012

Counting other things in QueryHealth and HQMF

In Query Health, we've been focused on counting patients who meet particular criteria.  But just as we are counting patients, we could also be counting encounters, providers, organizations, or just about anything else that you wanted to count to compute a quality measure.  The key to determining what you are counting depends upon your data model.

We bind what gets counted through the use of definition elements and reference elements in the dataCriteriaSection.  Each criteria element references something that is defined in a definition element.  But we don't really say a thing about how the data model itself is structured in the HQMF representation in the HQMF.

In the Query Health model, we start with a patient.  A patient has demographics (which is often how we select them for the initial patient population).  They can also have problems, medications, allergies, immunizations, diagnostic results, vital signs, procedures and encounters.  We might tie the record of a problem, medication, allergy, immunization, diagnostic result, vital sign or procedure back to an encounter.  And we might tie an encounter back to a provider.  So in that model, when you start from patient, you analyze the HQMF from that perspective, and that is what you wind up counting.

But, if you wanted to count encounters, you'd organize your data model differently.  Instead of starting with a patient, you'd start from the encounter.  Your "IPP" would not longer be a collection of patients, but instead, would be a collection of encounters.  You might select them based on what was recorded during that encounter (e.g., diagnosis), or what was done during that encounter (e.g., a specific procedure), or details about the encounter such as the type of setting (e.g., ambulatory, ED, inpatient). An encounter would have a set of problems, medications, immunizations, allergies, diagnostic results, vital signs, procedures and patients associated with it.

How you organize the model influences greatly how you wind up counting, but it doesn't require many changes to HQMF.  It may have been written with a particular model (the patient-centric one) in mind, but HQMF in practice doesn't assume any particular data model.  So what would we need to change?


To clarify things, we'd want to change the "Initial Patient Population" entry to become the "Initial Population" entry.  This is simply a clarification that what we are counting could be something other than patients.  The other thing we would want to do is add a classifier to the measure heading to indicate what it is we are counting.  The classifier serves two purposes:

  1. It identifies to the user what is actually being counted. 
  2. It indicates what is being counted so that the implementer can use the correct data model.
To add this classifier, we simply need a code to represent it, and a value set for the different kinds of things that could be counted.  What we are counting could be patients, providers, encounters, procedures, immunizations, lab tests, locations, organizations or just about any other kind of entity or event.  For most cases, I think we would readily use patients, and might use encounters, treatments (procedures and medications) or diagnostics (test results).  There might be cases where it might be interesting to compute quality measures that count providers, organizations, locations, or even devices.

The XML to express this would be pretty simple:
<QualityMeasureDocument>
    ...
  <subjectOf>
    <measureAttribute>
      <code code='COUNTS' codeSystem='2.16.840.1.113883.5.5'
                codeSystemName='HL7 Act Code'/>
      <value code='...' codeSystem='...'/>
    </measureAttribute>
  </subjectOf>

It's pretty easy to imagine a quality measure for an encounter or procedure (and this example could be written either way):  Encounter population = all surgical encounters.  Denominator all surgical encounters on a patient older that 70.  Numerator, all surgical encounters where the patient was given a flu vaccination in the 3 months during or prior to the encounter.

You'll note that I didn't fill in the details for the <value> element.  That's because I don't know what they should be.  We could come up with a value set from SNOMED CT, or from HL7 Vocabularies (we might need to use several, because patients are roles, but encounters and procedures are acts).

With respect to quality measures for those other things, I could imagine cases where what you might want to count the number of times a certain test is used, compared to the number of times that test is positive.  That could tell you some interesting things about the utilization of that test, and it could be compared against other tests, or other uses of the test at other locations or regions.  It too could have a different model that it was executed against.  But this example is also amenable for computation using a patient- or encounter-based model.  In the encounter based model, the denominator would be encounters where that test was ordered.  The numerator would be the number of cases where the result of the test ordered came back positive.  So maybe we don't need a large vocabulary to express what we are counting.

There is another approach to this problem as well, which I'll go into more detail upon tomorrow.  If we expand on our use of measure observation, so that rather than just counting things that match, we accumulate the values of expressions in a <measureObservation> element, we could support a number of additional capabilities.  If you look at the SQL prototype I discussed back in November, you can see where the final output relies on the COUNT  function in the query.  Other aggregated statistics functions could also be used.