## Logisticka regresija

# Pri istrazivanju eksplozije rakete USS Challenger 1986. doslo je do zakljucka
# da je specifičan O-prsten otkazao. Inženjeri su smatrali da verovatnoca
# otkazivanja ima veze sa spoljnom temperaturom pri lansiranju.
challenger <- read.delim('challenger.txt') #ucitavanje podataka iz txt fajla

# Pregled podataka, temp je temperatura u Fahrenheitu, a oring je podatak da li
# je došlo do otkazivanja.
View(challenger)


# Logistička regresija modeluje verovatnocu jedne klase binarne promenljive, u
# ovom slučaju verovatnocu otkazivanja pomenutog O-prstena.
# Funkcija glm() sluzi za pravljenje generalizovanih linearnih modela, koji
# predstavljaju modifikacije linearne regresije za razlicite uslove.
# Sa family='binomial' naglasavamo da je ciljna promenljiva binarna, i pravimo
# model koji predvidja verovatnocu.
logistic_model <- glm(oring ~ temp, data = challenger, family = 'binomial') 
summary(logistic_model)
# Slican prikaz kao i kod linearne regresije, u tabeli imamo prikaz vrednosti
# individualnih koeficijenata kao i testove njihove znacajnosti (z-test).
# AIC(Akaike information criterion) je mera koja može da se koristi za
# uporedjivanje vise modela nad istim podacima. Manja vrednost je bolja.

# Prikaz dobijenog modela:
logistic_model$fitted.values #verovatnoce koje model predvidja
beta_0 = logistic_model$coefficients[1]
beta_1 = logistic_model$coefficients[2]
plot(oring ~ temp, data = challenger)
xn = seq(50, 90, 0.1)
yn = predict(logistic_model, data.frame(temp = xn), type = 'response')
points(xn, yn, type = "l", col = "blue", lwd = 2) #model
abline(v = -beta_0/beta_1, col = "red", lwd = 2) #granicna linija modela (beta_0 + beta_1*x = 0)
# Sve levo od crvene linije ima verovatnoću > 0.5, pa bismo klasifikovali kao 1
# (predvidjamo otkazivanje). Desno je verovatnoca < 0.5 (predvidjamo da nece
# doci do otkazivanja).
?predict.glm # za informacije



# Logisticka regresija sa vise atributa
library(mlbench)
data("PimaIndiansDiabetes")
data = PimaIndiansDiabetes
?PimaIndiansDiabetes #informacije o bazi
View(data)


# Kada sa desne strane izraza za formulu stavimo tacku, govorimo R-u da uzme sve
# ostale kolone iz baze za prediktore.
full_model <- glm(diabetes ~ ., data = data, family = 'binomial')
summary(full_model)

# Pravimo model od dva najznacajnija atributa (po p-vrednostima testa znacajnosti),
# radi vizuelizacije.
model <- glm(diabetes ~ glucose + mass, data = data, family = 'binomial')
summary(model)



library(ggplot2)
predicted_probabilities = model$fitted.values
data$predictions = predicted_probabilities
# Prikazujemo na grafiku dva atributa, a bojom označavamo klasu (ciljnu promenljivu)
ggplot(data = data, aes(x = glucose, y = mass, color = as.factor(diabetes))) +
  geom_point()

# Mozemo da vidimo tacke sa 0 vrednostima za mass i za glucose. Ove tacke su
# dobar primer autlajere koji treba da se izbace. Konkretno, ove vrednosti su
# nedostajuce, ali su upisane kao 0. Zbog toga cemo da ih izbacimo, ponovo
# istreniramo model i prikazemo grafik.
idx_to_remove = which(data$mass == 0 | data$glucose == 0)
data = data[-idx, ]
model <- glm(diabetes ~ glucose + mass, data = data, family = 'binomial')
predicted_probabilities = model$fitted.values
data$predictions = predicted_probabilities
ggplot(data = data, aes(x = glucose, y = mass, color = as.factor(diabetes))) +
  geom_point()


# Prikazujemo sad bojom verovatnoću koju model predviđa
data$predictions = predicted_probabilities
p = ggplot(data = data, aes(x = glucose, y = mass, color = predictions)) + 
  geom_point() +
  scale_color_gradientn(colors = c("red","white","blue"), limits = c(0,1))
plot(p)

# Dodajemo graničnu liniju predikcije (p = 0.5) na grafik
beta_0 = model$coefficients[1]
beta_1 = model$coefficients[2]
beta_2 = model$coefficients[3]
p + geom_abline(intercept = -beta_0/beta_2, slope = -beta_1/beta_2, lwd = 1, col = 'green')



## KNN - K najblizih suseda
# U 'FNN' biblioteci imamo podrsku za KNN algoritam
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
# predvidjanje.

# Posto KNN uzima u obzir euklidsko rastojanje tacaka, jako je bitno da su nam
# prediktora na istoj skali, jer ce inace oni na manjoj skali da budu potpuno
# zanemareni.
# Skaliramo podatke tako sto standardizujemo svaki prediktor - oduzmemo mu
# srednju vrednosti i podelimo sa standardnom devijacijom.
means = apply(X, 2, mean)
sds = apply(X, 2, sd)
X_scaled = scale(X, means, sds)

# Pokazujemo kako se vrsi predvidjanje knn algoritmom. Vrsimo predvidjanje nad
# podacima nad kojima vrsimo i trening.
knn_model_predictions <- knn(train = X_scaled, test = X_scaled, cl = y, k = 1)
mean(y == knn_model_predictions) #tacnost modela
# Vidimo da je tacnost skoro 100% za k=1, 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

# Posto smo skalirali X, moramo i resetku da skaliramo sa istim parametrima,
# inace prediktori nisu u odgovarajucim 'mernim jedinicama'.
df_dense_scaled = scale(df_dense, means, sds)

# Vrsimo predvidjanje (isprobati za razlicite vrednosti k, da se vidi kako se
# razlikuje model):
dense_predictions = knn(train = X_scaled, test = df_dense_scaled, cl = y, k = 1)

# Iscrtavanje:
df_dense$prediction = dense_predictions
ggplot(data = df_dense, 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.

