from sklearn.linear_model import LinearRegression
from sklearn.feature_selection import RFE, f_regression
import numpy as np
from sklearn.preprocessing import MinMaxScaler
from operator import itemgetter
from sklearn.ensemble import RandomForestRegressor


def rank_to_dict(ranks, names):
    ranks = np.abs(ranks)

    minmax = MinMaxScaler()

    ranks = minmax.fit_transform(np.array(ranks).reshape(FEATURES_AMOUNT, 1)).ravel()
    ranks = map(lambda x: round(x, 2), ranks)
    return dict(zip(names, ranks))


def flip_array(arr):
    return -1 * arr + np.max(arr)


def sort_by_desc(dictionary):
    return dict(sorted(dictionary.items(), key=itemgetter(1), reverse=True))


def calc_mean(ranks):
    mean = {}

    for key, value in ranks.items():
        for item in value.items():
            if item[0] not in mean:
                mean[item[0]] = 0
                mean[item[0]] += item[1]

    for key, value in mean.items():
        res = value / len(ranks)
        mean[key] = round(res, 2)

    return sort_by_desc(mean)


# Исходные данные составляют 750 строк-наблюдений и 14 столбцов-признаков

FEATURES_SIZE = 750
FEATURES_AMOUNT = 14

# Генерация случайных исходных данных

np.random.seed(0)
x = np.random.uniform(0, 1, (FEATURES_SIZE, 14))

# Создание функции-выхода (постановка регрессионной проблемы Фридмана) и добавление зависимости признаков

y = (10 * np.sin(np.pi * x[:, 0] * x[:, 1]) + 20 * (x[:, 2] - .5) ** 2 +
     10 * x[:, 3] + 5 * x[:, 4] ** 5 + np.random.normal(0, 1))

x[:, 10:] = x[:, :4] + np.random.normal(0, .025, (FEATURES_SIZE, 4))

# Создаём модель рекурсивного сокращения признаков на основе линейной модели и обучаем её

regression = LinearRegression()
regression.fit(x, y)
rfe = RFE(regression)
rfe.fit(x, y)

# Создаём модель сокращения признаков случайными деревьями и обучаем её

rfr = RandomForestRegressor()
rfr.fit(x, y)

# Создаём модель линейной корреляции и обучаем её

f, _ = f_regression(x, y, center=False)

# Аккумулируем наименования признаков

features_names = ["x%s" % i for i in range(1, FEATURES_AMOUNT + 1)]

# Собираем отображения значений каждого признака каждой моделью

features_ranks = {
    'RFE': sort_by_desc(rank_to_dict(flip_array(rfe.ranking_), features_names)),
    'RFR': sort_by_desc(rank_to_dict(rfr.feature_importances_, features_names)),
    'f_regression': sort_by_desc(rank_to_dict(f, features_names))
}

# Подсчитываем среднюю оценку и выводим результаты

print(f"Результаты:"
      f"\n RFE \n{features_ranks['RFE']}"
      f"\n RFR \n{features_ranks['RFR']}"
      f"\n f_regression \n {features_ranks['f_regression']}"
      f"\n Средние значения \n{calc_mean(features_ranks)}")