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)##
## 0 1
## 6742 5187
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).
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