Comorbidity measures

To calculate the comorbidities in my upcoming article in the Bone and Joint Journal I have used the python plugin for IBM SPSS. The code is both validated manually on a subset of patients and tested by the codes defined in Dr. Hude Quan’s code.

In order to speed up the code I have a simple ICD-10 detector, if you’re not working in Sweden then leave the year parameter set to False:

def IsIcd10Code(icd, year = False):
    # If year was specified then Sweden changed to ICD 10 
    # in 1997 but since not everyone change at the same time
    # 1996 to 1999 are considered safer to perform a 
    # regex format check as belov 
    if (year): 
        if (year < 1996):
            return False
        elif(year > 1999):
            return True
    
    # No true ICD 10 codes have a V in them except for transportation
    # accidents and those don't count although ICD 9 has a few odd V
    # codes in it.
    if (re.match("[A-Z][0-9]+", icd, re.IGNORECASE)
        and re.match("[^V][0-9]+", icd, re.IGNORECASE)):
        return True
    else:
        return False

Now the code to identify a Charlson ICD-code is below. There are two versions of the Charlson codes, one is the Sundarajan et al’s 2004 version and the second one is Quan et al’s 2005 version:

def FindCharlsonsMatch(icdCode, icd_ver = False, version = 2, print_debug = False):
    """Checks if the icd code exists in any of the expressions and
    returns a string with the code for the charlsons"""

    # Create the charlsons regular expressions
    # to compare 2
    # v1 is based on Sundarajan et al 2004
    charlsons_v1 = {}
    charlsons_v1['MI'] = {'icd9': ['41[02]'],
        'icd10': ['I2([12]|52)']}

    charlsons_v1['CHF'] = {'icd9': ['428'],
        'icd10': ['I50']}

    charlsons_v1['PVD'] = {'icd9': ['44(1|39)', '7854', 'V434'],
        'icd10': ['I7(1|90|39)', 'R02', 'Z95[89]']}

    charlsons_v1['CEVD'] = {'icd9': ['43[012345678]'],
        'icd10': ['I6([01234569]|7[012456789]|8[128])', 'G45[0124689]', 'G46']}

    charlsons_v1['DEM'] = {'icd9': ['290'], 'icd10': ['F0([012]|51)']}

    charlsons_v1['COPD'] = {'icd9': ['49[0123456]', '50[012345]'],
        'icd10': ['J4[01234567]', 'J6[01234567]']}

    charlsons_v1['Rheum'] = {'icd9': ['710[014]', '714[012]', '71481', '5171', '725'],
        'icd10': ['M3([24]|32|53)', 'M0(5[012389]|6[039])']}

    charlsons_v1['PUD'] = {'icd9': ['53[1234]'],
        'icd10': ['K2[5678]']}

    charlsons_v1['MILDLD'] = {'icd9': ['571[2456]'],
        'icd10': ['K7(0[23]|17|3|4[023456])']}

    charlsons_v1['DIAB_UC'] = {'icd9': ['250[01237]'],
        'icd10': ['E1[0134][159]']}

    charlsons_v1['DIAB_C'] = {'icd9': ['250[456]'],
        'icd10': ['E1[0134][234]']}

    charlsons_v1['PARA'] = {'icd9': ['342', '3441'],
        'icd10': ['G041', 'G8(1|2[012])']}

    charlsons_v1['RD'] = {'icd9': ['58([2568]|3[01234567])'],
        'icd10': ['N0([13]|5[23456]|7[234])', 'N1[89]', 'N25']}

    charlsons_v1['CANCER'] = {'icd9': ['1([4568]|7[0124569]|9[0123459])', '20[012345678]'],
        'icd10': ['C[012356]', 'C4[01356789]', 'C7[0123456]', 'C8([12345]|8[379])', 'C9(0[01]|[12356]|4([01237]|51))']}

    charlsons_v1['METS'] = {'icd9': ['19([678]|9[01])'],
        'icd10': ['C7[789]', 'C80']}

    charlsons_v1['MSLD'] = {'icd9': ['572[2348]'],
        'icd10': ['K7(04|2[19]|6[67])']}

    charlsons_v1['HIV'] = {'icd9': ['04[234]'],
        'icd10': ['B2[01234]']}

    # Based on Quan et al 2005
    charlsons_v2 = {}
    charlsons_v2['MI'] = {'icd10': ['I2([12]|52)'],
    'icd9': ['41[02]']}

    charlsons_v2['CHF'] = {'icd10': ['I099', 'I1(10|3[02])', 'I255', 'I4(2[056789]|3)', 'I50', 'P290'],
    'icd9': ['39891', '402(01|11|91)', '404(01|03|[19][13])', '42(5[456789]|8)']}

    charlsons_v2['PVD'] = {'icd10': ['I7([01]|3[189]|71|9[02])', 'K55[189]', 'Z95[89]'],
    'icd9': ['0930', '4373', '44([01]|3[123456789]|71)', '557[19]', 'V434']}

    charlsons_v2['CEVD'] = {'icd10': ['G4[56]', 'H340', 'I6'],
    'icd9': ['36234', '43[012345678]']}

    charlsons_v2['DEM'] = {'icd10': ['F0([0123]|51)', 'G3(0|11)'],
    'icd9': ['29(0|41)', '3312']}

    charlsons_v2['COPD'] = {'icd10': ['I27[89]', 'J4[01234567]', 'J6([01234567]|84)', 'J70[13]'],
    'icd9': ['416[89]', '49', '50([012345]|64|8[18])']}

    charlsons_v2['Rheum'] = {'icd10': ['M0[56]', 'M3(15|[234]|5[13]|60)'],
    'icd9': ['4465', '71(0[01234]|4[0128])', '725']}

    charlsons_v2['PUD'] = {'icd10': ['K2[5678]'],
    'icd9': ['53[1234]']}

    charlsons_v2['MILDLD'] = {'icd10': ['B18', 'K7(0[01239]|1[3457]|[34]|6[023489])', 'Z944'],
    'icd9': ['070([23][23]|[45]4|[69])', '57([01]|3[3489])', 'V427']}

    charlsons_v2['DIAB_UC'] = {'icd10': ['E1[01234][01689]'],
    'icd9': ['250[012389]']}

    charlsons_v2['DIAB_C'] = {'icd10': ['E1[01234][23457]'],
    'icd9': ['250[4567]']}

    charlsons_v2['PARA'] = {'icd10': ['G041', 'G114', 'G8(0[12]|[12]|3[012349])'],
    'icd9': ['3341', '34([23]|4[01234569])']}

    charlsons_v2['RD'] = {'icd10': ['I120', 'I131', 'N0(3[234567]|5[234567])', 'N1[89]', 'N250', 'Z49[012]', 'Z940', 'Z992'],
    'icd9': ['403[019]1', '404[019][23]', '58(2|3[01234567]|[56]|80)', 'V4(20|51)', 'V56']}

    charlsons_v2['CANCER'] = {'icd10': ['C[01]', 'C2[0123456]', 'C3[01234789]', 'C4[01356789]', 'C5[012345678]', 'C6', 'C7[0123456]', 'C8[123458]', 'C9[01234567]'],
    'icd9': ['1[456]', '17[012456789]', '18', '19[012345]', '20[012345678]', '2386']}

    charlsons_v2['MSLD'] = {'icd10': ['I8(5[09]|64)', 'I982', 'K7(04|[12]1|29|6[567])'],
    'icd9': ['456[012]', '572[2345678]']}

    charlsons_v2['METS'] = {'icd10': ['C7[789]', 'C80'],
    'icd9': [ '19[6789]']}

    charlsons_v2['HIV'] = {'icd10': ['B2[0124]'],
    'icd9': [ '04[234]']}

    charlson_4_comparing = charlsons_v2
    if (version == 1):
        charlson_4_comparing = charlsons_v1

    # Speeds up only to check the codes that are possible
    if (icd_ver == False):
        code_is_icd10 = IsIcd10Code(icdCode)
    else:
        code_is_icd10 = icd_ver == 10

    matches = {}
    for key_disease, reg_expr in charlson_4_comparing.iteritems():
        if (code_is_icd10 == False and matches.has_key(key_disease) == False):
            for expr in reg_expr['icd9']:
                if (re.match(expr, icdCode, re.IGNORECASE)):
                    if (print_debug):
                        print "The code {0} has an icd-9 match based on this Charlsons expression = '{1}'".format(icdCode, expr)
                    matches[key_disease] = 1
                    break
        if (code_is_icd10 == True and matches.has_key(key_disease) == False):
            for expr in reg_expr['icd10']:
                if (re.match(expr, icdCode, re.IGNORECASE)):
                    if (print_debug):
                        print "The code {0} has an icd-10 match based on this Charlsons expression = '{1}'".format(icdCode, expr)
                    matches[key_disease] = 1
                    break

    return matches

