##### Rozdział 4: Naiwny klasyfikator bayesowski --------------------

## Przykład: filtrowanie spamu SMS ----
## Etap 2: badanie i przygotowywanie danych ---- 

# wczytujemy dane sms do ramki danych
sms_raw <- read.csv("sms_spam.csv")

# badamy strukturę danych sms
str(sms_raw)

# przekształcamy spam/ham w czynnik
sms_raw$type <- factor(sms_raw$type)

# dokładniej badamy zmienną type
str(sms_raw$type)
table(sms_raw$type)

# budujemy korpus za pomocą pakietu do eksploracji tekstu (tm)
library(tm)
sms_corpus <- VCorpus(VectorSource(sms_raw$text))

# badamy korpus SMS-ów
print(sms_corpus)
inspect(sms_corpus[1:2])

as.character(sms_corpus[[1]])
lapply(sms_corpus[1:2], as.character)

# oczyszczamy korpus za pomocą funkcji tm_map()
sms_corpus_clean <- tm_map(sms_corpus, content_transformer(tolower))

# pokazujemy różnicę między sms_corpus a sms_corpus_clean
as.character(sms_corpus[[1]])
as.character(sms_corpus_clean[[1]])

sms_corpus_clean <- tm_map(sms_corpus_clean, removeNumbers) # usuwamy liczby
sms_corpus_clean <- tm_map(sms_corpus_clean, removeWords, stopwords()) # usuwamy słowa stopu
sms_corpus_clean <- tm_map(sms_corpus_clean, removePunctuation) # usuwamy interpunkcję

# wskazówka: tworzymy niestandardową funkcję, aby zastępować (a nie usuwać) interpunkcję
removePunctuation("hello...world")
replacePunctuation <- function(x) { gsub("[[:punct:]]+", " ", x) }
replacePunctuation("hello...world")

# ilustracja tematyzacji słów
library(SnowballC)
wordStem(c("learn", "learned", "learning", "learns"))

sms_corpus_clean <- tm_map(sms_corpus_clean, stemDocument)

sms_corpus_clean <- tm_map(sms_corpus_clean, stripWhitespace) # eliminujemy niepotrzebne odstępy

# badamy ostateczny, oczyszczony korpus
lapply(sms_corpus[1:3], as.character)
lapply(sms_corpus_clean[1:3], as.character)

# tworzymy rozrzedzoną macierz dokumentowo-wyrazową
sms_dtm <- DocumentTermMatrix(sms_corpus_clean)

# alternatywne rozwiązanie: tworzymy rozrzedzoną macierz dokumentowo-wyrazową bezpośrednio z korpusu SMS-ów
sms_dtm2 <- DocumentTermMatrix(sms_corpus, control = list(
  tolower = TRUE,
  removeNumbers = TRUE,
  stopwords = TRUE,
  removePunctuation = TRUE,
  stemming = TRUE
))

# alternatywne rozwiązanie: użycie niestandardowej funkcji słów stopu zapewnia identyczny wynik
sms_dtm3 <- DocumentTermMatrix(sms_corpus, control = list(
  tolower = TRUE,
  removeNumbers = TRUE,
  stopwords = function(x) { removeWords(x, stopwords()) },
  removePunctuation = TRUE,
  stemming = TRUE
))

# porównujemy wyniki
sms_dtm
sms_dtm2
sms_dtm3

# tworzymy treningowy i testowy zbiór danych
sms_dtm_train <- sms_dtm[1:4169, ]
sms_dtm_test  <- sms_dtm[4170:5559, ]

# zapisujemy etykiety
sms_train_labels <- sms_raw[1:4169, ]$type
sms_test_labels  <- sms_raw[4170:5559, ]$type

# sprawdzamy, czy proporcje spamu są podobne
prop.table(table(sms_train_labels))
prop.table(table(sms_test_labels))

# wizualizacja chmury słów
library(wordcloud)
wordcloud(sms_corpus_clean, min.freq = 50, random.order = FALSE)

# dzielimy dane treningowe na grupy spamu i hamu
spam <- subset(sms_raw, type == "spam")
ham  <- subset(sms_raw, type == "ham")

wordcloud(spam$text, max.words = 40, scale = c(3, 0.5))
wordcloud(ham$text, max.words = 40, scale = c(3, 0.5))

# cechy wskaźnikowe dla częstych słów
findFreqTerms(sms_dtm_train, 5)

# zapisujemy często występujące wyrazy w wektorze znakowym
sms_freq_words <- findFreqTerms(sms_dtm_train, 5)
str(sms_freq_words)

# tworzymy macierze DTM zawierające tylko częste wyrazy
sms_dtm_freq_train <- sms_dtm_train[ , sms_freq_words]
sms_dtm_freq_test <- sms_dtm_test[ , sms_freq_words]

# przekształcamy liczebności w czynnik
convert_counts <- function(x) {
  x <- ifelse(x > 0, "Yes", "No")
}

# stosujemy funkcję convert_counts() do kolumn danych treningowych/testowych
sms_train <- apply(sms_dtm_freq_train, MARGIN = 2, convert_counts)
sms_test  <- apply(sms_dtm_freq_test, MARGIN = 2, convert_counts)

## Etap 3: trenowanie modelu na danych
library(naivebayes)
sms_classifier <- naive_bayes(sms_train, sms_train_labels)

## Etap 4: ewaluacja modelu ----
sms_test_pred <- predict(sms_classifier, sms_test)

library(gmodels)
CrossTable(sms_test_pred, sms_test_labels,
           prop.chisq = FALSE, prop.c = FALSE, prop.r = FALSE,
           dnn = c('predicted', 'actual'))

## Etap 5: poprawianie działania modelu
sms_classifier2 <- naive_bayes(sms_train, sms_train_labels, laplace = 1)
sms_test_pred2 <- predict(sms_classifier2, sms_test)
CrossTable(sms_test_pred2, sms_test_labels,
           prop.chisq = FALSE, prop.c = FALSE, prop.r = FALSE,
           dnn = c('predicted', 'actual'))
