Logistic Regression Test
library(dplyr) #For Data Wrangling
library(UBL) #for upsampling data
library(caret) #for model evaluation cust <- read.csv("cust.csv", sep = ";", stringsAsFactors = T) %>%
select(y,marital,education,housing,loan,poutcome)
glimpse(cust)## Rows: 45,211
## Columns: 6
## $ y <fct> no, no, no, no, no, no, no, no, no, no, no, no, no, no, no, …
## $ marital <fct> married, single, married, married, single, married, single, …
## $ education <fct> tertiary, secondary, secondary, unknown, unknown, tertiary, …
## $ housing <fct> yes, yes, yes, yes, no, yes, yes, yes, yes, yes, yes, yes, y…
## $ loan <fct> no, no, yes, no, no, no, yes, no, no, no, no, no, no, no, no…
## $ poutcome <fct> unknown, unknown, unknown, unknown, unknown, unknown, unknow…
Data Structure
- marital : marital status (categorical: “married”,“divorced”,“single”; note: “divorced” means divorced or widowed)
- education (categorical: “unknown”,“secondary”,“primary”,“tertiary”)
- housing: has housing loan? (binary: “yes”,“no”)
- loan: has personal loan? (binary: “yes”,“no”)
- poutcome: outcome of the previous marketing campaign (categorical: “unknown”,“other”,“failure”,“success”)
Output variable (target):
- y - has the client subscribed a term deposit? (binary: “yes”,“no”)
NA Value check
From the results checking , there is no null data
anyNA(cust)## [1] FALSE
make train and test data
I make the proportion of data for train as much as 80% of the total data and 20% for test/prediction data
RNGkind(sample.kind = "Rounding")
set.seed(100)
# index sampling
index <- sample(x = nrow(cust) , size = nrow(cust) * 0.8)
# splitting
cust_train <- cust[index , ]
cust_test <- cust[-index , ]Proportion Check of train data
if we see that the number of data proportions in the “y” field of the custom table is unbalanced, then it needs to be balanced so that the model can be trained better
prop.table(table(cust_train$y))##
## no yes
## 0.8828799 0.1171201
Balancing the proportion of train data with upsampling
cust_train_smote <- SmoteClassif(form = y ~ .,
dat = cust_train,
C.perc = "balance",
dist = "HVDM")recheck proportion of target variable after balancing
prop.table(table(cust_train_smote$y))##
## no yes
## 0.5 0.5
Build logistic regression model with all predictor
model_cust1 <- glm(formula = y~. ,
data = cust_train_smote,
family = "binomial")
summary(model_cust1)##
## Call:
## glm(formula = y ~ ., family = "binomial", data = cust_train_smote)
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -2.58735 -1.11797 -0.06127 1.11044 2.20265
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 0.71546 0.05459 13.105 < 2e-16 ***
## maritalmarried -1.04298 0.03247 -32.121 < 2e-16 ***
## maritalsingle -0.28975 0.03483 -8.319 < 2e-16 ***
## educationsecondary -0.01776 0.03420 -0.519 0.604
## educationtertiary 0.28325 0.03554 7.969 1.60e-15 ***
## educationunknown -0.38677 0.06771 -5.712 1.12e-08 ***
## housingyes -0.70928 0.02330 -30.444 < 2e-16 ***
## loanyes -1.11355 0.04019 -27.710 < 2e-16 ***
## poutcomeother 0.15666 0.06940 2.257 0.024 *
## poutcomesuccess 2.31266 0.07840 29.499 < 2e-16 ***
## poutcomeunknown 0.20385 0.04060 5.021 5.15e-07 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 50139 on 36167 degrees of freedom
## Residual deviance: 44411 on 36157 degrees of freedom
## AIC: 44433
##
## Number of Fisher Scoring iterations: 4
# Build logistic regression model with selected predictor
model_cust2 <- glm(formula = y ~ education + marital,
data = cust_train_smote,
family = "binomial")
summary(model_cust2)##
## Call:
## glm(formula = y ~ education + marital, family = "binomial", data = cust_train_smote)
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -1.59875 -1.13109 -0.04557 1.12177 1.48380
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 0.59596 0.03584 16.628 < 2e-16 ***
## educationsecondary -0.12213 0.03236 -3.775 0.000160 ***
## educationtertiary 0.35549 0.03378 10.522 < 2e-16 ***
## educationunknown -0.23103 0.06447 -3.584 0.000339 ***
## maritalmarried -1.06139 0.03100 -34.234 < 2e-16 ***
## maritalsingle -0.34153 0.03326 -10.269 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 50139 on 36167 degrees of freedom
## Residual deviance: 48053 on 36162 degrees of freedom
## AIC: 48065
##
## Number of Fisher Scoring iterations: 4
Build logistic regression model with all predictor II
model_cust3 <- glm(formula = y ~ loan+ housing,
data = cust_train_smote,
family = "binomial")
summary(model_cust3)##
## Call:
## glm(formula = y ~ loan + housing, family = "binomial", data = cust_train_smote)
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -1.3943 -1.0595 0.1684 0.9750 1.8394
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 0.49683 0.01542 32.23 <2e-16 ***
## loanyes -1.20427 0.03873 -31.09 <2e-16 ***
## housingyes -0.78060 0.02181 -35.80 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 50139 on 36167 degrees of freedom
## Residual deviance: 47599 on 36165 degrees of freedom
## AIC: 47605
##
## Number of Fisher Scoring iterations: 4
Model Comparation with AIC value
The Akaike information criterion (AIC) is a mathematical method for evaluating how well a model fits the data it was generated from. The smaller the AIC value, the better the model. So, we choose the model with all the predictor variables (model1)
model_cust1$aic## [1] 44432.54
model_cust2$aic## [1] 48064.92
model_cust3$aic## [1] 47604.97
Interpretation
exp(model_cust1$coefficients)## (Intercept) maritalmarried maritalsingle educationsecondary
## 2.0451233 0.3524028 0.7484540 0.9823988
## educationtertiary educationunknown housingyes loanyes
## 1.3274343 0.6792499 0.4919983 0.3283898
## poutcomeother poutcomesuccess poutcomeunknown
## 1.1695958 10.1012982 1.2261091
- Married customers are 0.35 times more likely to subscribe to a term deposit
- for a single customer, it is 0.74 times more likely to subscribe to a term deposit
- customers with secondary education are 0.98 times more likely to subscribe to term deposits
- customers with tertiary education are 1.32 times more likely to subscribe to term deposits
Prediction
cust_test$pred.Risk <- predict(object = model_cust1,
newdata = cust_test,
type = "response")cust_test$pred.Label <- ifelse(cust_test$pred.Risk > 0.7 , yes = "yes" , no = "no")
cust_test$pred.Label <- as.factor(cust_test$pred.Label)
head(cust_test)## y marital education housing loan poutcome pred.Risk pred.Label
## 2 no single secondary yes no unknown 0.4756494 no
## 4 no married unknown yes no unknown 0.2279853 no
## 12 no single secondary yes no unknown 0.4756494 no
## 21 no married secondary yes yes unknown 0.1230059 no
## 22 no married tertiary yes no unknown 0.3659321 no
## 23 no single primary yes yes unknown 0.2326736 no
Model Evaluation
confusionMatrix(data = cust_test$pred.Label, reference = cust_test$y , positive = "yes")## Confusion Matrix and Statistics
##
## Reference
## Prediction no yes
## no 7320 739
## yes 670 314
##
## Accuracy : 0.8442
## 95% CI : (0.8365, 0.8516)
## No Information Rate : 0.8836
## P-Value [Acc > NIR] : 1.00000
##
## Kappa : 0.2206
##
## Mcnemar's Test P-Value : 0.07005
##
## Sensitivity : 0.29820
## Specificity : 0.91615
## Pos Pred Value : 0.31911
## Neg Pred Value : 0.90830
## Prevalence : 0.11644
## Detection Rate : 0.03472
## Detection Prevalence : 0.10881
## Balanced Accuracy : 0.60717
##
## 'Positive' Class : yes
##
- Accuracy value (0.8442), means +-84% predicted data is correct
- The sensitivity value and the small post press value illustrate the proportion of the test data that is indeed imbalance but prediction still acurate