<file_sep>appnope==0.1.2
backcall==0.2.0
click==8.0.1
cycler==0.10.0
debugpy==1.4.3
decorator==5.1.0
entrypoints==0.3
Flask==2.0.1
gunicorn==20.1.0
ipykernel==6.4.1
ipython==7.27.0
ipython-genutils==0.2.0
itsdangerous==2.0.1
jedi==0.18.0
Jinja2==3.0.1
joblib==1.0.1
jupyter-client==7.0.3
jupyter-core==4.8.1
kiwisolver==1.3.2
MarkupSafe==2.0.1
matplotlib==3.4.3
matplotlib-inline==0.1.3
nest-asyncio==1.5.1
numpy==1.21.2
pandas==1.3.3
parso==0.8.2
pexpect==4.8.0
pickleshare==0.7.5
Pillow==8.3.2
prompt-toolkit==3.0.20
ptyprocess==0.7.0
Pygments==2.10.0
pyparsing==2.4.7
python-dateutil==2.8.2
pytz==2021.1
pyzmq==22.3.0
scikit-learn==0.24.2
scipy==1.7.1
seaborn==0.11.2
six==1.16.0
sklearn==0.0
threadpoolctl==2.2.0
tornado==6.1
traitlets==5.1.0
wcwidth==0.2.5
Werkzeug==2.0.1
xgboost==1.4.2
<file_sep>import pandas as pd
import pickle
from collections import defaultdict
import numpy as np
from setting import WoECat, WoENom



#format output
# {
#     "model":"german-credit-risk",
#     "version": "1.0.0",
#     "score_proba":{result}
#
# }
# raw_input ={"person_home_ownership": "MORTGAGE",
#   "loan_intent": "DEBTCONSOLIDATION",
#   "loan_grade": "C",
#   "cb_person_default_on_file": "N",
#   "person_age": 29,
#   "person_income": 95000,
#   "person_emp_length": 4.0,
#   "loan_int_rate": 13.72,
#   "loan_percent_income": 0.03,
#   "cb_person_cred_hist_length": 9,
#   "loan_amnt": 2400}

features_list =["person_home_ownership",
  "loan_intent",
  "loan_grade",
  "cb_person_default_on_file",
  "person_age",
  "person_income",
  "person_emp_length",
  "loan_int_rate",
  "loan_percent_income",
  "cb_person_cred_hist_length",
  "loan_amnt"]
class CustomUnpickler(pickle.Unpickler):
    def find_class(self, module, name):
        try:
            return super().find_class(__name__, name)
        except AttributeError:
            return super().find_class(module, name)

preprocess = CustomUnpickler(open("trained_model/FE-WOE-1.0.0.pkl", "rb")).load()

# with open("trained_model/FE-WOE-1.0.0.pkl", "rb") as f_in: #A
#     preprocess= pickle.load(f_in) #B
model = CustomUnpickler(open("trained_model/M-XGB-1.0.0.pkl", "rb")).load()


def formating_data(raw_input):
    # pandas dataframe
    # urutan colomn
    raw_input = pd.DataFrame.from_dict(raw_input, orient="index").T.replace({
        None: np.nan,
        "null":np.nan,
        "" : np.nan


    })
    features = features_list
    return raw_input[features]
def preprocess_data(raw_input):
    """
    acct dictionary format input
    return pandas / numpy with the same format as development
    """
    # validate data
    X = preprocess.transform(raw_input)
    return X


def predict_data(data):
    # load model
    # input dict
    # preprocess preprocess_data
    # return final predictions
    data = formating_data(data)
    data = preprocess_data(data)
    result = model.predict_proba(data)[:,1]
    return round(result[0],6)

# if __name__ == "__main__":
#     # print(y)
#
#     result = predict_data(raw_input)
#     # print(type(result))
#     print(result)
#     # assert score_proba == result
<file_sep>## Heroku Deployment Access