The Royal Collegue of Surgeon’s Charlson score uses the script below:

def FindRoyalCharlsonsMatch(icdCode, prev_hosp_admission, icd_ver = False, only_icd10 = True, print_debug = False):
    """Checks if the icd code exists in any of the expressions and
    returns a string with the code for the charlsons
    
    Certain codes are only valid if previous hospital admission and therefore this needs 
    to have the prev_hosp_admission variable set to True/False depending if the icd code
    is generated from a previous hospital visit or not.
    """

    # Based on Armitage JN, van der Meulen JH. 
    # -- Identifying co-morbidity in surgical patients using 
    # --- administrative data with the Royal College of Surgeons Charlson Score. 
    # -- British Journal of Surgery. 2010 Maj 1;97(5):772-781. 
    
    rs_charlsons = {}
    rs_charlsons['MI'] = {'icd10': ['I2([123]|52)'],
    'icd9': ['41[02]']}

    rs_charlsons['CHF'] = {'icd10': ['I1[13]', 'I255', 'I4[23]', 'I50', 'I517'],
    'icd9': ['39891', '402(01|11|91)', '404(01|03|[19][13])', '42(5[456789]|8)']}

    rs_charlsons['PVD'] = {'icd10': ['I7[0123]', 'I77[01]', 'K55[189]', 'R02', 'Z95[89]'],
    'icd9': ['0930', '4373', '44([01]|3[123456789]|71)', '557[19]', 'V434']}

    rs_charlsons['CEVD'] = {'icd10': ['G4[56]', 'I6'],
    'icd9': ['36234', '43[012345678]']}

    rs_charlsons['DEM'] = {'icd10': ['A810','F0([0123]|51)', 'G3[01]'],
    'icd9': ['29(0|41)', '3312']}

    rs_charlsons['COPD'] = {'icd10': ['I26', 'I27', 'J4[01234567]', 'J6([01234567]|84)', 'J70[13]'],
    'icd9': ['416[89]', '49', '50([012345]|64|8[18])']}

    rs_charlsons['Rheum'] = {'icd10': ['M0[569]', 'M120', 'M3(15|[23456])'],
    'icd9': ['4465', '71(0[01234]|4[0128])', '725']}

    rs_charlsons['LD'] = {'icd10': ['B18', 'I85', 'I864', 'I982', 'K7([01]|2[19]|6)', 'R162', 'Z944'],
    'icd9': ['070([23][23]|[45]4|[69])', '456[012]', '572[2345678]', '57([01]|3[3489])', 'V427']}

    rs_charlsons['DIAB'] = {'icd10': ['E1[01234]'],
    'icd9': ['250']}

    rs_charlsons['PARA'] = {'icd10': ['G114', 'G8[123]'],
    'icd9': ['3341', '34([23]|4[01234569])']}

    rs_charlsons['RD'] = {'icd10': ['I1[23]', 'N0[13578]', 'N1(7[12]|[89])', 'N25', 'Z49', 'Z940', 'Z992'],
    'icd9': ['403[019]1', '404[019][23]', '58(2|3[01234567]|[56]|80)', 'V4(20|51)', 'V56']}

    rs_charlsons['CANCER'] = {'icd10': ['C[01]', 'C2[0123456]', 'C3[01234789]', 'C4[01356789]', 'C5[012345678]', 'C6', 'C7[0123456]', 'C8[0123458]', 'C9[01234567]'],
    'icd9': ['1[456]', '17[012456789]', '18', '19[012345]', '20[012345678]', '2386']}

    rs_charlsons['METS'] = {'icd10': ['C7[789]'],
    'icd9': [ '19[6789]']}

    rs_charlsons['HIV'] = {'icd10': ['B2[0124]'],
    'icd9': [ '04[234]']}
    
    regexp_4_idc10_acute_diagnos_codes = '(I2[123]|J46|N17[12]|N19)'
    
    # Used the translator from Socialstyrelsen
    regexp_4_idc9_acute_diagnos_codes = '(410|42(30|95|96|98)|4939|58[34][67]|5908|586|7919|5939)'

    # Speeds up only to check the codes that are possible
    if (icd_ver == False):
        code_is_icd10 = IsIcd10Code(icdCode)
    else:
        code_is_icd10 = icd_ver == 10

    matches = {}
    for key_disease, reg_expr in rs_charlsons.iteritems():
        # ICD 9 part
        if (code_is_icd10 == False and only_icd10 == False and matches.has_key(key_disease) == False):
            # Check that none of the acute diagnoses is checked
            if (prev_hosp_admission == True or re.match(regexp_4_idc9_acute_diagnos_codes, icdCode, re.IGNORECASE) == None):
                for expr in reg_expr['icd9']:
                    if (re.match(expr, icdCode, re.IGNORECASE)):
                        if (print_debug):
                            print "The code {0} has an icd-9 match based on this org. Charlsons expression = '{1}'".format(icdCode, expr)
                        matches[key_disease] = 1
                        break

        # ICD 10 part
        if (code_is_icd10 == True and matches.has_key(key_disease) == False):
            # Check that none of the acute diagnoses is set
            if (prev_hosp_admission == True or 
                re.match(regexp_4_idc10_acute_diagnos_codes, icdCode, re.IGNORECASE) == None):
                for expr in reg_expr['icd10']:
                    if (re.match(expr, icdCode, re.IGNORECASE)):
                        if (print_debug):
                            print "The code {0} has an icd-10 match based on this RS Charlsons expression = '{1}'".format(icdCode, expr)
                        matches[key_disease] = 1
                        break

    return matches

