blocks-transformer/post_processing.py
Admin User fe9e388f36
All checks were successful
Build and Push Docker Image / test (push) Successful in 2m6s
Build and Push Docker Image / build_and_push (push) Successful in 4m10s
Update post_processing.py
2025-07-15 09:39:51 +00:00

254 lines
12 KiB
Python

import math
EPD_REASON_MAP = {
"000": "No Adverse Factors",
"001": "Available Credit On Bankcard Accounts Is Too Low",
"006": "Bankcard Account Balances Are Too High In Proportion To Credit Limits",
"010": "Too Many Delinquencies",
"016": "Too Few Satisfactory Accounts",
"020": "Length Of Time Revolving Accounts Have Been Established Is Too Short",
"022": "Too many inquiries",
"023": "Months Since Most Recent Delinquency Is Too Short",
"024": "Too Many Serious Delinquencies",
"026": "Number Of Delinquent Accounts Is Too High In Proportion To Total Number Of Accounts",
"029": "Retail Account Balances Are Too High In Proportion To Credit Limits",
"030": "Not Enough Retail Debt Experience",
"031": "Revolving Account Balances Are Too High In Proportion To Credit Limits",
"035": "Length Of Time Accounts Have Been Established Is Too Short",
"037": "Too Few Bankcard Accounts",
"043": "Too Few Open Revolving Accounts",
"061": "Too Many Recently Opened Accounts",
"066": "Too Many Serious Derogatory Items",
"069": "Not Enough Debt Experience",
"070": "Length Of Time Since Most Recent Bankcard Account Has Been Established Is Too Short",
"074": "Too Few Satisfactory Revolving Accounts",
"076": "Total Amount Past Due Is Too High",
"103": "Not Enough Available Credit",
"105": "Too Few Revolving Accounts",
"117": "Length Of Time Since Most Seriously Delinquent Account Has Been Established Is Too Short",
"132": "Too Few Open Accounts",
"142": "Not Enough Balance Decreases On Active Non-Mortgage Accounts",
"146": "Recency Of A Balance Overlimit On A Bankcard Account",
"154": "Insufficient Payment Activity Over The Last Year",
"155": "Recency Of Max Aggregate Bankcard Balance Over The Last Year",
"158": "Too Few Open Retail Accounts",
"174": "Too Few Open Bankcard Accounts",
"181": "High Recent Balance Range Relative To Previous Balance Range",
"192": "Not Enough Available Credit On Revolving Accounts",
"201": "Length Of Time Since Oldest Auto Account Has Been Established Is Too Short"
}
VANTAGE_REASON_MAP = {
"10": "Too few accounts paid as agreed",
"11": "Oldest account was opened too recently",
"12": "Delinquent or derogatory status on accounts is too recent",
"13": "Balances on delinquent or derogatory accounts are too high",
"14": "Too high proportion of accounts recently opened",
"15": "Lack of recently reported accounts",
"16": "Total of credit limits and loan amounts is too low",
"17": "No open accounts in your credit file",
"18": "Lack of account information",
"19": "No negative reason code",
"20": "Delinquent or derogatory bankcard",
"21": "Too many bankcards with a high balance",
"22": "Too few bankcards with high credit limit",
"23": "Too high proportion of bankcards recently opened",
"24": "Too many bankcards with high balance compared to credit limit",
"25": "Too high proportion of balances from bankcards",
"26": "Balances on bankcards are too high",
"27": "Delinquent or derogatory status on revolving accounts is too recent",
"28": "Average credit limit on open bankcards is too low",
"29": "Balances on bankcards are too high compared with credit limits",
"30": "Too few open revolving accounts",
"31": "Not enough available credit on revolving accounts",
"32": "Oldest bankcard was opened too recently",
"33": "Not enough balance paid down over time on bankcards",
"34": "Most recently opened revolving account is too new",
"35": "Lack of revolving account information",
"36": "Lack of recently reported revolving accounts",
"37": "No open bankcards in your credit file",
"38": "Lack of bankcard account information",
"39": "Balances on delinquent or derogatory bankcards are too high",
"4": "Balances on accts too high compared to credit limits and loan amounts",
"04": "Balances on accts too high compared to credit limits and loan amounts",
"40": "Too many delinquent or derogatory revolving accounts",
"41": "Average time since revolving accounts opened is too recent",
"42": "Total credit limits on open revolving accounts are too low",
"43": "Too many revolving accounts with high balance compared to credit limit",
"44": "Balances on revolving accts are too high compared with credit limits",
"45": "Not enough balance paid down over time on retail accounts",
"46": "Oldest revolving account was opened too recently",
"47": "No open retail accounts in your credit file",
"48": "Lack of retail account information",
"49": "Not enough balance paid down over time on revolving accounts",
"5": "Too many recent delinquencies",
"05": "Too many recent delinquencies",
"50": "Balances on personal installment accts too high compared to loan amts",
"51": "Too few installment accounts recently paid as agreed",
"52": "Delinquent or derogatory installment account",
"53": "Not enough balance paid down over time on installment accounts",
"54": "Delinquent or derogatory status on installment accounts is too recent",
"55": "Lack of recently reported auto accounts",
"56": "Lack of recently reported installment accounts",
"57": "No open installment accounts in your credit file",
"58": "Lack of installment account information",
"59": "Balances on retail cards are too high compared with credit limits",
"6": "Too many accounts recently opened",
"06": "Too many accounts recently opened",
"60": "Total delinquent or derogatory balances on real estate loans too high",
"61": "No open first mortgage accounts in your credit file",
"62": "Lack of first mortgage account information",
"63": "Delinquent or derogatory real estate secured loan",
"64": "Not enough balance paid down over time on real estate secured loans",
"65": "Oldest real estate secured loan was opened too recently",
"66": "Delinquent or derogatory status on real estate loans is too recent",
"67": "No open real estate secured loans in your credit file",
"68": "Lack of real estate secured loan information",
"69": "Too high proportion of balances from loans not secured by real estate",
"7": "You have too many delinquent or derogatory accounts",
"07": "You have too many delinquent or derogatory accounts",
"70": "Too high proportion of auto accounts are delinquent or derogatory",
"71": "Not enough balance paid down over time on auto accounts",
"72": "Too few auto accounts paid as agreed",
"73": "Delinquent or derogatory auto account",
"74": "Balances on auto accounts are too high compared with loan amounts",
"75": "Payments on auto accounts less than scheduled amount",
"76": "Delinquent or derogatory status on auto accounts is too recent",
"77": "No open auto accounts in your credit file",
"78": "Lack of auto account information",
"79": "No negative reason code",
"8": "Too few accounts recently paid as agreed",
"08": "Too few accounts recently paid as agreed",
"80": "Delinquent or derogatory student loan",
"81": "Not enough balance paid down over time on student loans",
"82": "Lack of recently reported student loans",
"83": "No negative reason code",
"84": "Number of inquiries was a factor in determining the score",
"85": "Too many inquiries",
"86": "Derogatory public records",
"87": "Unpaid collections",
"88": "Bankruptcy",
"89": "No negative reason code",
"9": "Delinquent or derogatory account",
"09": "Delinquent or derogatory account",
"90": "No open revolving accounts in your credit file",
"91": "Balances on delinquent or derogatory revolving accounts are too high",
"92": "Delinquent or derogatory first mortgage",
"93": "Not enough balance paid down over time on first mortgage accounts",
"94": "No negative reason code",
"95": "No negative reason code",
"96": "Too few open accounts",
"97": "Too few accounts"
}
REASON_MAP = {
'evtg04': "System Generated",
'eads66': "System Generated",
's004s': "Length of time on file is too short",
'mt34s': "Not enough balance decreases on mortgage trades in the past 12 months",
'ct320': "Insufficient payment activity",
'us21s': "Length of time since most recent installment account has been established is too short",
'utlmag02': "Revolving account balances are too high in proportion to credit limits over the last 24 months",
'trv01': "Recency of a balance overlimit on a bankcard account",
'us34s': "Not enough balance decreases on installment trades in the past 12 months"
}
def generate_reason_codes(score_key, factors):
# fallback to 4 null rows if no factors found
if not isinstance(factors, list) or len(factors) == 0:
return [{"code": None, "rank": None, "description": None} for _ in range(4)]
reason_map = VANTAGE_REASON_MAP if score_key == "evtg04" else EPD_REASON_MAP if score_key == "eads66" else {}
reason_codes = []
for f in factors[:4]:
code = f.get("code")
rank = f.get("rank")
description = reason_map.get(str(code), "")
reason_codes.append({
"code": code,
"rank": rank,
"description": description
})
# pad to 4
while len(reason_codes) < 4:
reason_codes.append({"code": None, "rank": None, "description": None})
return reason_codes
def post_processing(processing_output, record):
prediction = processing_output["prediction"]
shape_reasoncode = processing_output["shape_reasoncode"]
# grade mapping:
if prediction < 0:
grade = "M14"
else:
m = math.ceil(prediction / 0.01)
m = max(m, 1)
m = min(m, 14)
grade = f"M{m}"
# if prediction ≤ 0.04, not declined
if prediction <= 0.04:
return {
"grade": grade,
"reason_codes": [{
"code": None,
"rank": None,
"description": None
}]
}
conditions = {
'evtg04': lambda x: x < 700,
'eads66': lambda x: x < 700,
's004s': lambda x: x < 12,
'mt34s': lambda x: x > 95,
'ct320': lambda x: x <= 3,
'us21s': lambda x: x <= 3,
'utlmag02': lambda x: x > 300,
'trv01': lambda x: x <= 3,
'us34s': lambda x: x > 90
}
for item in shape_reasoncode:
feat = item.get("feature")
val = item.get("value")
cond = conditions.get(feat)
if cond:
try:
if cond(val):
# If score-type feature (evtg04 or eads66) → full factors-based reason
if feat in ("evtg04", "eads66"):
return {
"grade": grade,
"reason_codes": generate_reason_codes(feat, record.get(f"{feat}_factors", []))
}
else:
# Other features → only 1 reason code based on REASON_MAP
return {
"grade": grade,
"reason_codes": [{
"code": feat,
"rank": "1",
"description": REASON_MAP.get(feat, "Reason not mapped")
}]
}
except Exception:
continue
# Default fallback
return {
"grade": grade,
"reason_codes": [{
"code": feat,
"rank": "1",
"description": "No suitable Product Offerings found"
}]
}