```python
# Preload packages
from pyspark.sql import SparkSession
from pyspark.ml import feature, regression, evaluation, Pipeline
from pyspark.sql import functions as fn, Row
import matplotlib.pyplot as plt
spark = SparkSession.builder.getOrCreate()
sc = spark.sparkContext
# dataframe functions
from pyspark.sql import functions as fn
```

# Random forest for predicting credit score

# A problem with Logistic Regression

While logistic regression is a simple, fast, and effective method. However, the predictions depend linearly on the features. This is, after we have learned the parameters $\theta_0,\theta_1,\dots,\theta_m$, given the features $X$, we have a prediction for the a new point $y$:

\begin{equation}
p(y \mid X) = \frac{1}{1+\exp(-(\theta_0 + \sum_{j>0} x_j \theta_j))}
\end{equation}

Let say we choose to predict that $y$ is 1 if $p(y \mid X) > \frac{1}{2}$, therefore, the decision boundary is given by:

\begin{equation}
\frac{1}{1+\exp(-(\theta_0 + \sum_{j>0} x_j \theta_j))} > \frac{1}{2}
\end{equation}

\begin{equation}
1 > \frac{1+\exp(-(\theta_0 + \sum_{j>0} x_j \theta_j))}{2}
\end{equation}

\begin{equation}
2 > 1+\exp(-(\theta_0 + \sum_{j>0} x_j \theta_j))
\end{equation}

\begin{equation}
1 > \exp(-(\theta_0 + \sum_{j>0} x_j \theta_j))
\end{equation}

By taking the log:

\begin{equation}
0 > -(\theta_0 + \sum_{j>0} x_j \theta_j)
\end{equation}

therefore:

\begin{equation}
0 < \theta_0 + \sum_{j>0} x_j \theta_j
\end{equation}

The decision boundary is exactly when the linear combination $X^T\theta = 0$

## The XOR function