While the Elixhauser score has the below function:

def FindElixhausersMatch(icdCode, icd_ver = False, print_debug=False):
    """Checks if the icd code exists in any of the expressions and
    returns a string with the code for the elixhausers"""

    elixhausers = {}
    #Congestive heart failure
    elixhausers['CHF'] = {'icd-10': ['I099', 'I1(10|3[02])', 'I255', 'I4(2[056789]|3)', 'I50', 'P290'],
    #'icd-9': ['39891', '402(01|11|91)', '404(01|03|[19][13])', '42(5[456789]|8)']}
    # Changes due to the Swedish coding
    'icd-9': ['3980', '402', '4040', '42(5[456789]|8)']}

    # Cardiac arrhythmias
    elixhausers['Arrhy'] = {'icd-10': ['I44[123]', 'I456', 'I459',
    'I4[789]', 'R00[018]', 'T821',
    'Z[49]50'],
    #'icd-9':['426([079]|1[023])',
    #Swedish changes
    'icd-9':['426[079]',
    '427[01234]',
    '427[6789]', '7850',
    #'9960[14]',
    'V450', 'V533']}

    # Valvular disease
    elixhausers['VD'] = {'icd-10': ['A520', 'I0[5678]',
    'I09[18]', 'I3[456789]',
    'Q23[0123]', 'Z95[234]'],
    'icd-9':['0932', '39[4567]', '424',
    '746[3456]', 'V422', 'V433']}

    # Pulmonary circulation disorders
    elixhausers['PCD'] = {'icd-10': ['I2([67]|8[089])'],
    'icd-9':['415[01]', '416',
    '417[089]']}

    elixhausers['PVD'] = {'icd-10': ['I7([01]|3[189]|71|9[02])', 'K55[189]', 'Z95[89]'],
    'icd-9': ['0930', '4373', '44([01]|3[123456789])', '4471', '557[19]', 'V434']}

    # Hypertension, uncomplicated
    elixhausers['HPTN_UC'] = {'icd-10': ['I10'],
    'icd-9':['401']}

    # Hypertension, complicated
    elixhausers['HPTN_C'] = {'icd-10': ['I1[1235]'],
    'icd-9':['40[2345]']}

    # Paralysis
    elixhausers['Para'] = {'icd-10': ['G041', 'G114', 'G8(0[12]|[12]|3[012349])'],
    'icd-9': ['3341', '34([23]|4[01234569])']}

    # Other neurological disorders
    elixhausers['OthND'] = {'icd-10':['G1[0123]', 'G2[012]', 'G25[45]',
    'G31[289]',
    'G3[2567]',
    'G4[01]', 'G93[14]',
    'R470', 'R56'],
    'icd-9': ['3319', '332[01]',
    '333[45]',
    '33([45]|62)',
    '34([015]|8[13])',
    '78[04]3']}

    # Chronic pulmonary disease
    elixhausers['COPD'] = {'icd-10': ['I27[89]', 'J4[01234567]', 'J6([01234567]|84)', 'J70[13]'],
    'icd-9': ['416[89]', '49', '50([012345]|64|8[18])']}


    # Diabetes, uncomplicated
    # Slightly different from charlsons
    elixhausers['Diab_UC'] = {'icd-10': ['E1[01234][019]'],
    'icd-9': ['250[0123]']}

    # Diabetes, complicated
    # Slightly different from charlsons
    elixhausers['Diab_C'] = {'icd-10': ['E1[01234][2345678]'],
    'icd-9': ['250[456789]']}

    # Hypothyroidism
    elixhausers['Hptothy'] = {'icd-10': ['E0[0123]', 'E890'],
    'icd-9':['2409', '24([34]|6[18])']}

    # Renal failure
    # Differs from Charlsons
    elixhausers['RF'] = {'icd-10': ['I120', 'I131', 'N1[89]', 'N250', 'Z49[012]', 'Z940', 'Z992'],
    #'icd-9': ['403[019]1', '404[019][23]', '58([56]|80)', 'V4(20|51)', 'V56']}
    # Swe change
    'icd-9': ['403', '404', '58([56]|80)', 'V4(20|51)', 'V56']}


    # Liver disease
    elixhausers['LD'] = {'icd-10': ['B18', 'I8(5|64)', 'I982', 'K7(0|1[13457]|[234]|6[023456789])', 'Z944'],
    #'icd-9': ['070([23][23]|[45]4|[69])', '456[012]', '57([01]|2[2345678]|3[3489])', 'V427']}
    # Swe changes
    'icd-9': ['070([23]|[45])', '456[012]', '57([01]|2[2345678]|3[3489])', 'V427']}


    # Peptic ulcer disease excluding bleeding
    elixhausers['PUD_NB']  = {'icd-10': ['K2[5678][79]'],
    'icd-9': ['53[1234][79]']}


    # AIDS/HIV
    elixhausers['HIV'] = {'icd-10': ['B2[0124]'],
    'icd-9': ['04[234]']}

    # Lymphoma
    elixhausers['Lymp'] = {'icd-10': ['C8[123458]',
    'C96', 'C90[02]'],
    'icd-9':['20[012]', '2030', '2386']}

    # Metastatic cancer
    elixhausers['METS'] = {'icd-10': ['C7[789]', 'C80'],
    'icd-9': ['19[6789]']}

    # Solid tumor without metastasis
    elixhausers['Tumor'] = {'icd-10': ['C[01]', 'C2[0123456]',
    'C3[01234789]', 'C4[01356789]', 'C5[012345678]',
    'C6', 'C7[0123456]', 'C97'],
    'icd-9': ['1[456]', '17[012456789]', '18', '19([012345])']}


    # Rheumatoid arthritis/collagen vascular diseases
    elixhausers['Rheum_A'] = {'icd-10': ['L94[013]', 'M0[568]', 'M12[03]',
    'M3(0|1[0123]|[2345])', 'M4(5|6[189])'],
    #'icd-9': ['446', '7010', '71(0[0123489]|12|4|93)', '72([05]|8(5|89)|930)']}
    'icd-9': ['446', '7010', '71(0[0123489]|12|4|93)', '72([05]|8[58]|93)']}

    # Coagulopathy
    elixhausers['Coag'] = {'icd-10': ['D6[5678]',
    'D69[13456]'],
    'icd-9':['286', '2871', '287[345]']}

    # Obesity
    elixhausers['Obesity'] = {'icd-10': ['E66'],
    'icd-9':['2780']}

    # Weight loss
    elixhausers['WL'] = {'icd-10': ['E4[0123456]', 'R634', 'R64'],
    'icd-9':['26[0123]', '7832', '7994']}

    # Fluid and electrolyte disorders
    elixhausers['Fluid'] = {'icd-10': ['E222', 'E8[67]'],
    'icd-9':['2536', '276']}

    # Blood loss anemia
    elixhausers['BLA'] = {'icd-10': ['D500'],
    'icd-9':['2800']}

    # Deficiency anemia
    elixhausers['DA'] = {'icd-10': ['D50[89]', 'D5[123]'],
    'icd-9':['280[123456789]', '281']}

    # Alcohol abuse
    elixhausers['Alcohol'] = {'icd-10': ['F10', 'E52', 'G621', 'I426',
    'K292', 'K70[039]',
    'T51', 'Z502',
    'Z714', 'Z721'],
    'icd-9':['2652', '291[12356789]',
    '303[09]', '3050', '3575',
    '4255', '5353', '571[0123]', '980', 'V113']}

    # Drug abuse
    elixhausers['Drug'] = {'icd-10': ['F1[12345689]',
    'Z715', 'Z722'],
    'icd-9':['292', '304', '305[23456789]', 'V6542']}
    
    # Psychoses
    elixhausers['Psycho'] = {'icd-10': ['F2[0234589]',
    'F3([01]2|15)'],
    'icd-9':['2938', 
    #'296[0145]4',
    # Changes in Swedish version
     '296[08]',
    '29[578]']}
    
    
    # Depression
    elixhausers['Dep'] = {'icd-10': ['F204',
    'F31[345]', 'F3[23]',
    'F341', 'F4[13]2'],
    'icd-9':[
    #'296[235]',
    # Changes in Swedish version
    '296[135]',
    '3004', '309', '311']}

    # Speeds up only to check the codes that are possible
    if (icd_ver == False):
        code_is_icd10 = IsIcd10Code(icdCode)
    else:
        code_is_icd10 = icd_ver == 10

    matches = {}
    for key_disease, reg_expr in elixhausers.iteritems():
        try:
            if (code_is_icd10 == False and matches.has_key(key_disease) == False):
                for expr in reg_expr['icd-9']:
                    if (re.match(expr, icdCode, re.IGNORECASE)):
                        if (print_debug):
                            print "The code {0} has an icd-9 match based on this Elixhausers expression = '{1}'".format(icdCode, expr)
                        matches[key_disease] = 1
                        break
            if (code_is_icd10 == True and matches.has_key(key_disease) == False):
                for expr in reg_expr['icd-10']:
                    if (re.match(expr, icdCode, re.IGNORECASE)):
                        if (print_debug):
                            print "The code {0} has an icd-10 match based on this Elixhausers expression = '{1}'".format(icdCode, expr)
                        matches[key_disease] = 1
                        break
        except Exception, e:
            print "\n******"
            print "Exception for regexp {0}".format(expr)
            print "Exception: ", e

    return matches

