INFO-H510 Week 11 Data Dive

library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.4
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.0     ✔ tibble    3.2.1
## ✔ lubridate 1.9.3     ✔ tidyr     1.3.0
## ✔ purrr     1.0.2     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(dplyr)
library(ggthemes)
library(ggrepel)
library(broom)
library(lindia)
library(ggplot2)

#Loading data
data <- read_delim("../Churn_Modelling.csv", delim = ",")
## Rows: 10002 Columns: 14
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr  (3): Surname, Geography, Gender
## dbl (11): RowNumber, CustomerId, CreditScore, Age, Tenure, Balance, NumOfPro...
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
str(data)
## spc_tbl_ [10,002 × 14] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ RowNumber      : num [1:10002] 1 2 3 4 5 6 7 8 9 10 ...
##  $ CustomerId     : num [1:10002] 15634602 15647311 15619304 15701354 15737888 ...
##  $ Surname        : chr [1:10002] "Hargrave" "Hill" "Onio" "Boni" ...
##  $ CreditScore    : num [1:10002] 619 608 502 699 850 645 822 376 501 684 ...
##  $ Geography      : chr [1:10002] "France" "Spain" "France" "France" ...
##  $ Gender         : chr [1:10002] "Female" "Female" "Female" "Female" ...
##  $ Age            : num [1:10002] 42 41 42 39 43 44 50 29 44 NA ...
##  $ Tenure         : num [1:10002] 2 1 8 1 2 8 7 4 4 2 ...
##  $ Balance        : num [1:10002] 0 83808 159661 0 125511 ...
##  $ NumOfProducts  : num [1:10002] 1 1 3 2 1 2 2 4 2 1 ...
##  $ HasCrCard      : num [1:10002] 1 0 1 0 NA 1 1 1 0 1 ...
##  $ IsActiveMember : num [1:10002] 1 1 0 0 1 0 1 0 NA 1 ...
##  $ EstimatedSalary: num [1:10002] 101349 112543 113932 93827 79084 ...
##  $ Exited         : num [1:10002] 1 0 1 0 0 1 0 1 0 0 ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   RowNumber = col_double(),
##   ..   CustomerId = col_double(),
##   ..   Surname = col_character(),
##   ..   CreditScore = col_double(),
##   ..   Geography = col_character(),
##   ..   Gender = col_character(),
##   ..   Age = col_double(),
##   ..   Tenure = col_double(),
##   ..   Balance = col_double(),
##   ..   NumOfProducts = col_double(),
##   ..   HasCrCard = col_double(),
##   ..   IsActiveMember = col_double(),
##   ..   EstimatedSalary = col_double(),
##   ..   Exited = col_double()
##   .. )
##  - attr(*, "problems")=<externalptr>

In the dataset the variable Tenure describes the number of years a customer has resided with the banking organization. There could be a possible relation with this variable, to other variables within the dataset such as, Balance, EstimatedSalary etc., these variables often indicate the financial stability of the customer after can be associated with a longer tenure of the customer. So, let Tenure be the response variable, while Balance and EstimatedSalary being the explanatory variables.

linear_model <- lm(Tenure ~ Balance + EstimatedSalary, data = data)
summary(linear_model)
## 
## Call:
## lm(formula = Tenure ~ Balance + EstimatedSalary, data = data)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -5.0954 -2.0958 -0.0122  2.0829  5.0867 
## 
## Coefficients:
##                   Estimate Std. Error t value Pr(>|t|)    
## (Intercept)      5.016e+00  6.768e-02  74.112   <2e-16 ***
## Balance         -5.740e-07  4.635e-07  -1.238    0.216    
## EstimatedSalary  4.016e-07  5.029e-07   0.799    0.425    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 2.892 on 9999 degrees of freedom
## Multiple R-squared:  0.0002146,  Adjusted R-squared:  1.464e-05 
## F-statistic: 1.073 on 2 and 9999 DF,  p-value: 0.3419
linear_model <- glm(Tenure ~ Balance + EstimatedSalary, data = data)
summary(linear_model)
## 
## Call:
## glm(formula = Tenure ~ Balance + EstimatedSalary, data = data)
## 
## Coefficients:
##                   Estimate Std. Error t value Pr(>|t|)    
## (Intercept)      5.016e+00  6.768e-02  74.112   <2e-16 ***
## Balance         -5.740e-07  4.635e-07  -1.238    0.216    
## EstimatedSalary  4.016e-07  5.029e-07   0.799    0.425    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for gaussian family taken to be 8.363385)
## 
##     Null deviance: 83643  on 10001  degrees of freedom
## Residual deviance: 83625  on  9999  degrees of freedom
## AIC: 49632
## 
## Number of Fisher Scoring iterations: 2

