import pandas as pd  #biblioteka za rad sa podacima

#u biblioteci sklearn su implementirane klase i funkcije za istrazivanje podataka
from sklearn.tree import DecisionTreeClassifier, export_graphviz
from sklearn.model_selection import train_test_split
import sklearn.metrics as met  #modul metrics sadrzi f-je za evaluaciju modela za klasifikaciju

import subprocess

def calculate_metrics(part, true_values, predicted_values, class_names):
    """
    part: opis skupa
    true_values: prave klase instanci
    predicted_values: dodeljene klase instanci
    class_names: imena klasa (za pravljenje tabela radi lepseg prikaza rezultata)
    """

    print('Skup ', part)
    print('Matrica konfuzije')
    cnf_matrix = met.confusion_matrix(true_values, predicted_values)
    df_cnf_matrix = pd.DataFrame(cnf_matrix, index=class_names, columns=class_names)
    print(df_cnf_matrix)
    print('\n')

    accuracy = met.accuracy_score(true_values, predicted_values)
    print('Preciznost', accuracy)
    print('\n')

    accuracy = met.accuracy_score(true_values, predicted_values, normalize=False)
    print('Preciznost u broju instanci', accuracy)
    print('\n')

    class_report = met.classification_report(true_values, predicted_values)
    print('Izvestaj klasifikacije', class_report, sep='\n')


#f-ja za pravljenje slike drveta odlucivanja
def visualize_tree(tree, feature_names, class_names):
    """
    tree: objekat klase Tree
    feature_names: imena atributa koji su korisceni pri pravljenju modela
    class_names: vrednosti u ciljnom atributu
    """
    with open("dt.dot", "w") as f:
        #f-ja export_graphviz opisuje drvo odlucivanja u dot formatu
        export_graphviz(tree, out_file=f,
                        feature_names=feature_names,
                        class_names=class_names,
                        filled=True,
                        rounded=True)

    f.close()

    #na osnovu opisa drveta odlucivanja u dot formatu pravi se slika u png formatu
    subprocess.call("dot -Tpng dt.dot -o tree.png", shell=True)

#ucitavanje podataka iz datoteke u csv formatu i
#pravljenje tabele, tj. objekta klase DataFrame
df = pd.read_csv("iris.csv")

#deo sa upoznavanjem i pripremom podataka

print('Prvih 5 instanci iz skupa', df.head(), sep='\n')
print('\n')

print('Opis podataka (deskriptivne statistike)', df.describe(include='all'), sep='\n')
print('\n')

#provera da li postoje nedostajuce vrednosti u skupu, jer ako
#postoje moraju biti obradjene
print('Postojanje nedostajuce vrednosti', df.isna().any().any())
print('\n')

#posto je korelacija izmedju Petal_Width i Petal_Length 0.96,
#jedan od ova dva atributa se moze izostaviti pri pravljenju modela
print('Korelacije izmedju atributa', df.corr(), sep='\n')
print('\n')

features = df.columns[:4].tolist()

#ako se eliminise jedan atribut zbog korelacije, npr. Petal_Length
#features.remove('Petal_Length')

# deo sa klasifikacijom

#Posto fja train_test_split kao prvi parametar prima opis instanci (bez ciljnog atributa),
#a kao drugi parametar listu sa klasama instanci koje su zadate kao prvi argument,
#ucitani skup se prema tim potrebama deli na dva dela

print('Atributi za pravljenje modela', features)
print('\n')

x=df[features]  #opis instanci (skup sadrzi samo atribute koji se koriste za pravljenje modela)
y=df["Species"] #skup sa ciljnim atributom

"""
f-ja train_test_split kao rezultat vraca
    x_train: instance u trening skupu (bez ciljnog atributa)
    x_test: instance u test skupu (bez ciljnog atributa)
    y_train: klase instanci u trening skupu
    y_test: klase instanci u test skupu
"""
x_train, x_test, y_train, y_test = train_test_split(x, y, train_size=100, stratify=y)

#pravljenje klasifikatora koriscenjem klase DecisionTreeClassifier (u slajdovima je detaljnije opisana)
dt = DecisionTreeClassifier()
#dt = DecisionTreeClassifier(criterion='entropy')
#dt = DecisionTreeClassifier(max_depth=2)
#dt = DecisionTreeClassifier(min_samples_split=20, max_depth=4)
#dt = DecisionTreeClassifier(min_samples_split=20, max_depth=4, max_leaf_nodes=4)
#dt = DecisionTreeClassifier(min_impurity_split=0.05)

#pravljenje modela na osnovu trening skupa
dt.fit(x_train, y_train)

print('Klase', dt.classes_)
print('Znacajnost atributa', pd.Series(dt.feature_importances_, index=features), sep='\n')

# pravljenje skupa koji sadrzi oznaku instance i
# verovatnocu pripadnosti svakoj od klasa
x_proba =  pd.DataFrame(dt.predict_proba(x_test),
                        index= x_test.index,
                        columns=['prob_' + x for x in dt.classes_])

#spajanje skupa sa podacima o instancama i skupa sa verovatnocama,
#spajanje se vrsi po oznakama redova
x_test_proba = pd.concat([x_test, x_proba], axis=1)
print('Verovatnoca pripadnosti svakoj od klasa za prvih 10 instanci trening skupa')
print(x_test_proba.head(10))

#graficki prikaz drveta
visualize_tree(dt, features, dt.classes_)

#primena modela na trening podatke
y_pred = dt.predict(x_train)
calculate_metrics('Trening ',y_train, y_pred, dt.classes_ )

#primena modela na test podatke
y_pred = dt.predict(x_test)
calculate_metrics('Test',y_test, y_pred, dt.classes_ )