The intention with the debug parameter is simply to give the output of the matches codes. The function return dictionaries ({}) where the key is a code abbreviation for the particular score and the set value is 1. As a code can sometimes belong to two categories I chose to use the dictionary instead of just returning a string. Initially my plan was to include the weights, i.e. the 1 value, but later on I separated this into different functions. Below are the weighting functions. The RCS Charlsons doesn’t have any weights, just like the Elixhausers and therefore I’ve not added that one.

def GetCharlsonWeight(charlson_code):
    """ Looks for a code and returns that weight for
    that charlsons code
    """
    charlsons_weight = {}
    charlsons_weight['MI'] = 1
    charlsons_weight['CHF'] = 1
    charlsons_weight['PVD'] = 1
    charlsons_weight['CEVD'] = 1
    charlsons_weight['DEM'] = 1
    charlsons_weight['COPD'] = 1
    charlsons_weight['Rheum'] = 1
    charlsons_weight['PUD'] = 1
    charlsons_weight['MILDLD'] = 1
    charlsons_weight['DIAB_UC'] = 1
    charlsons_weight['DIAB_C'] = 2
    charlsons_weight['PARA'] = 2
    charlsons_weight['RD'] = 2
    charlsons_weight['CANCER'] = 2
    charlsons_weight['METS'] = 6
    charlsons_weight['MSLD'] = 3
    charlsons_weight['HIV'] = 6

    try:
        return charlsons_weight[charlson_code]
    except:
        raise ValueError("The charlson code group = '{0}' was not found in weight list".format(charlson_code))