From the above summary it is clear that the model is not a good fit for the variables, this can be seen from the p-value of the model which is very high (0.216 and 0.425) indicating we cannot make a confident hypothesis that the two explanatory variables properly fit the variable Tenure . Further this is also evident from the Multiple R-squared and the Adjusted R-squared error values from the linear model build above.

Furthermore diagnostics on the model can be computed using Deviance, Akaike Information Criterion, Bayesian Information Criterion and ANOVA

But these model diagnostics are best suited for comparing multiple model and their performance. So for the new model, let Tenure be the same response variable but changing the explanatory variable.

linear_model2 <- lm(Tenure ~ Balance + EstimatedSalary + Exited, data = data)
summary(linear_model2)
## 
## Call:
## lm(formula = Tenure ~ Balance + EstimatedSalary + Exited, data = data)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -5.1096 -2.1099 -0.0198  2.1467  5.1535 
## 
## Coefficients:
##                   Estimate Std. Error t value Pr(>|t|)    
## (Intercept)      5.029e+00  6.841e-02  73.511   <2e-16 ***
## Balance         -5.031e-07  4.668e-07  -1.078    0.281    
## EstimatedSalary  4.085e-07  5.029e-07   0.812    0.417    
## Exited          -9.283e-02  7.230e-02  -1.284    0.199    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 2.892 on 9998 degrees of freedom
## Multiple R-squared:  0.0003794,  Adjusted R-squared:  7.949e-05 
## F-statistic: 1.265 on 3 and 9998 DF,  p-value: 0.2845
linear_model2 <- glm(Tenure ~ Balance + EstimatedSalary + Exited, data = data)
summary(linear_model2)
## 
## Call:
## glm(formula = Tenure ~ Balance + EstimatedSalary + Exited, data = data)
## 
## Coefficients:
##                   Estimate Std. Error t value Pr(>|t|)    
## (Intercept)      5.029e+00  6.841e-02  73.511   <2e-16 ***
## Balance         -5.031e-07  4.668e-07  -1.078    0.281    
## EstimatedSalary  4.085e-07  5.029e-07   0.812    0.417    
## Exited          -9.283e-02  7.230e-02  -1.284    0.199    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for gaussian family taken to be 8.362843)
## 
##     Null deviance: 83643  on 10001  degrees of freedom
## Residual deviance: 83612  on  9998  degrees of freedom
## AIC: 49633
## 
## Number of Fisher Scoring iterations: 2

From the p-values and the both the R-squared errors it is clear that this model is also a very poor fit for the variables, but there error and p-values appear to be slightly better compared to the previous linear model.

So, this should mean that these the deviance values should be very high for both model but just a little bit smaller for the second model.

Deviance

paste("Model 1 Deviance", round(linear_model$deviance, 1))
## [1] "Model 1 Deviance 83625.5"
paste("Model 2 Deviance", round(linear_model2$deviance, 1))
## [1] "Model 2 Deviance 83611.7"

From the values above it can be seen that addition of the variable, Exited made the model just slightly better, but not perfect. Further diagnostics can be made from the using different diagnostic measures to reveal the similar information.

Akaike Information Criterion

paste("Model 1 AIC", round(linear_model$aic, 1))
## [1] "Model 1 AIC 49632.3"
paste("Model 2 AIC", round(linear_model2$aic, 1))
## [1] "Model 2 AIC 49632.7"

From both these values it is clean that both of then are loosing a lot of information, which essentially is the value the Akaike Information Criterion tries to predict. A negative value indicates that the model is performing well, while here the positive value indicates that the model is performing very poorly. Which can be seen from the Deviance, p-value and the R-squared error values seen above.

Moreover, there is slight variance among these model, which is also observed from the Deviance metric above, but in this case the difference among these relatively low (0.4). This could be attributed to the non-linear nature of the data, and the information loss is very high both these models appear to have similar values.

Bayesian Information Criterion

Similar to the Akaike Information Criterion, the Bayesian Information Criterion also utilizes the maximum likelihood estimator as well as the number of parameters in the model, while the formulation of the model slightly varies from the AIC.