[Credit_Risk_Exercise](https://credit-score-exercise.herokuapp.com/)

# Credit Risk Exercise

using `credit_risk_dataset.csv` file available under `notebooks/`

The dataset is composed by `32581` rows (observations) and `12` columns (variables).

- `person_age` : is the age of the person at the time of the loan.
- `person_income`: is the yearly income of the person at the time of the loan.
- `person_home_ownership`: is the type of ownership of the home.
- `person_emp_length`: is the amount of time in years that person is employed.
- `loan_intent`: is the aim of the loan.
- `loan_grade`: is a classification system that involves assigning a quality score to a loan based on a borrower's credit history, quality of the collateral, and the likelihood of repayment of the principal and interest.
- `loan_amnt`: is the dimension of the loan taken.
- `loan_int_rate`: is the interest paid for the loan.
- `loan_status`: is a dummy variable where 1 is default, 0 is not default.
- `loan_percent_income`: is the ratio between the loan taken and the annual income.
- `cb_person_default_on_file`: answers whether the person has defaulted before.
- `cb_person_cred_hist_length`: represents the number of years of personal history since the first loan taken from that person.

## How to Use

1. To use locally install all the requirements from file `requirements.txt`, using `pip install -r requirements.txt`
2. Run `app.py` to run python webserver and the application
3. Open `127.0.0.1:3000`

## Model Deployment

### Handling Missing Value
- There 2 feature that have a missing value `person_emp_length` contains 2.75% NaN and `loan_int_rate` contains 9.56% NaN
- We create a new feature `person_emp_length_nan` and	`loan_int_rate_nan` and fill nan values with median, the old feature that has nan values still exist in dataframe
- Model will try to use all the features that have Nan or not and compare each result

### Handling Outlier value
- The outlier will be handled by using Weight of Evidence (WOE)
- The old features and WOE features will be in the different models and the result will be compared.

### Model Evaluation

| Model || Train || Valid|| Holdout Sample|
| --- | --- | --- |--- | --- | --- | --- |
||Accuracy	|AUC	|Accuracy|	AUC|	Accuracy|	AUC|
|XGB OPT WOE CAT	|0.941270|	0.869816|	0.938476|	0.865756|	0.933045|	0.852295|
|XGB OPT DUMMY	|0.940257|	0.868141|	0.933076|	0.855592|	0.931348	|0.851468|
|RFR OPT WOE CAT|	0.914364	|0.813566	|0.914947	|0.814282|	0.913453|	0.809761|
|XGB OPT WOE	|0.892136	|0.780135|	0.875796|	0.749947|	0.886301|	0.767530|
|LogReg WOE OPT	|0.846280	|0.710719|	0.840694|	0.697469|	0.848504|	0.710803|
|RFR OPT WOE	|0.856743|	0.688594|	0.850723|	0.671920|	0.857143|	0.684250|
|LogReg OPT WOE CAT	|0.815902|	0.601540|	0.812343|	0.595295|	0.820734|	0.606344|

### Pipeline
- Using Model XGBoost and Weight of Evidence in categorical features
- Create a WOE in the pipeline for preprocessing
- Create an XGBmodel in pipeline to predict new data

## Guidance

### Example Input and Output Format
- This example if you want input data using `postman`
- This input need to be post under this endpoint:
`127.0.0.1:3000/pred` or `https://credit-score-exercise.herokuapp.com/pred`
#### Headers
- `Content-Type : application/json`

#### Body
- This input using `POST` method, with arguments:

|Field|	Description|	Value|
| --- | --- | --- |
|person_home_ownership	|Home ownership.	|'RENT', 'MORTGAGE', 'OWN', or 'OTHER'|
|loan_intent	|Loan intent.|	'PERSONAL', 'EDUCATION', 'MEDICAL', 'VENTURE', 'HOMEIMPROVEMENT', or 'DEBTCONSOLIDATION'|
|loan_grade|	Loan grade.|	'A', 'B', 'C, 'D', 'E', 'F', or 'G'|
|cb_person_default_on_file	|Historical default.|	'Y', or 'N'|
|person_age	|Age.	|Integer|
|person_income	|Annual Income.|	Integer|
|person_emp_length|	Employment length (in years)	|Integer|
|loan_int_rate|	Interest rate.	|Float|
|loan_percent_income	|Percent income.|	Float  (0.001 - 1.000)|
|cb_person_cred_hist_length	|Credit history length.	|Integer|
|loan_amnt|	Loan amount.|	Integer|

#### Example JSON Input


```json
{"person_home_ownership": "MORTGAGE",
  "loan_intent": "DEBTCONSOLIDATION",
  "loan_grade": "C",
  "cb_person_default_on_file": "N",
  "person_age": 29,
  "person_income": 95000,
  "person_emp_length": 4.0,
  "loan_int_rate": 13.72,
  "loan_percent_income": 0.03,
  "cb_person_cred_hist_length": 9,
  "loan_amnt": 2400}
```
-  After sending the input through postman, python `app.py` will return predicted data

#### Output

|Field|	Description|
| --- | --- |
|model	|Machine learning model.|
|score_proba	|Probability estimates.|
|version|	Model version.|

#### Example JSON Output

```json
{
        "model":"credit_risk_exercise",
        "score_proba":"0.059884",
        "version": "1.0.0"


    }
```
- HTTP Method using GET and POST from flask API
<file_sep>from sklearn.base import BaseEstimator, TransformerMixin
import numpy as np


class WoECat(BaseEstimator, TransformerMixin):
    def __init__(self, feature_name):
        self.feature_name = feature_name
    def fit(self, X, y = None):
        return self
    def transform(self, X, y = None):
        X_ = X.copy() # creating a copy to avoid changes to original dataset
        for i in self.feature_name:
            if i == 'person_home_ownership':
                X_[f'{i}_WOE'] = np.where(X_[i].isin(['MORTAGE']), -0.682,
                      np.where(X_[i].isin(['OTHER']), 0.468,
                      np.where(X_[i].isin(['OWN']), -1.24,
                     np.where(X_[i].isin(['RENT']), 0.502, 0))))
            elif i == 'loan_intent':
                X_[f'{i}_WOE'] = np.where(X_[i].isin(['DEBTCONSOLIDATION']), 0.360,
                  np.where(X_[i].isin(['EDUCATION']), -0.293,
                  np.where(X_[i].isin(['HOMEIMPROVEMENT']), 0.2355,
                 np.where(X_[i].isin(['MEDICAL']), 0.267,
                np.where(X_[i].isin(['PERSONAL']), -0.117,
                np.where(X_[i].isin(['VENTURE']), -0.472,0))))))
            elif i == 'loan_grade':
                X_[f'{i}_WOE'] = np.where(X_[i].isin(['A']), -0.925,
                  np.where(X_[i].isin(['B']), -0.361,
                  np.where(X_[i].isin(['C']), -0.064,
                 np.where(X_[i].isin(['D']), 1.64,
                np.where(X_[i].isin(['E']), 1.87,
                np.where(X_[i].isin(['F']), 2.14,
                         np.where(X_[i].isin(['G']), 5.41,0)))))))
            elif i == 'cb_person_default_on_file':
                X_[f'{i}_WOE'] = np.where(X_[i].isin(['N']), -0.213,
                  np.where(X_[i].isin(['Y']), -0.778, 0))
        X_ = X_.drop(self.feature_name, axis=1)
#         display(X_)
        return X_

class WoENom(BaseEstimator, TransformerMixin):
    def __init__(self):
        pass
    def fit(self, X, y = None):
        return self
    def transform(self, X, y = None):
        X_ = X.copy() # creating a copy to avoid changes to original dataset
        #do nothing
#         display(X_)
        return X_
<file_sep>from flask import Flask, jsonify, json, request, render_template
from logging import debug
app = Flask(__name__)
import pickle
from predict import predict_data

users = []


@app.route('/', methods=['GET', 'POST'])
def hello():
    return render_template("index.html")



@app.route("/pred", methods=['POST','GET'])
def make_predictions():
    if request.method == 'POST':
        data = request.get_json()
        # print(data)
        result = predict_data(data)
        result = {
            'model':'credit_risk_exercise',
            "version": '1.0.0',
            "score_proba":str(result)

        }
        print(result)
        return jsonify(result)

@app.route('/result', methods=['POST','GET'])
def result_pred():

    data = {}

    data["person_home_ownership"] = request.form.get("Home_Ownership")
    data["loan_intent"] = request.form.get("loan_intent")
    data["loan_grade"] = request.form.get("loan_grade")
    data["cb_person_default_on_file"] = request.form.get("Historical_default")
    data["person_age"] = request.form.get("Age")
    data["person_income"] = request.form.get("Annual_Income")
    data["person_emp_length"] = request.form.get("EmploymentLength")
    data["loan_int_rate"] = request.form.get("Interest_rate")
    data["loan_percent_income"] = request.form.get("PercentIncome")
    data["cb_person_cred_hist_length"] = request.form.get("CreditHistoryLength")
    data["loan_amnt"] = request.form.get("LoanAmount")
    print(data)
    result = predict_data(data)
    hasil = {
        'model':'credit_risk_exercise',
        "version": '1.0.0',
        "score_proba":str(result)

    }
    print(hasil)
    prediction = str(result)
    return render_template('result.html', name=prediction)

if __name__ == '__main__':
    app.run(port=3000, debug=False)
<file_sep>from flask import Flask, jsonify, json, request, render_template
from logging import debug
app = Flask(__name__)
import pickle
from predict import predict_data

users = []


@app.route('/', methods=['GET', 'POST'])
def hello():
    return render_template("index.html")



@app.route("/pred", methods=['POST','GET'])
def make_predictions():
    if request.method == 'POST':
        data = request.get_json()
        # print(data)
        result = predict_data(data)
        result = {
            'model':'german-credit-risk',
            "version": '1.0.0',
            "score_proba":result[0]

        }
        print(result)
        return jsonify(result)

@app.route('/result', methods=['POST','GET'])
def result_pred():

    data = {}

    data["Age"] = request.form.get("Age")
    data["Sex"] = request.form.get("Sex")
    data["Job"] = request.form.get("Job")
    data["Housing"] = request.form.get("Housing")
    data["Saving accounts"] = request.form.get("saving_account")
    data["Checking account"] = request.form.get("checking_account")
    data["Credit amount"] = request.form.get("credit_amount")
    data["Duration"] = request.form.get("duration")
    data["Purpose"] = request.form.get("purpose")
    result = predict_data(data)
    hasil = {
        'model':'german-credit-risk',
        "version": '1.0.0',
        "score_proba":result[0]

    }
    print(hasil)
    prediction = str(round(list(result)[0], 3))
    return render_template('result.html', name=prediction)

if __name__ == '__main__':
    app.run(port=3000, debug=False)