def GetElixhausersWeight(elixhauser_code):
    """All have value 1, this is just because
    Charlsons has one, gives a better symmetry"""
    return 1

Once you have looped through a patient’s all ICD-codes you want to check the hierarchy, i.e. malignancy = cancer so the score should only be counted as one. Note: this is undefined for the Elixhauser’s as it has no natural weighting although van Walraven et al’s weights can be applied.

def Adjust4CharlsonHierarchy(icdCodes):
    """
    Since metastases indicate - cancer, complicated diabetes - diabetes,
    moderate to severe liver disease - mild liver disease these
    variable should be cleared so that the subject doesn't get points for both.
    In other words the more complicated disease is the one that counts
    """
    if (len(icdCodes) == 0):
        return icdCodes

    if (icdCodes.has_key('METS') and icdCodes.has_key('CANCER')):
        if (icdCodes['METS'] > 0):
            del(icdCodes['CANCER'])

    if (icdCodes.has_key('MSLD') and icdCodes.has_key('MILDLD')):
        if (icdCodes['MSLD'] > 0):
            del(icdCodes['MILDLD'])

    if (icdCodes.has_key('DIAB_C') and icdCodes.has_key('DIAB_UC')):
        if (icdCodes['DIAB_C'] > 0):
            del(icdCodes['DIAB_UC'])

    return icdCodes