paste("Model 1 AIC", round(BIC(linear_model), 1))
## [1] "Model 1 AIC 49661.2"
paste("Model 2 AIC", round(BIC(linear_model2), 1))
## [1] "Model 2 AIC 49668.7"

The BIC loss also takes into account the number of parameters that the model, but since it is small dataset and working with a linear regression model same number of parameters, the loss value should be similar to that of the AIC, but the interesting pattern that could be observed from this is that the diagnostic value of the second linear model is slightly better than that of the first, although the difference is still small compared to the magnitude of the actual error. Nevertheless, is important to note that the loss estimates using parameters leads to a slightly increase in loss of the 2nd model.

ANOVA

Unlike the above 2 diagnostics the ANOVA is often used while checking the residual deviance of the model upon adding new variables. And variables that penalize the model’s performance can be removed while still having a model that performs very well.

anova(linear_model, linear_model2, test = "Chisq")
## Analysis of Deviance Table
## 
## Model 1: Tenure ~ Balance + EstimatedSalary
## Model 2: Tenure ~ Balance + EstimatedSalary + Exited
##   Resid. Df Resid. Dev Df Deviance Pr(>Chi)
## 1      9999      83625                     
## 2      9998      83612  1   13.786   0.1992

The residual error of the models are high indicating a poor fit for the data, while is support of the inferences that are made from all the above diagnostic values, similar to the Deviance and the AIC values the first model performs slightly better than the later.

The ANOVA diagnostic can be further analyzed using the addition of new variables to check their effect of the standard error. So, let the new model also have NumOfProducts also as part of the explanatory variables.

linear_model3 <- lm(Tenure ~ Balance + EstimatedSalary + Exited + NumOfProducts, data = data)
summary(linear_model3)
## 
## Call:
## lm(formula = Tenure ~ Balance + EstimatedSalary + Exited + NumOfProducts, 
##     data = data)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -5.1216 -2.1224 -0.0178  2.1554  5.1633 
## 
## Coefficients:
##                   Estimate Std. Error t value Pr(>|t|)    
## (Intercept)      4.940e+00  1.134e-01  43.580   <2e-16 ***
## Balance         -3.577e-07  4.895e-07  -0.731    0.465    
## EstimatedSalary  3.990e-07  5.030e-07   0.793    0.428    
## Exited          -9.194e-02  7.231e-02  -1.272    0.204    
## NumOfProducts    5.145e-02  5.220e-02   0.986    0.324    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 2.892 on 9997 degrees of freedom
## Multiple R-squared:  0.0004765,  Adjusted R-squared:  7.661e-05 
## F-statistic: 1.192 on 4 and 9997 DF,  p-value: 0.3122
linear_model3 <- glm(Tenure ~ Balance + EstimatedSalary + Exited + NumOfProducts, data = data)
summary(linear_model3)
## 
## Call:
## glm(formula = Tenure ~ Balance + EstimatedSalary + Exited + NumOfProducts, 
##     data = data)
## 
## Coefficients:
##                   Estimate Std. Error t value Pr(>|t|)    
## (Intercept)      4.940e+00  1.134e-01  43.580   <2e-16 ***
## Balance         -3.577e-07  4.895e-07  -0.731    0.465    
## EstimatedSalary  3.990e-07  5.030e-07   0.793    0.428    
## Exited          -9.194e-02  7.231e-02  -1.272    0.204    
## NumOfProducts    5.145e-02  5.220e-02   0.986    0.324    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for gaussian family taken to be 8.362867)
## 
##     Null deviance: 83643  on 10001  degrees of freedom
## Residual deviance: 83604  on  9997  degrees of freedom
## AIC: 49634
## 
## Number of Fisher Scoring iterations: 2
anova(linear_model, linear_model2, linear_model3, test = "Chisq")
## Analysis of Deviance Table
## 
## Model 1: Tenure ~ Balance + EstimatedSalary
## Model 2: Tenure ~ Balance + EstimatedSalary + Exited
## Model 3: Tenure ~ Balance + EstimatedSalary + Exited + NumOfProducts
##   Resid. Df Resid. Dev Df Deviance Pr(>Chi)
## 1      9999      83625                     
## 2      9998      83612  1  13.7861   0.1992
## 3      9997      83604  1   8.1222   0.3244

As we can see from the model’s residual deviation it is clear that the upon adding the variable NumOfProducts the deviation has gone down slightly, suggesting these set of variables being a better fit for the model fitting.