A problem of classic importance that clearly shows when linear boundaries do not work is the XOR function. The [XOR function](https://en.wikipedia.org/wiki/Exclusive_or) stands for eXclusive or and it means "one or the other but not both".

Let's define the function now:


```python
import pandas as pd
import numpy as np
```


```python
X = np.array([
        [0, 0],
        [0, 1],
        [1, 0],
        [1, 1]])
y = np.array([0, 1, 1, 0])
```


```python
pd.DataFrame(np.hstack((X, y.reshape(-1, 1))), columns=['x1', 'x2', 'y'])
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>x1</th>
      <th>x2</th>
      <th>y</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>0</td>
      <td>0</td>
      <td>0</td>
    </tr>
    <tr>
      <th>1</th>
      <td>0</td>
      <td>1</td>
      <td>1</td>
    </tr>
    <tr>
      <th>2</th>
      <td>1</td>
      <td>0</td>
      <td>1</td>
    </tr>
    <tr>
      <th>3</th>
      <td>1</td>
      <td>1</td>
      <td>0</td>
    </tr>
  </tbody>
</table>
</div>




```python
%matplotlib inline
```


```python
import matplotlib.pyplot as plt
```


```python
plt.scatter(X[y==0, 0], X[y==0, 1], c='red');
plt.scatter(X[y==1, 0], X[y==1, 1], c='blue');
plt.xlabel('x1');
plt.ylabel('x2');
plt.legend(['0', '1'], loc='center');
```

The problem with this function is that there is no linear function that correctly can classify the data. It is non-separable.

## Other problems

Sometimes we want to __combine__ features - make them __interact__. But we can't really test all possible combination of features. The space of possible combinations is too large.

## Decision tree

Decision tree recursively partitions the feature space. The decisions are typically simple. Because of the recursive partitioning, the decision can be represented as a tree.

These techniques were originally created to display decisions in a human-redeable fashion. They are therefore easy to read and interpret.

For example, for the XOR problem, is very easy to create a decision tree that solves it with 100% accuracy.


```python
def xor_decision_tree(x):
    x1, x2 = x
    if x1 == 0:
        if x2 == 0:
            return 0
        elif x2 == 1:
            return 1
    elif x1 == 1:
        if x2 == 0:
            return 1
        elif x2 == 1:
            return 0
```


```python
# accuracy
(list(map(xor_decision_tree, X)) == y).mean()
```




    1.0



### Types of decision trees

In mathematical terms, decision tress partition the feature space into, say, $M$ _regions_ $R_1, ..., R_M$. For each region, we build a prediction based on the training datapoints that fall into that region. 

#### Regression tree
For a regression problem and a region $m$, for example, we may predict that the region value is the mean of the points that belong to that region, denoted by $\hat{y}_{R_m}$. In this simple approach, we evaluate the performance of a regression tree by the squared error of the regression predictions

\begin{equation}
\sum_{m=1}^M \sum_{i\in R_m} (y_i - \hat{y}_{R_m})^2
\end{equation}

#### Classification tree

A similar approach is taken for classification trees, where the dependent variable $y$ is categorical. The classification trees predicts a class $k$ for region $m$ as the _most common training class_ occuring in that region.

To measure the performance of a classification tree, we often measure the proportion of training examples from class $k$ in region $m$, denoted as $\hat{p}_{mk}$. For a particular region, one performance measure is the Gini index

\begin{equation}
G_m = \sum_{k=1}^K \hat{p}_{mk} (1 - \hat{p}_{mk})
\end{equation}

Intuitively, the index is small when the values $\hat{p}_{mk}$ are close to zero or one.

Another measure of performance is cross entropy

\begin{equation}
D_m = - \sum_{k=1}^K \hat{p}_{mk} \text{log}(\hat{p}_{mk})
\end{equation}

where similarly distributions that are highly peaked have the lowest cross entropy.

At the end, we want to minimize either $\sum_m G_M$ or $\sum_m D_m$.

### Building a decision tree

The search of all possible recursive partitions is intractable. So typically the process is split into two steps.

1. Building. The tree is build from the top down (from one feature to all features) in a greedy fashion until some criteria is met.

    1. We start from a region that has every point
    1. We select a feature and a cutting point that splits a region so that the loss is minimized.
    1. We repeat recursively until a criteria, such as number of examples in a leaf, is met.

1. Pruning or regularization. Given a regularization parameter $\alpha$ and starting from the most complex tree found before, we find a new tree that optimizes the following loss function

\begin{equation}
\sum_{m=1}^M \sum_{x_i \in R_m} L(y_i, \hat{f}(x_i)) + \alpha M 
\end{equation}

where $L$ is the loss function either for a regression or classification problem. Since $M$ is the number regions, then $\alpha \mid M \mid$ acts as an $L1$ regularization.

# Problems with decision trees

In one word: __Overfitting__

Question: how many ways can we build a tree for the same dataset?

(from ISRL)

1. The good
    1. Trees are very easy to explain to people. In fact, they are even easier to explain than linear regression!
    1. Some people believe that decision trees more closely mirror human decision-making than do the regression and classification approaches seen in previous chapters.
    1. Trees can be displayed graphically, and are easily interpreted even by a non-expert (especially if they are small).
    1. Trees can easily handle qualitative predictors without the need to create dummy variables.

1. The bad
    1. Unfortunately, trees generally do not have the same level of predictive accuracy as some of the other regression and classification approaches seen in this book.
    1. Additionally, trees can be very non-robust. In other words, a small change in the data can cause a large change in the final estimated tree

# Bagging

The problem with decision trees and other learning methods is that they have _high variance_. If fit into two training set, they can produce very different fits.

One solution to this problem is to _average_ the opinions of several models fit to different training sets. Given that we usually don't have many training sets, we use a technique called _bootstrap_ to produce them. In boostrap, we generate _fictitious_ training sets by _sampling with replacement_ the training set. We do this multiple times, fitting a model to each bootsrap sample, and producing a predicting based on the average of the trees. This is called __bagging__.

## The wisdom of the crowds

One easy way to understand why bagging helps. Image asking $B$ people the opinion about the height of someone. Assume that each person has a high variance $\sigma^2$ in their estimates and no biases. If we average their opinions, what is the variance of the resulting estimate?


```python
import numpy as np
```


```python
person_std = np.sqrt(10.)
n_simulations = 10000
B = 10
opinions = np.random.normal(scale=person_std, size = (n_simulations, B))
```


```python
# lets average opinions
avg_opinions = opinions.mean(axis=1)
```


```python
# let's look at the variance
np.var(avg_opinions)
```




    0.9806098202243334




```python
import matplotlib.pyplot as plt
```


```python
plt.clf();
plt.plot(np.linspace(1, 10, num=10), 10/np.linspace(1, 10, num=10));
plt.xlabel('B');
plt.ylabel('Variance');
plt.ylim([0, 12]);
```

The variance seems to be a 10th of the original variance! Which is exacty right.

## Tree bagging

Similarly to the wisdom of the crowds, you can think of the statistical estimation of models and then bagging as a way of lowering the variance of models!

Therefore, if we fit ($\hat{f}^{*b}$) different decision tree to boostrapped version $b$ of the data, then we can average those results for regression:

\begin{equation}
\hat{f}_{\text{bagging}}(X) = \frac{1}{B} \sum_{b=1}^B  \hat{f}^{*b}(X)
\end{equation}

or in classification

\begin{equation}
\hat{f}_{\text{bagging}}(X) = \text{majority} \{\hat{f}^{*b}(X) \mid b \in \{1, \dots, B \} \}
\end{equation}

The problem with tree bagging is that we have lost the interpretability of results because we are now averaging the prediction of several trees.

One solution to this is to compute, across all trees, how much on average a feature decreases the loss function. We can then use that measure to sort features by importance.

One convenient by-product of bagging is that we can compute the validaton performance of our model by computing the performance of the fit in the _out of bag_ datapoints. This is, every time we sample with replacement from the data, there is a set of datapoints that is not used in the data.

The argument goes like this. A single point has a chance of $1/n$ of being sampled. This means that the probability of a point not being sampled during a bootstrap sample is $(1-\frac{1}{n})^n$. When $\lim_{n\rightarrow \infty} (1-\frac{1}{n})^n = \frac{1}{e} \approx 36% \approx 1/3 $. So, in boostrapped estimators a point will be out of the fitting 1/3 of the time. 

At the end, we will have $B/3$ out of sample estimators for each data which gives us testing performance estimation for free!

### Limitations of the wisdom of the crowds

Now, the wisdom of the crowds assumes that _opinions are independent_, but boostrapping makes the trees dependent because we are resampling the same training data multiple times.

What is the effect of making opinions dependent?


```python
# wisdom of the crowds with opinion correlation
corr = 0.9
B = 1000
cov = (np.ones((B, B)) * corr + np.eye(B)*(1-corr))*(person_std**2)
mu = np.zeros(B)
correlated_opinions = np.random.multivariate_normal(mu, cov, (n_simulations,))
```


```python
correlated_opinions.mean(1).var()
```




    8.908816178750739



With 90% correlation, the crowd doesn't do much better than individuals! In general, the variance of correlated opinions is:

\begin{equation}
\text{var}(f) = \bigl( \frac{1-\rho}{B} + \rho \bigr) \sigma^2
\end{equation}


```python
plt.clf();
pl1 = plt.plot(np.linspace(1, 10, num=10), 10/np.linspace(1, 10, num=10));
pl2 = plt.plot(np.linspace(1, 10, num=10), 10*((1-0.9)/np.linspace(1, 10, num=10) + 0.9));
plt.xlabel('B');
plt.ylabel('Variance');
plt.ylim([0, 12]);
plt.legend(['no correlation', '90% correlation']);
```

## Random forest

Random forest is a tweak to the boostrapped trees predictions that decorrelate trees. It does this by randomly sampling $m$ features during each split when bulding the tree. In this way, each of trees look at different features and become decorrelated. A typical value of $m = \sqrt{p}$, where $p$ is the total number of variables.

## Gradient boosting

Boosting _grows_ a model. It first fits a simple model and the next model focuses on samples poorly fit by the previous model. It is a way of _adaptively_ fitting the data. In a sense, boosting does not resample the data. 

Boosting is a technique whose theoretical workings go outside of the scope of this course. Roughly speaking, however, while bagging aims at lowering the variance in the learning process, boosting aims at reducing the bias.

# Application in Spark


```python
# dataframe functions
from pyspark.sql import functions as fn
```


```python
import os
```


```python
credit_score_df = spark.read.parquet('datasets/cs-training.parquet')
# change type to double, drop NAs, and change dependent variable name to label
credit_score_df = credit_score_df.withColumnRenamed('SeriousDlqin2yrs', 'label')
```


```python
credit_score_df.limit(10).toPandas()
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>case_id</th>
      <th>label</th>
      <th>RevolvingUtilizationOfUnsecuredLines</th>
      <th>age</th>
      <th>NumberOfTime30-59DaysPastDueNotWorse</th>
      <th>DebtRatio</th>
      <th>MonthlyIncome</th>
      <th>NumberOfOpenCreditLinesAndLoans</th>
      <th>NumberOfTimes90DaysLate</th>
      <th>NumberRealEstateLoansOrLines</th>
      <th>NumberOfTime60-89DaysPastDueNotWorse</th>
      <th>NumberOfDependents</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>1.0</td>
      <td>1.0</td>
      <td>0.766127</td>
      <td>45.0</td>
      <td>2.0</td>
      <td>0.802982</td>
      <td>9120.0</td>
      <td>13.0</td>
      <td>0.0</td>
      <td>6.0</td>
      <td>0.0</td>
      <td>2.0</td>
    </tr>
    <tr>
      <th>1</th>
      <td>2.0</td>
      <td>0.0</td>
      <td>0.957151</td>
      <td>40.0</td>
      <td>0.0</td>
      <td>0.121876</td>
      <td>2600.0</td>
      <td>4.0</td>
      <td>0.0</td>
      <td>0.0</td>
      <td>0.0</td>
      <td>1.0</td>
    </tr>
    <tr>
      <th>2</th>
      <td>3.0</td>
      <td>0.0</td>
      <td>0.658180</td>
      <td>38.0</td>
      <td>1.0</td>
      <td>0.085113</td>
      <td>3042.0</td>
      <td>2.0</td>
      <td>1.0</td>
      <td>0.0</td>
      <td>0.0</td>
      <td>0.0</td>
    </tr>
    <tr>
      <th>3</th>
      <td>4.0</td>
      <td>0.0</td>
      <td>0.233810</td>
      <td>30.0</td>
      <td>0.0</td>
      <td>0.036050</td>
      <td>3300.0</td>
      <td>5.0</td>
      <td>0.0</td>
      <td>0.0</td>
      <td>0.0</td>
      <td>0.0</td>
    </tr>
    <tr>
      <th>4</th>
      <td>5.0</td>
      <td>0.0</td>
      <td>0.907239</td>
      <td>49.0</td>
      <td>1.0</td>
      <td>0.024926</td>
      <td>63588.0</td>
      <td>7.0</td>
      <td>0.0</td>
      <td>1.0</td>
      <td>0.0</td>
      <td>0.0</td>
    </tr>
    <tr>
      <th>5</th>
      <td>6.0</td>
      <td>0.0</td>
      <td>0.213179</td>
      <td>74.0</td>
      <td>0.0</td>
      <td>0.375607</td>
      <td>3500.0</td>
      <td>3.0</td>
      <td>0.0</td>
      <td>1.0</td>
      <td>0.0</td>
      <td>1.0</td>
    </tr>
    <tr>
      <th>6</th>
      <td>8.0</td>
      <td>0.0</td>
      <td>0.754464</td>
      <td>39.0</td>
      <td>0.0</td>
      <td>0.209940</td>
      <td>3500.0</td>
      <td>8.0</td>
      <td>0.0</td>
      <td>0.0</td>
      <td>0.0</td>
      <td>0.0</td>
    </tr>
    <tr>
      <th>7</th>
      <td>10.0</td>
      <td>0.0</td>
      <td>0.189169</td>
      <td>57.0</td>
      <td>0.0</td>
      <td>0.606291</td>
      <td>23684.0</td>
      <td>9.0</td>
      <td>0.0</td>
      <td>4.0</td>
      <td>0.0</td>
      <td>2.0</td>
    </tr>
    <tr>
      <th>8</th>
      <td>11.0</td>
      <td>0.0</td>
      <td>0.644226</td>
      <td>30.0</td>
      <td>0.0</td>
      <td>0.309476</td>
      <td>2500.0</td>
      <td>5.0</td>
      <td>0.0</td>
      <td>0.0</td>
      <td>0.0</td>
      <td>0.0</td>
    </tr>
    <tr>
      <th>9</th>
      <td>12.0</td>
      <td>0.0</td>
      <td>0.018798</td>
      <td>51.0</td>
      <td>0.0</td>
      <td>0.531529</td>
      <td>6501.0</td>
      <td>7.0</td>
      <td>0.0</td>
      <td>2.0</td>
      <td>0.0</td>
      <td>2.0</td>
    </tr>
  </tbody>
</table>
</div>



| Variable Name                        | Description                                                                                                                                              | Type       |
|--------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------|------------|
| SeriousDlqin2yrs                     | Person experienced 90 days past due delinquency or worse                                                                                                 | Y/N        |
| RevolvingUtilizationOfUnsecuredLines | Total balance on credit cards and personal lines of credit except real estate and no installment debt like car loans divided by the sum of credit limits | percentage |
| age                                  | Age of borrower in years                                                                                                                                 | integer    |
| NumberOfTime30-59DaysPastDueNotWorse | Number of times borrower has been 30-59 days past due but no worse in the last 2 years.                                                                  | integer    |
| DebtRatio                            | Monthly debt payments, alimony,living costs divided by monthy gross income                                                                               | percentage |
| MonthlyIncome                        | Monthly income                                                                                                                                           | real       |
| NumberOfOpenCreditLinesAndLoans      | Number of Open loans (installment like car loan or mortgage) and Lines of credit (e.g. credit cards)                                                     | integer    |
| NumberOfTimes90DaysLate              | Number of times borrower has been 90 days or more past due.                                                                                              | integer    |
| NumberRealEstateLoansOrLines         | Number of mortgage and real estate loans including home equity lines of credit                                                                           | integer    |
| NumberOfTime60-89DaysPastDueNotWorse | Number of times borrower has been 60-89 days past due but no worse in the last 2 years.                                                                  | integer    |
| NumberOfDependents                   | Number of dependents in family excluding themselves (spouse, children etc.)                                                                              | integer    |

We need to put the independent variables into one feature vector


```python
from pyspark.ml.feature import VectorAssembler
```


```python
training_df, validation_df, testing_df = credit_score_df.randomSplit([0.6, 0.3, 0.1])
```


```python
# build a pipeline for analysis
va = VectorAssembler().setInputCols(training_df.columns[2:]).setOutputCol('features')
```


```python
from pyspark.ml.classification import LogisticRegression, RandomForestClassifier
```


```python
from pyspark.ml import Pipeline
```


```python
lr = LogisticRegression(regParam=0.1)
```


```python
lr_pipeline = Pipeline(stages=[va, lr]).fit(training_df)
```


```python
rf = RandomForestClassifier()
```


```python
rf_pipeline = Pipeline(stages=[va, rf]).fit(training_df)
```


```python
from pyspark.ml.evaluation import BinaryClassificationEvaluator
```


```python
bce = BinaryClassificationEvaluator()
```


```python
bce.evaluate(lr_pipeline.transform(validation_df))
```




    0.6692505322633585




```python
bce.evaluate(rf_pipeline.transform(validation_df))
```




    0.8402624304893843



Without much changes, Random Forest performs much better!

# Try to interpret the results

## Logistic regression


```python
lr_model = lr_pipeline.stages[-1]
```


```python
import pandas as pd
```


```python
pd.DataFrame(list(zip(credit_score_df.columns[2:], lr_model.coefficients.toArray())),
            columns = ['column', 'weight']).sort_values('weight')
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>column</th>
      <th>weight</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>1</th>
      <td>age</td>
      <td>-0.009877</td>
    </tr>
    <tr>
      <th>5</th>
      <td>NumberOfOpenCreditLinesAndLoans</td>
      <td>-0.003472</td>
    </tr>
    <tr>
      <th>3</th>
      <td>DebtRatio</td>
      <td>-0.000017</td>
    </tr>
    <tr>
      <th>0</th>
      <td>RevolvingUtilizationOfUnsecuredLines</td>
      <td>-0.000008</td>
    </tr>
    <tr>
      <th>4</th>
      <td>MonthlyIncome</td>
      <td>-0.000004</td>
    </tr>
    <tr>
      <th>8</th>
      <td>NumberOfTime60-89DaysPastDueNotWorse</td>
      <td>0.000890</td>
    </tr>
    <tr>
      <th>7</th>
      <td>NumberRealEstateLoansOrLines</td>
      <td>0.007461</td>
    </tr>
    <tr>
      <th>6</th>
      <td>NumberOfTimes90DaysLate</td>
      <td>0.014574</td>
    </tr>
    <tr>
      <th>2</th>
      <td>NumberOfTime30-59DaysPastDueNotWorse</td>
      <td>0.023416</td>
    </tr>
    <tr>
      <th>9</th>
      <td>NumberOfDependents</td>
      <td>0.057230</td>
    </tr>
  </tbody>
</table>
</div>



## Random forest


```python
rf_model = rf_pipeline.stages[-1]
```


```python
pd.DataFrame(list(zip(credit_score_df.columns[2:], rf_model.featureImportances.toArray())),
            columns = ['column', 'weight']).sort_values('weight')
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>column</th>
      <th>weight</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>9</th>
      <td>NumberOfDependents</td>
      <td>0.002222</td>
    </tr>
    <tr>
      <th>7</th>
      <td>NumberRealEstateLoansOrLines</td>
      <td>0.006340</td>
    </tr>
    <tr>
      <th>4</th>
      <td>MonthlyIncome</td>
      <td>0.006468</td>
    </tr>
    <tr>
      <th>5</th>
      <td>NumberOfOpenCreditLinesAndLoans</td>
      <td>0.008131</td>
    </tr>
    <tr>
      <th>3</th>
      <td>DebtRatio</td>
      <td>0.014706</td>
    </tr>
    <tr>
      <th>1</th>
      <td>age</td>
      <td>0.014848</td>
    </tr>
    <tr>
      <th>2</th>
      <td>NumberOfTime30-59DaysPastDueNotWorse</td>
      <td>0.150698</td>
    </tr>
    <tr>
      <th>8</th>
      <td>NumberOfTime60-89DaysPastDueNotWorse</td>
      <td>0.207864</td>
    </tr>
    <tr>
      <th>0</th>
      <td>RevolvingUtilizationOfUnsecuredLines</td>
      <td>0.226564</td>
    </tr>
    <tr>
      <th>6</th>
      <td>NumberOfTimes90DaysLate</td>
      <td>0.362159</td>
    </tr>
  </tbody>
</table>
</div>



Let's take a look at one tree


```python
len(rf_model.trees)
```




    20




```python
print(rf_model.trees[0].toDebugString)
```

    DecisionTreeClassificationModel (uid=dtc_0197d19de214) of depth 5 with 63 nodes
      If (feature 8 <= 0.5)
       If (feature 0 <= 0.760372257)
        If (feature 2 <= 1.5)
         If (feature 6 <= 0.5)
          If (feature 2 <= 0.5)
           Predict: 0.0
          Else (feature 2 > 0.5)
           Predict: 0.0
         Else (feature 6 > 0.5)
          If (feature 0 <= 0.3434886975)
           Predict: 0.0
          Else (feature 0 > 0.3434886975)
           Predict: 0.0
        Else (feature 2 > 1.5)
         If (feature 7 <= 6.5)
          If (feature 6 <= 0.5)
           Predict: 0.0
          Else (feature 6 > 0.5)
           Predict: 0.0
         Else (feature 7 > 6.5)
          If (feature 2 <= 2.5)
           Predict: 0.0
          Else (feature 2 > 2.5)
           Predict: 1.0
       Else (feature 0 > 0.760372257)
        If (feature 6 <= 0.5)
         If (feature 2 <= 1.5)
          If (feature 2 <= 0.5)
           Predict: 0.0
          Else (feature 2 > 0.5)
           Predict: 0.0
         Else (feature 2 > 1.5)
          If (feature 3 <= 0.52000446)
           Predict: 0.0
          Else (feature 3 > 0.52000446)
           Predict: 0.0
        Else (feature 6 > 0.5)
         If (feature 6 <= 2.5)
          If (feature 0 <= 0.9999999500000001)
           Predict: 0.0
          Else (feature 0 > 0.9999999500000001)
           Predict: 1.0
         Else (feature 6 > 2.5)
          If (feature 3 <= 0.102934254)
           Predict: 1.0
          Else (feature 3 > 0.102934254)
           Predict: 1.0
      Else (feature 8 > 0.5)
       If (feature 6 <= 0.5)
        If (feature 2 <= 2.5)
         If (feature 3 <= 0.52000446)
          If (feature 9 <= 4.5)
           Predict: 0.0
          Else (feature 9 > 4.5)
           Predict: 1.0
         Else (feature 3 > 0.52000446)
          If (feature 2 <= 0.5)
           Predict: 0.0
          Else (feature 2 > 0.5)
           Predict: 0.0
        Else (feature 2 > 2.5)
         If (feature 3 <= 1.5929396905000002)
          If (feature 8 <= 4.5)
           Predict: 0.0
          Else (feature 8 > 4.5)
           Predict: 1.0
         Else (feature 3 > 1.5929396905000002)
          If (feature 4 <= 887.5)
           Predict: 0.0
          Else (feature 4 > 887.5)
           Predict: 1.0
       Else (feature 6 > 0.5)
        If (feature 2 <= 0.5)
         If (feature 0 <= 0.0029947805)
          If (feature 1 <= 53.5)
           Predict: 1.0
          Else (feature 1 > 53.5)
           Predict: 0.0
         Else (feature 0 > 0.0029947805)
          If (feature 3 <= 0.043076442)
           Predict: 0.0
          Else (feature 3 > 0.043076442)
           Predict: 0.0
        Else (feature 2 > 0.5)
         If (feature 0 <= 0.6742148045)
          If (feature 1 <= 31.5)
           Predict: 1.0
          Else (feature 1 > 31.5)
           Predict: 0.0
         Else (feature 0 > 0.6742148045)
          If (feature 5 <= 13.5)
           Predict: 1.0
          Else (feature 5 > 13.5)
           Predict: 1.0
    


45b4b31a940ca2d642ac4f2fc9f816a8b5aa90c5
ipynb
Jupyter Notebook
notebooks/random_forest_for_predicting_credit_score.ipynb
daniel-acuna/ist718
0a83f373aa00dc9cd1ff2e8da74d0255f04c9728
BSD-4-Clause-UC
2018-09-17T14:02:59.000Z
2021-08-31T19:08:07.000Z
notebooks/random_forest_for_predicting_credit_score.ipynb
wozhouwozhou/ist718
565e9767f6f35f77f9c14f2a94b2d75a0a6e2c02
BSD-4-Clause-UC
2020-03-24T15:51:10.000Z
2021-12-13T19:48:14.000Z
notebooks/random_forest_for_predicting_credit_score.ipynb
wozhouwozhou/ist718
565e9767f6f35f77f9c14f2a94b2d75a0a6e2c02
BSD-4-Clause-UC
2018-09-25T13:35:39.000Z
2021-09-04T15:29:42.000Z
Qwen/Qwen-72B
1. YES
2. YES