def Adjust4RCSharlsonHierarchy(icdCodes):
    """
    Since metastases indicate - cancer, complicated diabetes - diabetes,
    moderate to severe liver disease - mild liver disease these
    variable should be cleared so that the subject doesn't get points for both.
    In other words the more complicated disease is the one that counts
    """
    if (len(icdCodes) == 0):
        return icdCodes

    if (icdCodes.has_key('METS') and icdCodes.has_key('CANCER')):
        if (icdCodes['METS'] > 0):
            del(icdCodes['CANCER'])

    return icdCodes

def Adjust4ElixhausersHierarchy(icdCodes):
    """
    Since metastases indicate - cancer, complicated diabetes - diabetes,
    complicated hypertension - hypertension these
    variable should be cleared so that the subject doesn't get points for both.
    In other words the more complicated disease is the one that counts
    """
    if (len(icdCodes) == 0):
        return icdCodes

    if (icdCodes.has_key('METS') and icdCodes.has_key('Tumor')):
        if (icdCodes['METS'] > 0):
            del(icdCodes['Tumor'])

    if (icdCodes.has_key('HPTN_C') and icdCodes.has_key('HPTN_UC')):
        if (icdCodes['HPTN_C'] > 0):
            del(icdCodes['HPTN_UC'])

    if (icdCodes.has_key('Diab_C') and icdCodes.has_key('Diab_UC')):
        if (icdCodes['Diab_C'] > 0):
            del(icdCodes['Diab_UC'])

    # Blood loss anemia and deficiency anemia
    # are regarded as uncorrelated

    return icdCodes

For generating the variable labels in SPSS I have a few helper functions. These return tuples containing dictionaries that have the name & label keys, where name is the key from the find-function and the label is the label for the SPSS label.

def GetCharlsonVdef(prefix = 'Charlsons_', charlson_score_var_name = 'CCI'):
    """ Returns tuples with the charlson variables
    ready to create the variables form Charlson
    comorbidity index through using the spssdata append(vdef())
    """
    charlson_variables = []
    charlson_variables.append({'name': 'MI', 'label': 'Ch Myocardial Infarction'})
    charlson_variables.append({'name': 'CHF', 'label': 'Ch Congestive Heart Failure'})
    charlson_variables.append({'name': 'PVD', 'label': 'Ch Periphral Vascular Disease'})
    charlson_variables.append({'name': 'CEVD', 'label': 'Ch Cerebrovascular Disease'})
    charlson_variables.append({'name': 'DEM', 'label': 'Ch Dementia'})
    charlson_variables.append({'name': 'COPD', 'label': 'Ch Chronic Pulmonary Disease'})
    charlson_variables.append({'name': 'Rheum', 'label': 'Ch Rheumatic Disease'})
    charlson_variables.append({'name': 'PUD', 'label': 'Ch Peptic Ulcer Disease'})
    charlson_variables.append({'name': 'MILDLD', 'label': 'Ch Mild Liver Disease'})
    charlson_variables.append({'name': 'DIAB_UC', 'label': 'Ch Diabetes without complications'})
    charlson_variables.append({'name': 'DIAB_C', 'label': 'Ch Diabetes with complications'})
    charlson_variables.append({'name': 'PARA', 'label': 'Ch Paraplegia and Hemiplegia'})
    charlson_variables.append({'name': 'RD', 'label': 'Ch Renal Disease'})
    charlson_variables.append({'name': 'CANCER', 'label': 'Ch Cancer'})
    charlson_variables.append({'name': 'METS', 'label': 'Ch Metastatic Carcinoma'})
    charlson_variables.append({'name': 'MSLD', 'label': 'Ch Moderate or Severe Liver Disease'})
    charlson_variables.append({'name': 'HIV', 'label': 'Ch AIDS/HIV'})

    return_tuple = []
    return_tuple.append(spssdata.vdef(charlson_score_var_name, vlabel='Charlsons comorbidity score', vfmt=['F', 2, 0]))
    for v in charlson_variables:
        label = 'Charlsons: ' + v['label']
        variable_name = prefix + v['name']
        return_tuple.append(spssdata.vdef(variable_name, vlabel=label, vfmt=['F', 2, 0]))
    return return_tuple


