# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# + pycharm={"name": "#%%\n"}
import numpy as np
# classification
# from models import *
data = np.array([[-4, -4, 0], [-3, -3, 0], [-2, -2, 0], [-1, -1, 0], [1, 1, 0], [2, 2, 0], [3, 3, 0],
                [1, -1, 1], [2, -2, 1], [3, -3, 3], [-1, 1, 1], [-2, 2, 1], [-3, 3, 1]])
# -

from models import *
import numpy as np
import pandas as pd

# +
from dataset import *
import pandas as pd
dataset = load_dataset('taiwan_credit_risk', train_prop=0.8, valid_prop=0.1)


df = pd.DataFrame(dataset.X_train)
df['y'] = dataset.y_train

data = df.to_numpy()

# +
dataset2 = pd.read_csv('datasets/ames_housing/ames_housing_training.csv', index_col=0)
df2 = pd.DataFrame(dataset2)

df2=df2.select_dtypes([np.number]).dropna()
data2_train = df2.to_numpy()[:(int)(0.9*len(df2))]
data2_val = df2.to_numpy()[(int)(0.9*len(df2)):]

# +
from sklearn.model_selection import StratifiedKFold, KFold
kf = KFold(n_splits=5, shuffle=True, random_state=123)


def load_data(path):
    dataset = pd.read_csv(path, index_col=0)
    df = pd.DataFrame(dataset)
#     df = df.sample(frac=1).reset_index(drop=True)

    df= df.select_dtypes([np.number]).dropna()
    data_train = df.to_numpy()[:(int)(0.9*len(df))]
    data_val = df.to_numpy()[(int)(0.9*len(df)):]
    return df, data_train, data_val

def k_fold(path):
    l1 = []
    l2 = []
    dataset = pd.read_csv(path, index_col=0)
    df = pd.DataFrame(dataset)
    df=df.select_dtypes([np.number]).dropna()
    for train_index, test_index in kf.split(df):
        b_train = df.to_numpy()[train_index]
        b_val = df.to_numpy()[test_index]
        l1.append(b_train)
        l2.append(b_val)
    return df, l1, l2
        
        
# -

df_bike, b_train, b_val = k_fold('datasets/Bike-Sharing-Dataset/day.csv')

b_train[0]


# +
def check_purity_regression(data, threshold):
    return len(data) < threshold

def calculate_mse(data_below, data_above):
    # Getting the means 
    below_mean = np.mean(data_below[:,-1])
    above_mean = np.mean(data_above[:,-1]) 
    # Getting the left and right residuals 
    res_below = data_below[:,-1] - below_mean 
    res_above = data_above[:,-1] - above_mean
    # Concatenating the residuals 
    r = np.concatenate((res_below, res_above), axis=None)

    # Calculating the mse 
    n = len(r)
    r = r ** 2
    r = np.sum(r)
    mse_split = r / n
    return mse_split

def calc_mse_whole(data):
    r = data[:,-1]-np.mean(data[:,-1])
    n = len(r)
    r = r ** 2
    r = np.sum(r)
    mse = r / n
    return mse

def get_potential_splits(data, n_columns, isForest=False):
#     print(f"finding splits for {data.shape}")
    potential_splits = {}
    if isForest:
        features = list()
#         print(n_columns)
        while len(features) < n_columns:
#             print(len(features))
            index = randrange(data.shape[1]-1)
#             print(index)

            if index not in features:
                features.append(index)
    else:
        features = [i for i in range(n_columns-1)]
    for column_index in features:
        potential_splits[column_index] = []
        values = data[:, column_index]
        unique_values = np.unique(values)

        for index in range(1, len(unique_values)):
            previous_value = unique_values[index - 1]
            current_value = unique_values[index]
            potential_split = (current_value + previous_value) / 2

            potential_splits[column_index].append(potential_split)

    return potential_splits


def determine_best_split(data, potential_splits):
#     print(f"Determining best split for {data.shape}")
    overall_mse = calc_mse_whole(data)
    best_split_column = None
    for column_index in potential_splits:
