The ACC/AHA recently revised lipid/cholesterol guidelines, and have thrown out the Framingham risk calculators we all know and love in Centricity, recommending we replace them with the Pooled Cohort Risk Calculations.
They have helpfully published their methodology on-line here:
http://content.onlinejacc.org/.....id=1770220
page 37-40 or so
I could deal with the exponent of "e" part of these equatoins, and even the need to build large arrays to set up the calculator...
BUT the data to be input requires LOGARITHMs to be extracted from the patient data of age, sex, bp, cholesterol values, etc.
Does anyone know how to calculate a Log in MEL? Its only been 45 years since I had high school math, and I'm not sure I remember how to crunch it out, and I can't find a LOG functoin in my MEL documentation..
Thanks.
Charles Zelnick ME
Stonington ME
Alright, it bugs me that there are apps on the iPhone for this but nothing in Centricity.
so because I knew there was a way to do this, (thank you Mr Potter, R.I.P.), I rooted around online and found a polynomial expansion that will calculate the log. Here is the code below. because you need more interations to get accuracy for larger numbers, the loop goes one order of magnitude beyond the value of z, which comes out fine in when i check this in the spreadsheet.
Next for the arrays...
Anyone want to help me tackle this?
{fn ln(z) {
double a b c d f z
a=(z-1)/z
d=a
b=a
f=z*10
for t=2, t<f, t=t+1
do
b=b*a
c=(1/t)*b
d=d+c
endfor
return d}
}
?
Paste this in a letter and you can test some numbers as below:
Natural Ln of 50: {ln(50)}
Natural Ln of 5: {ln(5)}
Ln of 200: {ln(200)}
Ln of 2000: {ln(2000)}
Did you find any takers willing to help with the coding? I wonder if another option is to build a risk calculator outside of the EMR and use {RUNSHELLOPEN()} to feed it the values (age, cholesterol, etc) and have it return the calculated risk. (Feel free to poke holes in the idea - I don't know if if would work or not.)
Stephen Adams
Runshellopen ??
Is there a good reference for the MEL commands? This command, as well as "Mel_send_flag" are but a couple of the commands that I have never read anywhere. I am always eager to try new approaches, and maybe even help with this.
However, I am not even aware of the runshellopen command to know what I can or cannot do (parameters, etc..)
Thanks.
In the help files under "using data symbols" look for "MEL utility functions." RUNSHELLOPEN passes arguments to an outside program and looks for a returned value. The only place I've used it is within quicktexts to open a web browser to a specific page (e.g. a drug formulary page, etc.)
Place this text in a quicktext and give it a try.... (if you like pizza)
{RUNSHELLOPEN(" http://order.papajohns.com/index.html ")}
I think I sorted this, by converting the equation to MEL and outputting it in a text component. It seems to work.
I need to further release it and test it within my organization before I can share it here.
The ln function was the key above; otherwise, just sorting out if the patient is male/female, caucasian/African American; then the rest of it is just crunching the coefficients in 4 equations.
Cheers.
Charles Zelnick MD
Excellent! Please post a solution when you get it worked out.
Steve
czelnick said:
Alright, it bugs me that there are apps on the iPhone for this but nothing in Centricity.
so because I knew there was a way to do this, (thank you Mr Potter, R.I.P.), I rooted around online and found a polynomial expansion that will calculate the log. Here is the code below. because you need more interations to get accuracy for larger numbers, the loop goes one order of magnitude beyond the value of z, which comes out fine in when i check this in the spreadsheet.
Next for the arrays...
Anyone want to help me tackle this?
{fn ln(z) {
double a b c d f z
a=(z-1)/z
d=a
b=a
f=z*10
for t=2, t<f, t=t+1
do
b=b*a
c=(1/t)*b
d=d+c
endfor
return d}
}
?
Paste this in a letter and you can test some numbers as below:
Natural Ln of 50: {ln(50)}
Natural Ln of 5: {ln(5)}
Ln of 200: {ln(200)}
Ln of 2000: {ln(2000)}
Thank you so much for posting this! I'm just starting on integrating the ACA/AHA guidelines. The lack of support for logarithms in MEL is frustrating.
We are in the process of rolling out a risk calculator to our users. We addressed the lack of higher order mathematical functions in MEL by passing the relevant parameters to a program written in C++; the results are written to a temporary file and retrieved by Centricity via the {RUNTEXTPROCESS()} function. The MEL to launch it looks like this:
{arg=DURATIONDAYS(str(PATIENT.DATEOFBIRTH),str(._TODAYSDATE))+"|"+PATIENT.SEX+"|"+LASTOBSVALUE("BP SYSTOLIC")+"|"+LASTOBSVALUE("HDL")+"|"+LASTOBSVALUE("CHOLESTEROL")+"|"+USERYESNO("Current smoker?")+"|"+USERYESNO("Diagnosed with diabetes?")+"|"+USERYESNO("On BP med?")+"|"+get(PATIENT.RACE,1);RUNTEXTPROCESS(DIR_EMR()+"\scripts\pooled_risk_calculator.exe",arg)}
Ok, I built this last year and it has been working in our system.
You must be a subscriber to the CCC forms and functions, as the code calls those functions.
Pasted below is a text component which we call with a quicktext (.pccvr) -- it will insert information about the patients CV risk and current treatment into the form.
This text component contains 2 important functions - one is the function [ln(z)] which computes a natural log of a number. Had to go back to my high school math over 45 years ago to figure that out.
The Pooled Cohort Cardiovascular Risk calculation [PCCVRisk()] then uses that ln function to compute the patients risk.
I have attached 3 tools that use this code: 1st a Handout that explains the pros and cons of statins; 2nd, a HISTORY page, for easy lookup of cardiovascular information including risk, and finally our current lab letter which fires the code if there are lipid test results.
Statin Decision Balance_4 code fix BHMH Cardiovascular 20141023 Lab Letter 2014_3
Good luck with this, your mileage may vary.
Cheers and Happy New Year.
Charles Zelnick
Island Family Medicine, Stonington, ME
==============code for text component=======================
{fn CVRsk() {
local cvr=""
If match(Toupper(OBSANY("SMOK STATUS")),"CURRENT")>0 then cvr=cvr + "Smoking/Tobacco Use" else "" endif
If OBSANY("HX OF HTN")=="yes" then cvr=cvr + "
Hypertension" else
If ccc_sys_hasdx("ICD-401") then cvr=cvr + "
Hypertension" else "" endif
endif
If ccc_sys_hasdx("ICD-585^ICD-403^ICD-404") then cvr=cvr + "
Chronic Kidney Disease" else "" endif
If ccc_sys_hasdx("ICD-250^ICD-249") then cvr=cvr + "
Diabetes Mellitus" else "" endif
If OBSANY("FH<55MALE MI")=="yes" then cvr=cvr + "
Family History of Heart Disease in a Male before Age 55" else "" endif
If OBSANY("FH<65FEMALMI")=="yes" then cvr= cvr + "
Family History of Heart Disease in a Female before Age 65" else "" endif
If OBSANY("HDL") <40 then cvr=cvr + "
Low HDL, below 40" else "" endif
If OBSANY("CARDCASCORE") <> "" then cvr=cvr + "
Cardiac Calcium Score= " + OBSANY("CARDCASCORE") + " [Normal <300 or <75%]" else "" endif
If OBSANY("CTCARTCASCO") <> "" then cvr=cvr + "
Cardiac Calcium Score= " + OBSANY("CTCARTCASCO") + " [Normal <300 or <75%]" else "" endif
If OBSANY("CRP") <> "" then cvr=cvr + "
C-Reactive Protein(CRP)= " + OBSANY("CRP") + " [Normal <2 mg/ml]" else "" endif
If OBSANY("CRP HI SENS") <> "" then cvr=cvr + "
C-Reactive Protein(High Sensitivity CRP)= " + OBSANY("CRP HI SENS") + " [Normal <2 mg/ml]" else "" endif
If OBSANY("CRP QUANTI") <> "" then cvr=cvr + "
C-Reactive Protein(Quantitative CRP)= " + OBSANY("CRP QUANTI") + " [Normal <2 mg/ml]" else "" endif
If PATIENT.SEX == "M" then
If PATIENT_AGE() >45 then cvr=cvr + "
Male, Age >45" else "" endif
else
If PATIENT_AGE ()>55 then cvr =cvr + "
Female, Age >55" else "" endif
endif
If cvr=="" then cvr="No Major Cardiac Risk Factors Recorded" endif
return cvr
}
}{fn ln(z) {
double a b c d f z
if z<1/2 then return "Cannot find ln of number below 0.5" endif
a=(z-1)/z
d=a
b=a
f=z*10
for t=2, t<f, t=t+1
do
b=b*a
c=(1/t)*b
d=d+c
endfor
return d}
}{fn PCCVRisk() {
local insum,e,w,r,q,s,t,MCV,PCRisk,SMOKSTATUS,DIABSTATUS
cond
case (PATIENT_AGE())<21
return "Patient too young to calculate risk" break
case (PATIENT_AGE())>79
return "Patient too old to calculate risk" break
case (OBSANY("CHOLESTEROL"))==""
return "NO Total Cholesterol value in flowsheet" break
case (OBSANY("HDL"))==""
return "No HDL value in Flowsheet" break
case (OBSANY("BP SYSTOLIC"))==""
return "No Systolic BP value in Flowsheet" break
case (PATIENT_AGE())>20
lAGE=ln(PATIENT_AGE())
lCHOL=ln(OBSANY("CHOLESTEROL"))
lHDL=ln(OBSANY("HDL"))
lBPS=ln(OBSANY("BP SYSTOLIC"))
If match(Toupper(OBSANY("SMOK STATUS")),"CURRENT")>0 THEN SMOKSTATUS=1 ELSE SMOKSTATUS=0 ENDIF
If ccc_sys_hasdx("ICD-250.^ICD-249.") then DIABSTATUS=1 else DIABSTATUS=0 ENDIF
If PATIENT.SEX=="M" then
If MATCH(PATIENT.RACE,"Black")> 0 then MCV=19.54 s=0.8954 //BLACK MALE
If (ccc_sys_hasdx("ICD-401"))=="FALSE" then
insum=2.469*lAGE+0.302*lCHOL+(-0.307)*lHDL+1.809*lBPS+0.549*SMOKSTATUS+0.645*DIABSTATUS
Else
insum=2.469*lAGE+0.302*lCHOL+(-0.307)*lHDL+1.916*lBPS+0.549*SMOKSTATUS+0.645*DIABSTATUS
Endif
Else
MCV=61.18 s=0.9144 //WHITE MALE
If (ccc_sys_hasdx("ICD-401"))=="FALSE" then
insum=12.344*lAGE+11.853*lCHOL+(-2.664)*(lAGE*lCHOL)+(-7.99)*lHDL+1.769*(lAGE*lHDL)+1.764*lBPS+7.837*SMOKSTATUS+(-1.795)*lAGE*SMOKSTATUS+0.658*DIABSTATUS
Else
insum=12.344*lAGE+11.853*lCHOL+(-2.664)*(lAGE*lCHOL)+(-7.99)*lHDL+1.769*(lAGE*lHDL)+1.797*lBPS+7.837*SMOKSTATUS+(-1.795)*lAGE*SMOKSTATUS+0.658*DIABSTATUS
Endif
Endif
Else //FEMALE
If MATCH(PATIENT.RACE,"Black")> 0 then MCV= 86.61 s=0.9533 //BLACK FEMALE
If (ccc_sys_hasdx("ICD-401"))=="FALSE" then
insum=17.114*lAGE+0.94*lCHOL+(-18.92)*lHDL+4.475*(lAGE*lHDL)+27.820*lBPS+(-6.087)*(lAGE*lBPS)+0.691*SMOKSTATUS+0.874*DIABSTATUS
Else
insum=17.114*lAGE+0.94*lCHOL+(-18.92)*lHDL+4.475*(lAGE*lHDL)+29.291*lBPS+(-6.432)*(lAGE*lBPS)+0.691*SMOKSTATUS+0.874*DIABSTATUS
Endif
Else MCV=(-29.18) s=0.9665 //WHITE FEMALE
If (ccc_sys_hasdx("ICD-401"))=="FALSE" then
insum=(-29.799)*lAGE+4.884*(lAGE*lAGE)+13.540*lCHOL+(-3.114)*(lAGE*lCHOL)+(-13.578)*lHDL+3.149*(lAGE*lHDL)+1.957*lBPS+7.574*SMOKSTATUS+(-1.665)*lAGE*SMOKSTATUS+0.661*DIABSTATUS
Else
insum=(-29.799)*lAGE+4.884*(lAGE*lAGE)+13.540*lCHOL+(-3.114)*(lAGE*lCHOL)+(-13.578)*lHDL+3.149*(lAGE*lHDL)+2.019*lBPS+7.574*SMOKSTATUS+(-1.665)*lAGE*SMOKSTATUS+0.661*DIABSTATUS
Endif
Endif
Endif
e=2.71828
w=insum-MCV
r=e^w
q=s^r
PCRisk=(1-q)*100
OBSNOW("PCCVR10YRRSK",str(PCRisk))
t= "Based on: Total Cholesterol = " + (OBSANY("CHOLESTEROL")) + " and HDL= " + (OBSANY("HDL")) + " and Systolic BP = " + (OBSANY("BP SYSTOLIC")) + fmt( "
10-Year Pooled Cohort Cardiovascular Risk = ","B") + PCRisk +"%"
return t
endcond}
}{fn sys_filter_all(GCIlist) {
// Returns list of Name and date of current meds in allergy list
// that match GPI fragments in list.
// Returns null otherwise.
local meds=getfield(ALL_AFTER("DELIMITED"),"|","")
local types=getfield(GCIlist,"^","")
local r="", i=0, j=0
for j=1, j<=size(meds), j=j+1 do
for i=1, i<=size(types), i=i+1 do
if match(CCC_sys_dp(meds[j],6),types[i])==1
then if r<>"" then r=r+hret endif
r=r+CCC_sys_dp(meds[j],1)+" ("+CCC_sys_dp(meds[j],2)+")" break
else continue
endif
endfor
endfor
return r }
}
Cardiovascular Risk Analysis
{ if ccc_sys_hasdx("ICD-410^ICD-411^ICD-413^ICD-414^ICD-V45.8^ICD-429.2^ICD-440^ICD-441^ICD-442^ICD-43^ICD-443.89^ICD-443.9^ICD-443.2^ICD-V15.1") then "A Cardiovascular Disease diagnosis is on the Problem List" else "
Major Cardiac Risk Factors:
" + CVRsk() endif + "
" + cond
case CCC_sys_filter_meds("3940")<>""
"Current Statin on Medlist:
" + CCC_sys_filter_meds("3940")+ "
"
case OBSANY("NOTONSTATIN")<>""
"Past history Statin declined or contraindicated
"
case sys_filter_all("3940")<> ""
"Allergy list includes a Statin
"
else
"No Statin on Med or Allergy List and No documented contraindication
"
endcond + (If OBSANY("CHD 10YR RSK")<>"","
Framingham 10 Year Cardiac Risk was " + OBSANY("CHD 10YR RSK") + " on " + LASTOBSDATE("CHD 10YR RSK") + "
", "") + "POOLED COHORT 10 Year Cardiac Risk is
" + PCCVRisk()
}
You did a brilliant job coding this. Thanks for sharing! One quick suggestion: The current version doesn't detect Vytorin as a statin but adding an additional case statement detects that combination: (the case CCC_sys_filter_meds("3999") is what I added.
You have the following Major Cardiac Risk Factors:
" + CVRsk() endif + "
" + cond
case CCC_sys_filter_meds("3940")""
"-Current Statin on Medlist:
" + CCC_sys_filter_meds("3940")+ "
"
case CCC_sys_filter_meds("3999")""
"-Current Statin on Medlist:
" + CCC_sys_filter_meds("3999")+ "
"
case OBSANY("NOTONSTATIN")""
"-Past history Statin declined or contraindicated"
case sys_filter_all("3940") ""
"-Allergy list includes a Statin"
Thank you,
Steve