def GetRCSharlsonVdef(prefix = 'RCSh_', charlson_score_var_name = 'RS_CCI'):
    """ Returns tuples with the royal society charlson variables
    ready to create the variables form Charlson
    comorbidity index through using the spssdata append(vdef())
    """
    charlson_variables = []
    charlson_variables.append({'name': 'MI', 'label': 'RCS Myocardial Infarction'})
    charlson_variables.append({'name': 'CHF', 'label': 'RCS Congestive Heart Failure'})
    charlson_variables.append({'name': 'PVD', 'label': 'RCS Periphral Vascular Disease'})
    charlson_variables.append({'name': 'CEVD', 'label': 'RCS Cerebrovascular Disease'})
    charlson_variables.append({'name': 'DEM', 'label': 'RCS Dementia'})
    charlson_variables.append({'name': 'COPD', 'label': 'RCS Chronic Pulmonary Disease'})
    charlson_variables.append({'name': 'Rheum', 'label': 'RCS Rheumatic Disease'})
    charlson_variables.append({'name': 'LD', 'label': 'RCS Liver Disease'})
    charlson_variables.append({'name': 'DIAB', 'label': 'RCS Diabetes'})
    charlson_variables.append({'name': 'PARA', 'label': 'RCS Paraplegia and Hemiplegia'})
    charlson_variables.append({'name': 'RD', 'label': 'RCS Renal Disease'})
    charlson_variables.append({'name': 'CANCER', 'label': 'RCS Cancer'})
    charlson_variables.append({'name': 'METS', 'label': 'RCS Metastatic Carcinoma'})
    charlson_variables.append({'name': 'HIV', 'label': 'RCS AIDS/HIV'})

    return_tuple = []
    return_tuple.append(spssdata.vdef(charlson_score_var_name, vlabel='RS Charlsons comorbidity score', vfmt=['F', 2, 0]))
    for v in charlson_variables:
        label = 'Charlsons: ' + v['label']
        variable_name = prefix + v['name']
        return_tuple.append(spssdata.vdef(variable_name, vlabel=label, vfmt=['F', 2, 0]))
    return return_tuple

def GetElixhausersVdef(prefix = 'Elix_', elixhausers_score_var_name = 'ElxCI'):
    """ Returns tuples with the charlson variables
    ready to create the variables form Charlson
    comorbidity index through using the spssdata append(vdef())
    """
    elixhausers_variables = []
    elixhausers_variables.append({'name': 'CHF', 'label': 'Elx Congestive Heart Failure'})
    elixhausers_variables.append({'name': 'Arrhy', 'label': 'Elx Caridiac Arrhythmia'})
    elixhausers_variables.append({'name': 'VD', 'label': 'Elx Valvular Disease'})
    elixhausers_variables.append({'name': 'PCD', 'label': 'Elx Pulmonary Circulation Disorders'})
    elixhausers_variables.append({'name': 'PVD', 'label': 'Elx Peripheral Vascular Disorders'})
    elixhausers_variables.append({'name': 'HPTN_UC', 'label': 'Elx Hypertension Uncomlicated'})
    elixhausers_variables.append({'name': 'HPTN_C', 'label': 'Elx Hypertension comlicated'})
    elixhausers_variables.append({'name': 'Para', 'label': 'Elx Paralysis'})
    elixhausers_variables.append({'name': 'OthND', 'label': 'Elx Other Neurological Disorders'})
    elixhausers_variables.append({'name': 'COPD', 'label': 'Elx Chronic Pulmonary Disease'})
    elixhausers_variables.append({'name': 'Diab_UC', 'label': 'Elx Diabetes Uncomplicated'})
    elixhausers_variables.append({'name': 'Diab_C', 'label': 'Elx Diabetes Complicated'})
    elixhausers_variables.append({'name': 'Hptothy', 'label': 'Elx Hypothyroidism'})
    elixhausers_variables.append({'name': 'RF', 'label': 'Elx Renal Failure'})
    elixhausers_variables.append({'name': 'LD', 'label': 'Elx Liver Disease'})
    elixhausers_variables.append({'name': 'PUD_NB', 'label': 'Elx Peptic Ulcer Disease excluding bleeding'})
    elixhausers_variables.append({'name': 'HIV', 'label': 'Elx AIDS/HIV'})
    elixhausers_variables.append({'name': 'Lymp', 'label': 'Elx Lymphoma'})
    elixhausers_variables.append({'name': 'METS', 'label': 'Elx Metastatic Cancer'})
    elixhausers_variables.append({'name': 'Tumor', 'label': 'Elx Solid Tumor without Metastasis'})
    elixhausers_variables.append({'name': 'Rheum_A', 'label': 'Elx Rheumatoid Arthsitis/collagen'})
    elixhausers_variables.append({'name': 'Coag', 'label': 'Elx Coagulopathy'})
    elixhausers_variables.append({'name': 'Obesity', 'label': 'Elx Obesity'})
    elixhausers_variables.append({'name': 'WL', 'label': 'Elx Weight Loss'})
    elixhausers_variables.append({'name': 'Fluid', 'label': 'Elx Fluid and Ecletrolyte Disorders'})
    elixhausers_variables.append({'name': 'BLA', 'label': 'Elx Blood Loss Anemia'})
    elixhausers_variables.append({'name': 'DA', 'label': 'Elx Deficiency Anemia'})
    elixhausers_variables.append({'name': 'Alcohol', 'label': 'Elx Alcohol Abuse'})
    elixhausers_variables.append({'name': 'Drug', 'label': 'Elx Drug Abuse'})
    elixhausers_variables.append({'name': 'Psycho', 'label': 'Elx Psychoses'})
    elixhausers_variables.append({'name': 'Dep', 'label': 'Elx Depression'})

    return_tuple = []
    return_tuple.append(spssdata.vdef(elixhausers_score_var_name, vlabel='Elixhausers comorbidity score', vfmt=['F', 2, 0]))
    for v in elixhausers_variables:
        label = 'Elixhausers: ' + v['label'].strip()
        variable_name = prefix + v['name']
        return_tuple.append(spssdata.vdef(variable_name, vlabel=label, vfmt=['F', 2, 0]))
    return return_tuple