#         print(f"column index: {column_index}")
        time_split = 0
        for value in potential_splits[column_index]:
            
            data_below, data_above = split_data(data, split_column=column_index, split_value=value)
            current_overall_mse = calculate_mse(data_below, data_above)
            if current_overall_mse < overall_mse:
                overall_mse = current_overall_mse
#                 print(f"overall mse: {overall_mse}")
                best_split_column = column_index
                best_split_value = value
    
    if best_split_column == None:
        return None, None

    return best_split_column, best_split_value

def regression_tree(x_train, y_train, threshold):
    data = np.concatenate((x_train, y_train), axis=1)
    regression_tree_helper(data, threshold)


def regression_tree_helper(data, threshold, isForest=False):

#     print(data.shape)
    if len(data) <= threshold:
        return data[:, -1]
    else:
        potential_splits = get_potential_splits(data, data.shape[1])
        split_column, split_value = determine_best_split(data, potential_splits)
        data_below, data_above = split_data(data, split_column, split_value)

        # instantiate sub-tree
        question = "{} <= {}".format(split_column, split_value)
        sub_tree = {question: []}

        # find answers (recursion)
        yes_answer = regression_tree_helper(data_below, threshold)
        no_answer = regression_tree_helper(data_above, threshold)

        sub_tree[question].append(yes_answer)
        sub_tree[question].append(no_answer)

        return sub_tree


def classify_example(example, decision_tree, q):
    question = list(decision_tree.keys())[0] # decision_tree.keys() returns a dictionary
    feature_name, comparison_operator, value = question.split() # question is of form '0' <= 0.5'

    # ask question
    if example[int(feature_name)] <= float(value):
        answer = decision_tree[question][0]
    else:
        answer = decision_tree[question][1]

    # base case
    if not isinstance(answer, dict):
        return np.quantile(answer, q)

    # recursive part
    else:
        residual_tree = answer
        return classify_example(example, residual_tree, q)




# +
def find_pred(main_tree, val):
    pred = []
    for x in val:
#         print(f"predict: {classify_example(x[:-1], main_tree, 0.95)}")
#         print(f"predict: {classify_example(x[:-1], main_tree, 0.05)}")
#         print(f"predict: {classify_example(x[:-1], main_tree, 0.5)}")
#         print(f"real: {x[-1]}")
        pred.append(classify_example(x[:-1], main_tree, 0.5))
    return pred
    
    
# -

def calc_mse_val(pred, y):
    r = np.sum((pred-y)**2)
    n = len(y)
    mse = r / n
    return mse


def calc_mae_val(pred, y):
    r = np.sum(abs(pred-y))
    n = len(y)
    mae = r / n
    return mae


def evaluation(val, main_tree):
    accuracy = 0
    for x in val:
        top = classify_example(x[:-1], main_tree, 0.975)
        bottom = classify_example(x[:-1], main_tree, 0.025)
        if x[-1] <= top and x[-1] >= bottom:
            accuracy += 1

    percent = accuracy / len(val)
    return percent
    


# + pycharm={"is_executing": true, "name": "#%%\n"}
#baseline comparison of decision tree 
from sklearn.tree import DecisionTreeRegressor
cmp = 0
acc1 = 0
acc2 = 0
for i in range(len(b_train)):
    result = []

    main_tree = regression_tree_helper(b_train[i], 10)
    p = find_pred(main_tree, b_val[i])
    
        
    regressor = DecisionTreeRegressor(random_state=0)
    regressor.fit(b_train[i][:,:-1], b_train[i][:,-1])
    p2 = [x for x in regressor.predict(b_val[i][:,:-1])]
    qt = calc_mse_val(p, b_val[i][:,-1])
    t = calc_mse_val(p2, b_val[i][:,-1])
    acc1+= qt
    acc2+=t
    print("qt = ", qt)
    print("t = ", t)
    print("accucracy = ", evaluation(b_val[i], main_tree))
    if qt < t:
        cmp += 1
