Importando datos

Importamos los datos de los correos marcados o no como spam. Cargando las librerías necesarias y después de ver que solo tenemos el texto del correo y si es o no spam, pasamos a remover los signos de puntuación de los correos así como cierto caracteres especiales, después pasamos todos lo correos a letra minúscula para poder buscar ciertas palabras que aparecen constantemente en los correos con spam.

library(tidyverse)
library(stringr)
library(e1071)
library(caTools)
data <- read.csv("C:/Users/luisa/OneDrive/Documentos/fraud_email.csv")
glimpse(data)
## Rows: 11,929
## Columns: 2
## $ Text  <chr> "Supply Quality China's EXCLUSIVE dimensions at Unbeatable Pr...
## $ Class <int> 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0...
data$Text <- str_replace(data$Text, "[:punct:]", "")
data$Text <- str_replace(data$Text, "<", "")
data$Text <- str_replace(data$Text, ">", "")
data$Text <- str_to_lower(data$Text)
data$Count <- sapply(strsplit(data$Text, " "), length)
data$Transfer <- as.numeric(str_detect(data$Text, "transfer"))
data$Web <- as.numeric(str_detect(data$Text, "http:"))
data$Million <- as.numeric(str_detect(data$Text, "million"))
data$Investment <- as.numeric(str_detect(data$Text, "investment"))
data$Private <- as.numeric(str_detect(data$Text, "private"))
data$Interested <- as.numeric(str_detect(data$Text, "interested"))
data$Space <- ifelse(str_count(data$Text, " ") > 0, 0, 1)
data$Ind <- rowSums(data[,4:10])

Como se puede ver en el código, contamos el número de palabras y buscamos cada palabra de interés con str_detect y convertimos esa columna de booleanos en un tipo de indicadora, para después sumar esos 0s y 1s para tener nuestra columna Ind así tendremos una columna numérica que entre más grande, más propenso es el correo a ser spam. Después seleccionamos las columnas numéricas de interés y si es o no spam.

Convertimos la clase (spam o no) en factor para que sea más sencillo la aplicación del modelo SVM.

datos <- data[,c(3, 11, 2)]
datos$Class <- factor(datos$Class, levels = c(0,1))
boxplot(datos$Count, datos$Ind)

table(datos$Class)
## 
##    0    1 
## 6742 5187

Aplicación de SVM

Para aplicar el modelo vamos a plantar una semilla para poder replicar los resultados. Empezamos partiendo los datos en 2, uno para ajustar el modelo y otro para probarlo.

set.seed(123)
split <- sample.split(datos$Class, SplitRatio = 0.7)
train <- subset(datos, split == T)
test <- subset(datos, split == F)

Después los transformaremos por buena práctica (además de que Count tiene valores muy grandes a comparación de Ind).

train[-3] <- scale(train[-3])
test[-3] <- scale(test[-3])

Y pasamos a aplicar el modelo de SVM y buscamos el mejor notando que el modelo puede mejorar un poco usando la función tune.

class <- svm(formula = Class ~.,
             data = train,
             type = 'C-classification',
             kernel = 'linear')
summary(class)
## 
## Call:
## svm(formula = Class ~ ., data = train, type = "C-classification", 
##     kernel = "linear")
## 
## 
## Parameters:
##    SVM-Type:  C-classification 
##  SVM-Kernel:  linear 
##        cost:  1 
## 
## Number of Support Vectors:  2392
## 
##  ( 1194 1198 )
## 
## 
## Number of Classes:  2 
## 
## Levels: 
##  0 1
tmodel <- tune(svm, Class ~., data = train,
            ranges=list(epsilon= seq(0,1,0.1), cost = 2^(2:7)))
plot(tmodel)
mymodel <- tmodel$best.model
summary(mymodel)

## 
## Call:
## best.tune(method = svm, train.x = Class ~ ., data = train, ranges = list(epsilon = seq(0, 
##     1, 0.1), cost = 2^(2:7)))
## 
## 
## Parameters:
##    SVM-Type:  C-classification 
##  SVM-Kernel:  radial 
##        cost:  128 
## 
## Number of Support Vectors:  1588
## 
##  ( 763 825 )
## 
## 
## Number of Classes:  2 
## 
## Levels: 
##  0 1

Finalmente vemos el balanced accuracy del mejor modelo y como se ve el plano.

pred_best <- predict(mymodel, newdata = test[-3])
tab <- table(Actual = test[,3], Predicted = pred_best)
tab
##       Predicted
## Actual    0    1
##      0 1672  351
##      1  155 1401
sens <- tab[2,2]/(tab[2,2] + tab[2,1])
specf <- tab[1,1]/(tab[1,1] + tab[1,2])
bal_acc <- (sens + specf)/2
bal_acc
## [1] 0.8634405
plot(mymodel, train,
     color.palette = cm.colors)