diff --git a/LabWork01/FuncLoad.py b/LabWork01/FuncLoad.py index 9513ba0..50d58ce 100644 --- a/LabWork01/FuncLoad.py +++ b/LabWork01/FuncLoad.py @@ -1,6 +1,6 @@ import pandas as pd -def createDataFrame(): +def createDataFrame() -> pd.DataFrame: df = pd.read_csv('res/Stores.csv') return df \ No newline at end of file diff --git a/LabWork01/LabWork6/ConvertorDataFrame.py b/LabWork01/LabWork6/ConvertorDataFrame.py new file mode 100644 index 0000000..5e48e05 --- /dev/null +++ b/LabWork01/LabWork6/ConvertorDataFrame.py @@ -0,0 +1,36 @@ +import pandas as pd + +def covertorDataFrame(): + + df = pd.read_csv("../../res/Stores.csv") + + countMainRows = 25 + + newDf = df.head(countMainRows) + + newDf['TextStoreArea'] = df['Store_Area'].apply( + lambda x: 'Small_Area' if x <= 1100 else ('Average_Area' if 1100 < x <= 1700 else 'Big_Area')) + + newDf['TextStoreSales'] = df['Store_Sales'].apply( + lambda x: 'Small_Sales' if x <= 50000 else ('Average_Sales' if 50000 < x <= 80000 else 'Big_Sales')) + + newDf['TextDailyCustomerCount'] = df['Daily_Customer_Count'].apply( + lambda x: 'Small_Customer' if x <= 400 else ('Average_Customer' if 400 < x <= 900 else 'Big_Customer')) + + # using dictionary to convert specific columns + convert_dict = {'Store_ID': str, + 'Store_Area': str, + 'Items_Available': str, + 'Daily_Customer_Count': str, + 'Store_Sales': str + } + + newDf = newDf.astype(convert_dict) + + print(newDf[['TextStoreSales', 'TextStoreSales', 'TextStoreArea']]) + + return newDf[['TextDailyCustomerCount', 'TextStoreArea', 'TextStoreSales']] + +# [['Store_Area', 'Store_Sales', 'Daily_Customer_Count', 'TextStoreArea']] + +# [['Store_ID', 'Store_Area', 'TextStoreArea', 'Items_Available', 'Daily_Customer_Count', 'Store_Sales']] \ No newline at end of file diff --git a/LabWork01/LabWork6/SecondTree.py b/LabWork01/LabWork6/SecondTree.py new file mode 100644 index 0000000..c1bbc43 --- /dev/null +++ b/LabWork01/LabWork6/SecondTree.py @@ -0,0 +1,78 @@ +import pandas as pd # 0 tabs +import numpy as np # 0 tabs + +class DecisionTree: # 0 tabs + def __init__(self, max_depth=None): # 1 tab + self.max_depth = max_depth # 2 tabs + self.tree = {} # 2 tabs + + def entropy(self, y): # 1 tab + classes, counts = np.unique(y, return_counts=True) # 2 tabs + probabilities = counts / len(y) # 2 tabs + entropy = sum(-p * np.log2(p) for p in probabilities) # 2 tabs + return entropy # 2 tabs + + def information_gain(self, X, y, feature, threshold): # 1 tab + left_indices = X[:, feature] < threshold # 2 tabs + right_indices = ~left_indices # 2 tabs + left_entropy = self.entropy(y[left_indices]) # 2 tabs + right_entropy = self.entropy(y[right_indices]) # 2 tabs + left_weight = np.sum(left_indices) / len(y) # 2 tabs + right_weight = 1 - left_weight # 2 tabs + gain = self.entropy(y) - (left_weight * left_entropy + right_weight * right_entropy) # 2 tabs + return gain # 2 tabs + + def best_split(self, X, y): # 1 tab + best_feature = None # 2 tabs + best_threshold = None # 2 tabs + best_gain = 0 # 2 tabs + for feature in range(X.shape[1]): # 2 tabs + thresholds = np.unique(X[:, feature]) # 3 tabs + for threshold in thresholds: # 3 tabs + gain = self.information_gain(X, y, feature, threshold) # 4 tabs + if gain > best_gain: # 4 tabs + best_gain = gain # 5 tabs + best_feature = feature # 5 tabs + best_threshold = threshold # 5 tabs + return best_feature, best_threshold # 2 tabs + + def build_tree(self, X, y, depth=0): # 1 tab + if len(np.unique(y)) == 1 or (self.max_depth is not None and depth >= self.max_depth): # 2 tabs + return {'class': np.argmax(np.bincount(y))} # 3 tabs + best_feature, best_threshold = self.best_split(X, y) # 2 tabs + left_indices = X[:, best_feature] < best_threshold # 2 tabs + right_indices = ~left_indices # 2 tabs + tree = {'feature': best_feature, 'threshold': best_threshold} # 2 tabs + tree['left'] = self.build_tree(X[left_indices], y[left_indices], depth + 1) # 2 tabs + tree['right'] = self.build_tree(X[right_indices], y[right_indices], depth + 1) # 2 tabs + return tree # 2 tabs + + def fit(self, X, y): # 1 tab + self.tree = self.build_tree(X, y) # 2 tabs + + def predict_instance(self, tree, x): # 1 tab + if 'class' in tree: # 2 tabs + return tree['class'] # 3 tabs + if x[tree['feature']] < tree['threshold']: # 2 tabs + return self.predict_instance(tree['left'], x) # 3 tabs + else: # 2 tabs + return self.predict_instance(tree['right'], x) # 3 tabs + + def predict(self, X): # 1 tab + return [self.predict_instance(self.tree, x) for x in X] # 2 tabs + +# Пример использования +data = { # 0 tabs + 'feature1': [1, 2, 3, 4, 5], # 1 tab + 'feature2': [0, 0, 1, 1, 0], # 1 tab + 'target': [0, 0, 1, 1, 1] # 1 tab +} + +df = pd.DataFrame(data) # 0 tabs +X = df[['feature1', 'feature2']].values # 1 tab +y = df['target'].values # 1 tab + +model = DecisionTree(max_depth=3) # 0 tabs +model.fit(X, y) # 1 tab +predictions = model.predict(X) # 1 tab +print(predictions) # 0 tabs \ No newline at end of file diff --git a/LabWork01/LabWork6/Tree.py b/LabWork01/LabWork6/Tree.py new file mode 100644 index 0000000..8632e60 --- /dev/null +++ b/LabWork01/LabWork6/Tree.py @@ -0,0 +1,118 @@ +import math +import pandas as pd +from functools import reduce + +from LabWork01.FuncLoad import createDataFrame +from LabWork01.LabWork6.ConvertorDataFrame import covertorDataFrame + +# Дата сет +# data = [ +# ["красный", "квадрат", "красный квадрат"], +# ["красный", "прямоугольник", "красный прямоугольник"], +# ["красный", "круг", "красный круг"], +# ["красный", "треугольник", "красный треугольник"], +# +# ["зеленый", "квадрат", "зеленый квадрат"], +# ["зеленый", "треугольник", "зеленый треугольник"], +# ["зеленый", "круг", "зеленый круг"], +# ] +# df0 = pd.DataFrame(data) +# df0.columns = ["цвет", "форма", "результат"] + +df0 = covertorDataFrame() + +# Лямбда-выражение для распределения значений, аргумент - pandas.Series, +# возвращаемое значение - массив с количеством каждого из значений +# Из вводных данных s с помощью value_counts() находим частоту каждого из значений, +# и пока в нашем словаре есть элементы, будет работать цикл, запускаемый items(). +# Чтобы выходные данные не менялись с каждым запуском цикла, мы используем sorted, +# который меняет порядок от большего к меньшему +# В итоге, генерируется массив, содержащий строку из следующих компонентов: ключ (k) и значение (v). +cstr = lambda s: [k + ":" + str(v) for k, v in sorted(s.value_counts().items())] + +# Структура данных Decision Tree +tree = { + # name: Название этого нода (узла) + "name": "decision tree " + df0.columns[-1] + " " + str(cstr(df0.iloc[:, -1])), + # df: Данные, связанные с этим нодом (узлом) + "df": df0, + # edges: Список ребер (ветвей), выходящих из этого узла, + # или пустой массив, если ниже нет листового узла. + "edges": [], +} + +# Генерацию дерева, у узлов которого могут быть ветви, сохраняем в open +open = [tree] + +# Лямба-выражение для вычесления энтропии. +# Аргумент - pandas.Series、возвращаемое значение - число энтропии +entropy = lambda s: -reduce(lambda x, y: x + y, map(lambda x: (x / len(s)) * math.log2(x / len(s)), s.value_counts())) + +# Зацикливаем, пока open не станет пустым +while (len(open) != 0): + # Вытаскиваем из массива open первый элемент, + # и вытаскиваем данные, хранящиеся в этом узле + n = open.pop(0) + df_n = n["df"] + + # В случае, если энтропия этого узла равна 0, мы больше не можем вырастить из него новые ветви + # поэтому прекращаем ветвление от этого узла + if 0 == entropy(df_n.iloc[:, -1]): + continue + # Создаем переменную, в которую будем сохранять список значений атрибута с возможностью разветвления + attrs = {} + # Исследуем все атрибуты, кроме последнего столбца класса атрибутов + for attr in df_n.columns[:-1]: + # Создаем переменную, которая хранит значение энтропии при ветвлении с этим атрибутом, + # данные после разветвления и значение атрибута, который разветвляется. + attrs[attr] = {"entropy": 0, "dfs": [], "values": []} + # Исследуем все возможные значения этого атрибута. + # Кроме того, sorted предназначен для предотвращения изменения порядка массива, + # из которого были удалены повторяющиеся значения атрибутов, при каждом его выполнении. + for value in sorted(set(df_n[attr])): + # Фильтруем данные по значению атрибута + df_m = df_n.query(attr + "=='" + value + "'") + # Высчитываем энтропию, данные и значения сохрнаяем + attrs[attr]["entropy"] += entropy(df_m.iloc[:, -1]) * df_m.shape[0] / df_n.shape[0] + attrs[attr]["dfs"] += [df_m] + attrs[attr]["values"] += [value] + pass + pass + # Если не осталось ни одного атрибута, значение которого можно разделить, + # прерываем исследование этого узла. + if len(attrs) == 0: + continue + # Получаем атрибут с наименьшим значением энтропии + attr = min(attrs, key=lambda x: attrs[x]["entropy"]) + # Добавляем каждое значение разветвленного атрибута + # и данные, полученные после разветвления, в наше дерево и в open. + for d, v in zip(attrs[attr]["dfs"], attrs[attr]["values"]): + m = {"name": attr + "=" + v, "edges": [], "df": d.drop(columns=attr)} + n["edges"].append(m) + open.append(m) + pass + +# Выводим дата сет +print(df0, "\n-------------") + + +# Метод преобразования дерева в символы, аргуметы - tree:структура данных древа, +# indent:присоединяймый к дочернему узлу indent, +# Возвращаемое значение - символьное представление древа. +# Этот метод вызывается рекурсивно для преобразования всех данных в дереве в символы. +def tstr(tree, indent=""): + # Создаем символьное представление этого узла. + # Если этот узел является листовым узлом (количество элементов в массиве ребер равно 0), + # частотное распределение последнего столбца данных df, связанных с деревом, преобразуется в символы. + s = indent + tree["name"] + str(cstr(tree["df"].iloc[:, -1]) if len(tree["edges"]) == 0 else "") + "\n" + # Зацикливаем все ветви этого узла. + for e in tree["edges"]: + # Добавляем символьное представление дочернего узла к символьному представлению родительского узла. + # Добавляем еще больше символов к indent этого узла. + s += tstr(e, indent + " ") + pass + return s + + +# Выводим древо в его символьном представлении. +print(tstr(tree)) diff --git a/LabWork01/LoadDB.py b/LabWork01/LoadDB.py index 46923ed..da8b867 100644 --- a/LabWork01/LoadDB.py +++ b/LabWork01/LoadDB.py @@ -14,6 +14,7 @@ from LabWork01.LabWork3.CustomGraphics import createCusGraphics from LabWork01.LabWork3.DeletePng import deleteAllPng from LabWork01.LabWork4.SiteSearch import SiteSearch from LabWork01.LabWork5.create_plot import create_plot_jpg +from LabWork01.LabWork6.ConvertorDataFrame import covertorDataFrame app = Flask(__name__) @@ -46,6 +47,7 @@ search_engine.add("https://www.kaggle.com/datasets/fedesoriano/stroke-prediction @app.route("/") def home(): + covertorDataFrame(listShops) return render_template('main_page.html', context=[], main_img=[], messages=[], image_names=[], tableAnalys=[], titles=[''], listTypes=listTypes, countNull=countNull, firstRow=1, secondRow=4, firstColumn=1, secondColumn=4) @app.route("/showDiapason", methods=['GET','POST'])