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