# Univerzitet u Novom Sadu, Fakultet tehničkih nauka, Novi Sad, Srbija
# Studijski program OAS Informacioni inženjering
# Predmet Metode i tehnike nauke o podacima

# Pomoćni sadržaj

# Korišćeni podaci
#
# Skup podataka "Oranges, Lemons and Apples dataset"
# - Internet sajt Škole za informatiku Univerziteta u Edinburgu 
#   (Velika Britanija)
# - podaci o merenjima voća
#   - autor podataka: Ijan Mari (Škola za informatiku, Univerzitet u 
#     Edinburgu, Velika Britanija)
#   - vreme nastanka podataka: 2006.
#   - format podataka: TSV
#
# Internet strana:
# https://homepages.inf.ed.ac.uk/imurray2/teaching/oranges_and_lemons/


# %% Biblioteke

import numpy as np
import pandas as pd


# %% Generator vrednosti

gpsv = np.random.default_rng(78)


# %% Klasa

class Perceptron:
    def __init__(self, veličina_ulaza, broj_neurona, generator):
        self.težine = generator.uniform(
            -0.1, 0.1, (broj_neurona, veličina_ulaza))
        self.pragovi = generator.random(broj_neurona)
        self.veličina_ulaza = veličina_ulaza
        self.broj_neurona = broj_neurona        
        self.fakt = lambda x : 1 if x >= 0 else 0    
        self.fakt_v = np.vectorize(self.fakt)
    
    def računanje_izlaza(self, ulaz):
        return self.fakt_v((self.težine @ ulaz) + self.pragovi)
     
    def obučavanje(self, ulaz, oznaka):
        izlaz = self.računanje_izlaza(ulaz)
        greška = oznaka - izlaz        
        self.težine = self.težine + (
            np.reshape(greška, (self.broj_neurona, 1)) @ 
            np.reshape(ulaz, (1, self.veličina_ulaza)))
        self.pragovi = self.pragovi + greška


# %% Upotreba

podaci = pd.read_csv("fruit_data", sep="\s+", header=None, 
                     names=["vrsta", "težina", "visina", "širina"])

print("podaci \n", podaci)

vrsta_jabuka = [1, 3, 4, 5]
vrsta_narandža = [6, 7, 8]

podaci_odabir = podaci.loc[podaci["vrsta"].isin(
    vrsta_jabuka + vrsta_narandža), :]

podaci_odabir.insert(
    0, "oznaka", np.repeat(-1, podaci_odabir.shape[0]))

podaci_odabir.loc[
    podaci_odabir["vrsta"].isin(vrsta_jabuka), ["oznaka"]] = 0

podaci_odabir.loc[
    podaci_odabir["vrsta"].isin(vrsta_narandža), ["oznaka"]] = 1

print("podaci odabrani \n", podaci_odabir)

ob_ulaz = ["težina", "visina", "širina"]
ob_cilj = ["oznaka"]

podaci_odabir_obučavanje = podaci_odabir.sample(
    frac=0.75, random_state=gpsv.bit_generator)
podaci_odabir_obučavanje_ulaz = podaci_odabir_obučavanje.get(ob_ulaz)

podaci_odabir_obučavanje_ulaz = (podaci_odabir_obučavanje_ulaz - 
podaci_odabir_obučavanje_ulaz.min()) / (podaci_odabir_obučavanje_ulaz.max() - 
                                        podaci_odabir_obučavanje_ulaz.min())

podaci_odabir_obučavanje_cilj = podaci_odabir_obučavanje.get(ob_cilj)

podaci_odabir_validacija = podaci_odabir.drop(
    index=podaci_odabir_obučavanje.index)
podaci_odabir_validacija_ulaz = podaci_odabir_validacija.get(ob_ulaz)

podaci_odabir_validacija_ulaz = (podaci_odabir_validacija_ulaz - 
podaci_odabir_validacija_ulaz.min()) / (podaci_odabir_validacija_ulaz.max() -
                                          podaci_odabir_validacija_ulaz.min())

podaci_odabir_validacija_cilj = podaci_odabir_validacija.get(ob_cilj)

veličina_ulaza = 3
broj_neurona = 1

mreža = Perceptron(veličina_ulaza, broj_neurona, gpsv)

for e in range(20):
    for i in range(podaci_odabir_obučavanje.shape[0]):
        ulaz = podaci_odabir_obučavanje_ulaz.iloc[i].values
        oznaka = podaci_odabir_obučavanje_cilj.iloc[i].values
        mreža.obučavanje(ulaz, oznaka)

ispravno_obučavanje = 0

for i in range(podaci_odabir_obučavanje.shape[0]):
    ulaz = podaci_odabir_obučavanje_ulaz.iloc[i].values
    oznaka = podaci_odabir_obučavanje_cilj.iloc[i].values
    izlaz = mreža.računanje_izlaza(ulaz)
    if (oznaka == izlaz).all():
        ispravno_obučavanje += 1

print("obučavanje: ispravno {} od {}".format(
    ispravno_obučavanje, podaci_odabir_obučavanje.shape[0]))

ispravno_validacija = 0

for i in range(podaci_odabir_validacija.shape[0]):
    ulaz = podaci_odabir_validacija_ulaz.iloc[i].values
    oznaka = podaci_odabir_validacija_cilj.iloc[i].values
    izlaz = mreža.računanje_izlaza(ulaz)
    if (oznaka == izlaz).all():
        ispravno_validacija += 1

print("validacija: ispravno {} od {}".format(
    ispravno_validacija, podaci_odabir_validacija.shape[0]))