In case you are working with a Swedish dataset I have this translation function for Swedish ICD-9 codes where a letter takes on the fourth position:

def TranslateFromSwedishIcd9(icd):
    """
    Swedish ICD 9 has a letter as 4:th variable and therefore this
    variable needs to be replaced with the true numeric value
    """
    if (re.match("[EV0-9][0-9]{2}[ABCDEFGHXW]", icd)):
        letter = icd[3:4].upper()
        translate_dict = {
            "A": "0",
            "B": "1",
            "C": "2",
            "D": "3",
            "E": "4",
            "F": "5",
            "G": "6",
            "H": "7",
            "W": "8",
            "X": "9"
         }

        icd_list = list(icd)
        icd_list[3] = translate_dict[letter]
        return "".join(icd_list)
    else:
        return icd

As these are just the basic functions for the scores I’ve also written a post about how to glue it all together.

Flattr this!

4 Responses to Comorbidity measures

  1. JN Gaetano says:

    I am a physician trying to implement comorbidity software using SPSS, and a beginner to using R and Python plug-ins. I consider myself otherwise proficient in basic SPSS. Having problems with knowing “where” to put the function code and “wx library needs to be installed and loaded, se the collapsed code block below.” I guess I just dont know how to build a format library?? and how to implement it? Any help would be much appreciated.

    • Max Gordon says:

      I would actually advise against using SPSS’ Python plug-in as it currently has a serious bug (at least when I reported the bug in May). I don’t think my script will work adequately.

      I have recently developed a new package that builds upon the SPSS code and that is validated on the same principles, you can find it on Github here. Just use the devtools package to install it. I’ll update this page soon to match that package, I just haven’t had time.

      • JN Gaetano says:

        Thank you for your reply. I’ve successfully installed your icd10.comorbidities package via devtools and am ready to use it with my dataset (the HCUP Nationwide Inpatient Sample, released by the AHRQ).

        I’m now stuck at entering the appropriate arguments for the cmrbdt.calc function. The ‘ds’ argument would be my active dataset in SPSS, but not sure how to point ‘ds’ there. My id_column would be a variable “KEY_NIS”, while my icd_columns are “DX1” to “DX25”

        Any hints to get me going? Apologize for my R ignorance, any help greatly appreciated…

        JNG

        • JN Gaetano says:

          Nevermind…figured it out…not sure if this is the best way, but works:

          x < - spssdata.GetDataFromSPSS(variables="DX1 to DX25")
          x <- as.data.frame(x) 
          y <- cmrbdt.calc(x, cmrbdt.finder_fn=cmrbdt.finder.regex.elixhauser_Quan2005, cmrbdt.finder_hierarchy_fn=hierarchy.elixhauser_Quan2005, cmrbdt.weight_fn=weight.Elixhausers.VanWalraven2009)
          
          dict <- spssdictionary.GetDictionaryFromSPSS() 
          casedata <- spssdata.GetDataFromSPSS() 
          varSpec <- c('ElixScore','Elixhauser Score',0,'F8','scale') 
          dict <- data.frame(dict,varSpec) 
          spssdictionary.SetDictionaryToSPSS('results',dict) 
          casedata <- data.frame(casedata,y$score) 
          spssdata.SetDataToSPSS('results',casedata) 
          spssdictionary.EndDataStep()
          

Leave a Reply