print(cmp)
#mean 
print("mean mse for quantile regression tree: ", acc1/len(b_train))
print("mean mse for sklearn implementation: ",acc2/len(b_train))

    
    

# +
from random import seed
from random import randrange
from math import sqrt
from sklearn.ensemble import RandomForestRegressor
 
def regression_forest_helper(data, threshold, n_columns):

#     print(data.shape)
    if len(data) <= threshold:
        return data[:, -1]
    else:
        potential_splits = get_potential_splits(data, n_columns, True)
        split_column, split_value = determine_best_split(data, potential_splits)
        if split_column == None:
            return data[:, -1]
        data_below, data_above = split_data(data, split_column, split_value)

        # instantiate sub-tree
        question = "{} <= {}".format(split_column, split_value)
        sub_tree = {question: []}

        # find answers (recursion)
        yes_answer = regression_forest_helper(data_below, threshold, n_columns)
        no_answer = regression_forest_helper(data_above, threshold, n_columns)

        sub_tree[question].append(yes_answer)
        sub_tree[question].append(no_answer)

        return sub_tree

# Evaluate an algorithm using a cross validation split
def evaluate_algorithm(path, algorithm, *args):
    df_bike, b_train, b_val = k_fold(path)
    scores_mae = list()
    scores_mse = list()
    scores_mse_base = list()
    for i in range(len(b_train)):
        #add baseline comparitor
        regr = RandomForestRegressor(n_estimator=n_trees)
        regr.fit(b_train[i][:,:-1], b_train[i][:,-1])
        baseline_predict = [x for x in regr.predict(b_val[i][:,:-1])]
        baseline_mse = calc_mse_val(baseline_predict, b_val[i][:,-1])
        scores_mse_base.append(baseline_mse)
        
        #our algo's prediction
        predicted = algorithm(b_train[i], b_val[i], *args)
        actual = b_val[i][:,-1]
        mae = calc_mae_val(predicted, actual)
        mse = calc_mse_val(predicted, actual)
        
        print("mae = ", mae)
        print("mse = ", mse)
        scores_mae.append(mae)
        scores_mse.append(mse)
    return scores_mae, scores_mse, scores_mse_base


# Create a random subsample from the dataset with replacement
def subsample(dataset, ratio):
    sample = list()
    n_sample = round(len(dataset) * ratio)
    while len(sample) < n_sample:
        index = randrange(len(dataset))
        sample.append(dataset[index])
    return np.array(sample)

# Make a prediction with a list of bagged trees
# return: m x n matrix, m = number of trees, n = number of samples
def bagging_predict(trees, val):
    predictions = [find_pred(tree, val) for tree in trees]
    ##todo: return mean of the column

    result = np.array(predictions)
    p = np.mean(result, axis=0)
    
    return p

# Random Forest Algorithm
def random_forest(train, test, threshold, sample_size, n_trees, n_features):
    trees = list()
    for i in range(n_trees):
        sample = subsample(train, sample_size)
        tree = regression_forest_helper(sample, threshold, n_features)
        trees.append(tree)

    return bagging_predict(trees, test)


# Test the random forest algorithm
seed(2)


# -

path = 'datasets/Bike-Sharing-Dataset/day.csv'
threshold = 1
sample_size = 1.0
n_features = int(df.shape[1]/3)
for n_trees in [10, 100, 1000]:
    scores_mae, scores_mse, scores_mse_base = evaluate_algorithm(path, random_forest, threshold, sample_size, n_trees, n_features)
    print('Trees: %d' % n_trees)
    print('Mean MSE: %.3f%' % (sum(scores_mse)/float(len(scores_mae))))
    print('Mean MSE baseline: %.3f%' % (sum(scores_mse_base)/float(len(scores_mae))))
    print('Mean MAE: %.3f%' % (sum(scores_mae)/float(len(scores_mse))))

StarcoderdataJupyterScriptsDedupFiltered