Transition Matrices

Overview

The Transition Matrices approach to modeling credit quality computes a probabiliy of a loan transitioning from one loan grade to another.

Calculating Transition Counts

''' calculate the counts of number of records transitioning from one loan grade to the another ''' def transitions(records, counts=None): if counts == None:counts = {} group1 = group(records, lambda x:x['account']) for acct in group1: list1 = group1[acct] list1 = sorted(list1, key=lambda x: x['date']) for index, item in enumerate(list1): if index>0: if list1[index-1]['grade'] not in counts:counts[list1[index-1]['grade']]={} if item['grade'] not in counts[list1[index-1]['grade']]:counts[list1[index-1]['grade']][item['grade']] = 0 counts[list1[index-1]['grade']][item['grade']] += 1 pass pass pass return counts

Format as a Matrix

Typically, when doing calculations with the transition matrix approach, you will need to have your transition probabilities arranged as a matrix. This enables you to calculate probabilities for periods other than the data snapshot period.

''' takes a dictionary of counts as given by the transitions function and returns a list of lists, formatted in matrix format ''' def to_matrix(counts, grades): matrix = [] for grade in grades: row = [0 for x in grades] matrix.append(row) count = counts[grade] total = sum(list(count.values())) for index2,grade2 in enumerate(grades): if grade2 in count: row[index2] = count[grade2]/total pass pass return matrix

Full Script

The following is the full code listing.

''' creates a dictionary of records from the inputted list keyed by the result of applying a function to each item in the list ''' def group(list1, method): ans = {} for item in list1: result = method(item) if not isinstance(result, list): result = [result] current= ans for index, item2 in enumerate(result): if index == len(result)-1: if item2 not in current: current[item2] = [] current[item2].append(item) pass else: if item2 not in current: current[item2] = {} current = current[item2] pass pass pass return ans ''' calculates counts based on a set of records that are assumed to be sorted by account and date this is for computing transitions for datasets that cannot be fit in memory, but can be assumed to be sorted as noted ''' def transitions_online(records, counts=None): if counts == None:counts = {} last = None for item in records: if last != None and last['account'] == item['account']: if last['grade'] not in counts: counts[last['grade']] = {} if item['grade'] not in counts[last['grade']] : counts[last['grade']][item['grade']] = 0 counts[last['grade']][item['grade']] += 1 pass last = item pass return counts ''' calculate the counts of number of records transitioning from one loan grade to the another ''' def transitions(records, counts=None): if counts == None:counts = {} group1 = group(records, lambda x:x['account']) for acct in group1: list1 = group1[acct] list1 = sorted(list1, key=lambda x: x['date']) for index, item in enumerate(list1): if index>0: if list1[index-1]['grade'] not in counts:counts[list1[index-1]['grade']]={} if item['grade'] not in counts[list1[index-1]['grade']]:counts[list1[index-1]['grade']][item['grade']] = 0 counts[list1[index-1]['grade']][item['grade']] += 1 pass pass pass return counts def probabilities(counts, grades): result = {} for grade in grades: result[grade] = {} count = counts[grade] total = sum(list(count.values())) for grade2 in grades: if grade2 in count: result[grade][grade2] = count[grade2]/total pass pass return result ''' takes a dictionary of counts as given by the transitions function and returns a list of lists, formatted in matrix format ''' def to_matrix(counts, grades): matrix = [] for grade in grades: row = [0 for x in grades] matrix.append(row) count = counts[grade] total = sum(list(count.values())) for index2,grade2 in enumerate(grades): if grade2 in count: row[index2] = count[grade2]/total pass pass return matrix