library(mlbench)
library(ggplot2)

# Ucitavamo isti skup podataka kao i sa proslog casa
data(PimaIndiansDiabetes)
data = PimaIndiansDiabetes
View(data)
plot(data$glucose, data$mass) #izdvajamo dva bitna atributa radi vizuelizacije

# U 'FNN' biblioteci imamo podrsku za algoritam k najblizih suseda (KNN)
library(FNN)
# Za koriscenje bibliotecke funkcije je bitno da su nam razdvojeni atributi i ciljna promenljiva
X <- data[,c(2,6)] #izdvajamo kolone glucose i mass kao prediktore za model
y <- as.numeric(data$diabetes) - 1 #kategoricku ciljnu promenljivu pretvaramo u numericku (nije obavezno)
?knn #vidimo zapis funkcije, odnosno kako treba da je zovemo
# Za razliku od prethodnih modela, KNN nema fazu treninga, vec se direktno radi predikcija
knn_model_predictions <- knn(train = X, test = X, cl = y, k=1)
mean(y == knn_model_predictions) #tacnost modela
# Vidimo da je tacnost skoro 100%, razlog za ovo je sto je k=1 u modelu, a najblizi sused bilo kojoj tacki je ona sama
# Jedini razlog zasto nije bas 100% je posto imamo vise slucajeva podataka sa potpuno jednakim mass i glucose vrednostima
# Ovo je jedan ekstreman primer preprilagodjavanja
# Pokazuje zasto ne mozemo da evaluiramo model na istom skupu nad kojim ga pravimo, odnosno treniramo 


## Vizuelizacija modela
# Generisemo 'resetku' (grid) podataka tako da su gusto rasporedjeni u dve dimenzije
glucose_dense = seq(-5, 205, length.out = 100)
mass_dense = seq(-5, 70, length.out = 100)
df_dense = expand.grid(glucose = glucose_dense, mass = mass_dense) #radi Dekartov proizvod dva skupa, u R-u dva vektora
nrow(df_dense) #Imamo 10000 podataka, 100x100 resetku u 2d prostoru

dense_predictions = knn(train = X, test = df_dense, cl = y, k = 11) #Predikcija nad generisanim tackama
df = df_dense
df$prediction = dense_predictions
ggplot(data = df, aes(x = glucose, y = mass, color = as.factor(prediction))) +
  geom_point() #iscrtavanje
# Vidimo da je KNN dosta komplikovan model po tome kako deli prostor na klase
# Pustanjem koda iznad ponovo za razlicite vrednosti k vidimo da je model jednostavniji, sto je vece k
# Sto je vece k, to se model manje preprilagodjava podacima


## Ceo proces pravljenja i evaluacije modela
# Pravimo model ovaj put nad svim atributima

y = as.numeric(data[,9])-1
X = data[,-9]

# Podela na trening i test
train_indexes = sample(1:nrow(X), size = floor(0.7*nrow(X))) #uzimamo 70% nasumicnih indeksa za trening skup

X_train = X[train_indexes, ]
X_test = X[-train_indexes, ]

y_train = y[train_indexes]
y_test = y[-train_indexes]


# Skaliranje podataka
# Iako ne pravi razliku za linearnu i logisticku regresiju, skaliranje je jako bitno za KNN
# Razlog je to sto KNN razdaljinu meri Euklidskom razdaljinom, pa razlicite skale atributa cine da imaju razlicit uticaj na model
# Bitan detalj! Srednje vrednosti i standardne devijacije racunamo na trening skupu, a primenjujemo za skaliranje i trening i test skupa
means = colMeans(X_train)
stds = apply(X_train,2, sd)
X_train_scaled = scale(X_train, center = means, scale = stds)
X_test_scaled = scale(X_test, center = means, scale = stds)

# Sada mozemo da napravimo predikciju modelom 
# Stavljamo prob = TRUE da bi model izbacio i verovatnoce, da bismo mogli da ih koristima za ROC krivu
knn_model_predictions = knn(X_train, X_test, cl = y_train, k = 1, prob = TRUE)
knn_predicted_proba = attributes(knn_model_predictions)$prob

confusion_matrix = table(y_test, knn_model_predictions)
confusion_matrix

# Accuracy (tacnost) mozemo iz matrice konfuzije da izracunamo
accuracy = (confusion_matrix[2,2] + confusion_matrix[1,1])/(confusion_matrix[2,2] + confusion_matrix[1,1] + confusion_matrix[2,1] + confusion_matrix[1,2])
accuracy
mean(y_test == knn_model_predictions) #a mozemo i lakse ovako

# Verovatnoce koje model izbacuje su u nestandardnom formatu
# Parce koda ispod samo pretvara p u 1-p u slucaju da je predvidjena negativna klasa (klasa 0)
knn_predicted_proba = (knn_model_predictions == 1)*knn_predicted_proba + (knn_model_predictions == 0)*(1 - knn_predicted_proba)


library(pROC)
roc_curve = roc(response = y_test, predictor = knn_predicted_proba)
plot(roc_curve)
auc(roc_curve)



