diff --git a/Justyna.md b/Justyna.md new file mode 100644 index 0000000..a55d03b --- /dev/null +++ b/Justyna.md @@ -0,0 +1,77 @@ +# Drzewa decyzyjne, algorytm ID3 + +### autor Justyna Zarzycka + +## Opis projektu +Projekt implementuje tworzenie drzewa decyzyjnego wykorzystującego algorytm ID3, ktióre pomaga określić chęci do pracy agenta na podstawie warunków panujących na planszy. + +### Tworzenie drzewa decyzyjnego + +funkcja budująca drzewo za pomocą algorymu ID3: + +```py +def ID3(data, original_data, features, target_attribute_name, parent_node_class=None): + + item_values = [info_gain(data, feature, target_attribute_name) for feature in + features] + best_feature_index = np.argmax(item_values) + best_feature = features[best_feature_index] + tree = {best_feature: {}} + features = [i for i in features if i != best_feature] + for value in np.unique(data[best_feature]): + + sub_data = data.where(data[best_feature] == value).dropna() + subtree = ID3(sub_data, data, features, target_attribute_name, parent_node_class) + + tree[best_feature][value] = subtree + + return (tree) +``` + +Drzewo budowane jest w oparciu o najlepsze możliwe podziały: + +```py +#obliczanie wartości przyrostu informacji +def info_gain(data, split_attribute_name, target_name): + + # Wartość entropii zbioru + total_entropy = entropy(data[target_name]) + + # Wyodrębnienie poszczególnych "podzbiorów" + vals, counts = np.unique(data[split_attribute_name], return_counts=True) + + # Średnia ważona entropii każdego podzbioru + weighted_entropy = np.sum( + [(counts[i] / np.sum(counts)) * entropy(data.where(data[split_attribute_name] == vals[i]).dropna()[target_name]) + for i in range(len(vals))]) + + # Przyrost informacji + information_gain = total_entropy - weighted_entropy + + return information_gain +``` + +Entropia: + +```py +def entropy(target_col): + values, counts = np.unique(target_col, return_counts=True) + entropy = np.sum( + [(-counts[i] / np.sum(counts)) * np.log2(counts[i] / np.sum(counts)) for i in range(len(values))]) + return entropy +``` + +### Zestaw uczący + +Zestaw budujący drzewo to lista zawierająca 20 przykładów waruków panujących na polu. Atrybyty zapisane są w formacie ['pogoda', 'ile_chwastow', 'ile_burakow', 'czy_chce_pracowac']. + +```py + ['slonecznie', 'duzo', 'bardzo_malo', 'srednio'], + ['deszcz', 'bardzo_duzo', 'malo', 'nie'], + ['grad', 'bardzo_duzo', 'bardzo_malo', 'nie'], + ['zachmurzenie', 'srednio', 'srednio', 'tak'] +``` + +### Implementacja w projekcie +Podprojet uruchamiany jest za pomocą klawisza *F5*. + diff --git a/Justyna.py b/Justyna.py index 5cad369..2c01aa3 100644 --- a/Justyna.py +++ b/Justyna.py @@ -1,3 +1,122 @@ +import pandas as pd +import numpy as np +from pprint import pprint +import time +import dataset +import random + +# obliczenie entropii dla wskazanej kolumny +def entropy(target_col): + values, counts = np.unique(target_col, return_counts=True) + entropy = np.sum( + [(-counts[i] / np.sum(counts)) * np.log2(counts[i] / np.sum(counts)) for i in range(len(values))]) + return entropy + +#obliczanie wartości przyrostu informacji +def info_gain(data, split_attribute_name, target_name): + + # Wartość entropii zbioru + total_entropy = entropy(data[target_name]) + + # Wyodrębnienie poszczególnych "podzbiorów" + vals, counts = np.unique(data[split_attribute_name], return_counts=True) + + # Średnia ważona entropii każdego podzbioru + weighted_entropy = np.sum( + [(counts[i] / np.sum(counts)) * entropy(data.where(data[split_attribute_name] == vals[i]).dropna()[target_name]) + for i in range(len(vals))]) + + # Przyrost informacji + information_gain = total_entropy - weighted_entropy + + return information_gain + + +def ID3(data, original_data, features, target_attribute_name, parent_node_class=None): + + + # Jeżeli wszystkie atrybuty są takie same, zwracamy liść z pierwszą napotkaną wartością + + if len(np.unique(data[target_attribute_name])) <= 1: + return np.unique(data[target_attribute_name])[0] + + elif len(data) == 0: + return np.unique(original_data[target_attribute_name])[ + np.argmax(np.unique(original_data[target_attribute_name], return_counts=True)[1])] + + elif len(features) == 0: + return parent_node_class + + else: + + # Aktualizacja nadrzędnej wartości + parent_node_class = np.unique(data[target_attribute_name])[ + np.argmax(np.unique(data[target_attribute_name], return_counts=True)[1])] + + # Obliczenie przyrostu informacji dla każdego potencjalnego atrybutu, + # według którego nastąpi podział zbioru + item_values = [info_gain(data, feature, target_attribute_name) for feature in + features] + + # Najlepszym atrybutem jest ten o największym przyroście informacji + best_feature_index = np.argmax(item_values) + best_feature = features[best_feature_index] + + # Struktura drzewa + tree = {best_feature: {}} + + # Aktualizacja zbioru atrybutów + features = [i for i in features if i != best_feature] + + # Dla każdej wartości wybranego atrybutu budujemy kolejne poddrzewo + for value in np.unique(data[best_feature]): + + sub_data = data.where(data[best_feature] == value).dropna() + subtree = ID3(sub_data, data, features, target_attribute_name, parent_node_class) + + tree[best_feature][value] = subtree + + return (tree) + +#tesownie drzewa +def test(data, tree): + queries = data.iloc[:, :-1].to_dict(orient="records") + + predicted = pd.DataFrame(columns=["predicted"]) + + for i in range(len(data)): + predicted.loc[i, "predicted"] = search(queries[i], tree, 'nie') + print('Precyzja przewidywań: ', (np.sum(predicted["predicted"] == data['czy_chce_pracowac']) / len(data)) * 100, '%') + +#dostowanie danych (lista na słownik) i wywolanie na nich funkcji serach +def predict_data(data, tree): + + queries = pd.DataFrame(data=data, columns=dataset.header) + predicted = pd.DataFrame(columns=["predicted"]) + dict = queries.iloc[:, :-1].to_dict(orient="records") + + for i in range(len(data)): + predicted.loc[i, "predicted"] = search(dict[i], tree, 'nie') + + predicted_list = predicted.values.tolist() + return predicted_list[0][0] + +#przeszukwianie drzewa +def search(query, tree, default='nie'): + + for key in list(query.keys()): + if key in list(tree.keys()): + try: + result = tree[key][query[key]] + except: + return default + result = tree[key][query[key]] + if isinstance(result, dict): + return search(query, result) + + else: + return result + class main(): def __init__(self,traktor,field,ui,path): self.traktor = traktor @@ -6,4 +125,72 @@ class main(): self.path = path def main(self): - pass \ No newline at end of file + training_data = pd.DataFrame(data=dataset.training_data, columns=dataset.header) + testing_data = pd.DataFrame(data=dataset.testing_data, columns=dataset.header) + + #utworzenie drzewa + tree = ID3(training_data, training_data, training_data.columns[:-1], 'czy_chce_pracowac') + pprint(tree) + + #uzyskanie danych od agenta + ocena_burakow = self.ocen_ile_burakow() + ocena_chwastow = self.ocen_ile_chwastow() + pogoda = self.pogoda() + print('chwasty: ' + ocena_chwastow) + print('buraki: ' + ocena_burakow) + print('pogoda: ' + pogoda) + data = [[pogoda, ocena_chwastow, ocena_burakow, '']] + + #podjecie decyzji + result = predict_data(data, tree) + print('czy traktor chce pracowac: ' + result) + + + def licz_chwasty_buraki(self): + chwasty = 0 + buraki = 0 + + for i in self.field.field_matrix: + for j in i: + if(j==8): + buraki = buraki + 1 + elif(j%2==1): + chwasty = chwasty + 1 + return chwasty, buraki + + def ocen_ile_burakow(self): + chwasty, buraki = self.licz_chwasty_buraki() + if buraki < 5: + return 'bardzo_malo' + elif buraki >= 5 and buraki<10: + return 'malo' + elif buraki >=10 and buraki<15: + return 'srednio' + elif buraki >=15 and buraki<20: + return 'duzo' + elif buraki >=20: + return 'bardzo_duzo' + + def ocen_ile_chwastow(self): + chwasty, buraki = self.licz_chwasty_buraki() + if chwasty < 40: + return 'bardzo_malo' + elif chwasty >= 40 and chwasty<42: + return 'malo' + elif chwasty >=42 and chwasty<45: + return 'srednio' + elif chwasty >=45 and chwasty<48: + return 'duzo' + elif chwasty >=48: + return 'bardzo_duzo' + + def pogoda(self): + number = random.randrange(0, 4) + if number==0: + return 'slonecznie' + elif number==1: + return 'deszcz' + elif number==2: + return 'grad' + elif number==3: + return 'zachmurzenie' \ No newline at end of file diff --git a/dataset.py b/dataset.py new file mode 100644 index 0000000..9d9d311 --- /dev/null +++ b/dataset.py @@ -0,0 +1,35 @@ + +header = ['pogoda', 'ile_chwastow', 'ile_burakow', 'czy_chce_pracowac'] + +training_data = [ + ['slonecznie', 'duzo', 'malo', 'tak'], + ['slonecznie', 'srednio', 'srdenio', 'tak'], + ['slonecznie', 'bardzo_duzo', 'malo', 'srednio'], + ['slonecznie', 'malo', 'bardzo_duzo', 'tak'], + ['slonecznie', 'duzo', 'bardzo_malo', 'srednio'], + ['deszcz', 'bardzo_duzo', 'malo', 'nie'], + ['deszcz', 'srednio', 'srednio', 'srednio'], + ['deszcz', 'malo', 'bardzo_duzo', 'tak'], + ['deszcz', 'duzo', 'duzo', 'srednio'], + ['deszcz', 'malo', 'malo', 'tak'], + ['grad', 'srednio', 'malo', 'nie'], + ['grad', 'bradzo_malo', 'bardzo_duzo', 'srednio'], + ['grad', 'duzo', 'srednio', 'nie'], + ['grad', 'malo', 'bardzo_malo', 'srednio'], + ['grad', 'bardzo_duzo', 'bardzo_malo', 'nie'], + ['zachmurzenie', 'srednio', 'srednio', 'tak'], + ['zachmurzenie', 'bardzo_duzo', 'duzo', 'nie'], + ['zachmurzenie', 'malo', 'srednio', 'tak'], + ['zachmurzenie', 'duzo', 'malo', 'srednio'], + ['zachmurzenie', 'malo', 'bardzo_malo', 'tak'] +] + +testing_data = [ + ['slonecznie', 'srednio', 'srednio', 'tak'], + ['deszcz', 'duzo', 'malo', 'nie'], + ['deszcz', 'duzo', 'duzo', 'srednio'], + ['grad', 'srednio', 'duzo', 'nie'], + ['zachmurzenie', 'bardzo_duzo', 'malo', 'nie'], + ['zachmurzenie', 'bardzo_duzo', 'malo', 'nie'] +] +