This is an R Markdown document. Markdown is a simple formatting syntax for authoring HTML, PDF, and MS Word documents. For more details on using R Markdown see http://rmarkdown.rstudio.com.
When you click the Knit button a document will be generated that includes both content as well as the output of any embedded R code chunks within the document. You can embed an R code chunk like this:
summary(cars)
## speed dist
## Min. : 4.0 Min. : 2.00
## 1st Qu.:12.0 1st Qu.: 26.00
## Median :15.0 Median : 36.00
## Mean :15.4 Mean : 42.98
## 3rd Qu.:19.0 3rd Qu.: 56.00
## Max. :25.0 Max. :120.00
You can also embed plots, for example:
Note that the echo = FALSE parameter was added to the
code chunk to prevent printing of the R code that generated the
plot.
setwd("C:/COLLINS")
#LOADING LIBRARY
library(rio)
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.4 ✔ readr 2.1.5
## ✔ forcats 1.0.0 ✔ stringr 1.5.2
## ✔ ggplot2 4.0.0 ✔ tibble 3.3.0
## ✔ lubridate 1.9.4 ✔ tidyr 1.3.1
## ✔ purrr 1.1.0
## ── 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(MASS)
##
## Attaching package: 'MASS'
##
## The following object is masked from 'package:dplyr':
##
## select
data=read.csv("Customer.csv")
head(data)
## customerID gender SeniorCitizen Partner Dependents tenure PhoneService
## 1 7590-VHVEG Female 0 Yes No 1 No
## 2 5575-GNVDE Male 0 No No 34 Yes
## 3 3668-QPYBK Male 0 No No 2 Yes
## 4 7795-CFOCW Male 0 No No 45 No
## 5 9237-HQITU Female 0 No No 2 Yes
## 6 9305-CDSKC Female 0 No No 8 Yes
## MultipleLines InternetService OnlineSecurity OnlineBackup DeviceProtection
## 1 No phone service DSL No Yes No
## 2 No DSL Yes No Yes
## 3 No DSL Yes Yes No
## 4 No phone service DSL Yes No Yes
## 5 No Fiber optic No No No
## 6 Yes Fiber optic No No Yes
## TechSupport StreamingTV StreamingMovies Contract PaperlessBilling
## 1 No No No Month-to-month Yes
## 2 No No No One year No
## 3 No No No Month-to-month Yes
## 4 Yes No No One year No
## 5 No No No Month-to-month Yes
## 6 No Yes Yes Month-to-month Yes
## PaymentMethod MonthlyCharges TotalCharges Churn
## 1 Electronic check 29.85 29.85 No
## 2 Mailed check 56.95 1889.50 No
## 3 Mailed check 53.85 108.15 Yes
## 4 Bank transfer (automatic) 42.30 1840.75 No
## 5 Electronic check 70.70 151.65 Yes
## 6 Electronic check 99.65 820.50 Yes
head(data, 10)
## customerID gender SeniorCitizen Partner Dependents tenure PhoneService
## 1 7590-VHVEG Female 0 Yes No 1 No
## 2 5575-GNVDE Male 0 No No 34 Yes
## 3 3668-QPYBK Male 0 No No 2 Yes
## 4 7795-CFOCW Male 0 No No 45 No
## 5 9237-HQITU Female 0 No No 2 Yes
## 6 9305-CDSKC Female 0 No No 8 Yes
## 7 1452-KIOVK Male 0 No Yes 22 Yes
## 8 6713-OKOMC Female 0 No No 10 No
## 9 7892-POOKP Female 0 Yes No 28 Yes
## 10 6388-TABGU Male 0 No Yes 62 Yes
## MultipleLines InternetService OnlineSecurity OnlineBackup
## 1 No phone service DSL No Yes
## 2 No DSL Yes No
## 3 No DSL Yes Yes
## 4 No phone service DSL Yes No
## 5 No Fiber optic No No
## 6 Yes Fiber optic No No
## 7 Yes Fiber optic No Yes
## 8 No phone service DSL Yes No
## 9 Yes Fiber optic No No
## 10 No DSL Yes Yes
## DeviceProtection TechSupport StreamingTV StreamingMovies Contract
## 1 No No No No Month-to-month
## 2 Yes No No No One year
## 3 No No No No Month-to-month
## 4 Yes Yes No No One year
## 5 No No No No Month-to-month
## 6 Yes No Yes Yes Month-to-month
## 7 No No Yes No Month-to-month
## 8 No No No No Month-to-month
## 9 Yes Yes Yes Yes Month-to-month
## 10 No No No No One year
## PaperlessBilling PaymentMethod MonthlyCharges TotalCharges Churn
## 1 Yes Electronic check 29.85 29.85 No
## 2 No Mailed check 56.95 1889.50 No
## 3 Yes Mailed check 53.85 108.15 Yes
## 4 No Bank transfer (automatic) 42.30 1840.75 No
## 5 Yes Electronic check 70.70 151.65 Yes
## 6 Yes Electronic check 99.65 820.50 Yes
## 7 Yes Credit card (automatic) 89.10 1949.40 No
## 8 No Mailed check 29.75 301.90 No
## 9 Yes Electronic check 104.80 3046.05 Yes
## 10 No Bank transfer (automatic) 56.15 3487.95 No
str(data)
## 'data.frame': 7043 obs. of 21 variables:
## $ customerID : chr "7590-VHVEG" "5575-GNVDE" "3668-QPYBK" "7795-CFOCW" ...
## $ gender : chr "Female" "Male" "Male" "Male" ...
## $ SeniorCitizen : int 0 0 0 0 0 0 0 0 0 0 ...
## $ Partner : chr "Yes" "No" "No" "No" ...
## $ Dependents : chr "No" "No" "No" "No" ...
## $ tenure : int 1 34 2 45 2 8 22 10 28 62 ...
## $ PhoneService : chr "No" "Yes" "Yes" "No" ...
## $ MultipleLines : chr "No phone service" "No" "No" "No phone service" ...
## $ InternetService : chr "DSL" "DSL" "DSL" "DSL" ...
## $ OnlineSecurity : chr "No" "Yes" "Yes" "Yes" ...
## $ OnlineBackup : chr "Yes" "No" "Yes" "No" ...
## $ DeviceProtection: chr "No" "Yes" "No" "Yes" ...
## $ TechSupport : chr "No" "No" "No" "Yes" ...
## $ StreamingTV : chr "No" "No" "No" "No" ...
## $ StreamingMovies : chr "No" "No" "No" "No" ...
## $ Contract : chr "Month-to-month" "One year" "Month-to-month" "One year" ...
## $ PaperlessBilling: chr "Yes" "No" "Yes" "No" ...
## $ PaymentMethod : chr "Electronic check" "Mailed check" "Mailed check" "Bank transfer (automatic)" ...
## $ MonthlyCharges : num 29.9 57 53.9 42.3 70.7 ...
## $ TotalCharges : num 29.9 1889.5 108.2 1840.8 151.7 ...
## $ Churn : chr "No" "No" "Yes" "No" ...
colnames(data)
## [1] "customerID" "gender" "SeniorCitizen" "Partner"
## [5] "Dependents" "tenure" "PhoneService" "MultipleLines"
## [9] "InternetService" "OnlineSecurity" "OnlineBackup" "DeviceProtection"
## [13] "TechSupport" "StreamingTV" "StreamingMovies" "Contract"
## [17] "PaperlessBilling" "PaymentMethod" "MonthlyCharges" "TotalCharges"
## [21] "Churn"
data$TotalCharges <- as.numeric(trimws(data$TotalCharges))
data <- data%>% drop_na(TotalCharges)
categorical_cols <- c("gender","Partner","Dependents","PhoneService",
"MultipleLines","InternetService","OnlineSecurity",
"OnlineBackup","DeviceProtection","TechSupport",
"StreamingTV","StreamingMovies","Contract",
"PaperlessBilling","PaymentMethod","Churn")
data[categorical_cols] <- lapply(data[categorical_cols], as.factor)
summary(data[, c("tenure","MonthlyCharges","TotalCharges")])
## tenure MonthlyCharges TotalCharges
## Min. : 1.00 Min. : 18.25 Min. : 18.8
## 1st Qu.: 9.00 1st Qu.: 35.59 1st Qu.: 401.4
## Median :29.00 Median : 70.35 Median :1397.5
## Mean :32.42 Mean : 64.80 Mean :2283.3
## 3rd Qu.:55.00 3rd Qu.: 89.86 3rd Qu.:3794.7
## Max. :72.00 Max. :118.75 Max. :8684.8
table(data$Churn)
##
## No Yes
## 5163 1869
prop.table(table(data$Churn)) * 100
##
## No Yes
## 73.4215 26.5785
ggplot(data, aes(x = Churn, y = MonthlyCharges, fill = Churn)) +
geom_boxplot()
ggplot(data, aes(x = tenure, fill = Churn)) +
geom_histogram(binwidth = 5, position = "dodge")
ggplot(data, aes(x = Contract, fill = Churn)) +
geom_bar(position = "fill") +
ylab("Proportion")
model_log <- glm(Churn ~ tenure + MonthlyCharges + Contract,
data = data, family = "binomial")
summary(model_log)
##
## Call:
## glm(formula = Churn ~ tenure + MonthlyCharges + Contract, family = "binomial",
## data = data)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -1.600590 0.089669 -17.85 <2e-16 ***
## tenure -0.035931 0.002049 -17.53 <2e-16 ***
## MonthlyCharges 0.028637 0.001350 21.22 <2e-16 ***
## ContractOne year -1.057378 0.100627 -10.51 <2e-16 ***
## ContractTwo year -1.993535 0.166946 -11.94 <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: 8143.4 on 7031 degrees of freedom
## Residual deviance: 6143.5 on 7027 degrees of freedom
## AIC: 6153.5
##
## Number of Fisher Scoring iterations: 6
ggplot(data, aes(x = Churn, fill = Churn)) +
geom_bar() +
labs(title = "Customer Churn Distribution")
ggplot(data, aes(x = Contract, fill = Churn)) +
geom_bar(position = "fill") +
ylab("Proportion") +
labs(title = "Churn by Contract Type")
ggplot(data, aes(x = tenure, fill = Churn)) +
geom_histogram(binwidth = 5, position = "dodge") +
labs(title = "Customer Tenure Distribution by Churn")
ggplot(data, aes(x = Churn, y = MonthlyCharges, fill = Churn)) +
geom_boxplot() +
labs(title = "Monthly Charges by Churn Status")
ggplot(data, aes(x = InternetService, fill = Churn)) +
geom_bar(position = "fill") +
ylab("Proportion") +
labs(title = "Churn by Internet Service Type")
ggplot(data, aes(x = Partner, fill = Churn)) +
geom_bar(position = "fill") +
ylab("Proportion") +
labs(title = "Churn by Partnership Status")
ggplot(data, aes(x = PaymentMethod, fill = Churn)) +
geom_bar(position = "fill") +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
ylab("Proportion") +
labs(title = "Churn by Payment Method")
ggplot(data, aes(x = factor(SeniorCitizen), fill = Churn)) +
geom_bar(position = "fill") +
ylab("Proportion") +
labs(title = "Churn by Senior Citizen Status", x = "Senior Citizen (0 = No, 1 = Yes)")
library(dplyr)
library(corrplot)
## corrplot 0.95 loaded
num_data <- data %>%
dplyr:: select(tenure, MonthlyCharges, TotalCharges)
corrplot(cor(num_data), method = "color", addCoef.col = "black",
title = "Correlation Heatmap", mar = c(0,0,1,0))
# Frequency table
table(data$Churn)
##
## No Yes
## 5163 1869
# Proportion
prop.table(table(data$Churn)) * 100
##
## No Yes
## 73.4215 26.5785
# Plot churn distribution
ggplot(data, aes(x = Churn, fill = Churn)) +
geom_bar() +
labs(title = "Churn Distribution")
churn_counts <- table(data$Churn)
pie(churn_counts,
labels = paste(names(churn_counts), round(100*churn_counts/sum(churn_counts), 1), "%"),
col = c("skyblue", "tomato"),
main = "Churn Distribution")
ggplot(data, aes(x = InternetService, y = MonthlyCharges, fill = InternetService)) +
geom_boxplot() +
labs(title = "Monthly Charges by Internet Service",
x = "Internet Service",
y = "Monthly Charges")
ggplot(data, aes(x = TotalCharges)) +
geom_density(fill = "lightblue", alpha = 0.6) +
labs(title = "Density of Total Charges",
x = "Total Charges", y = "Density")
ggplot(data, aes(x = Churn, y = TotalCharges, fill = Churn)) +
geom_boxplot() +
labs(title = "Total Charges by Churn Status",
x = "Churn", y = "Total Charges")
ggplot(data, aes(x = tenure, y = TotalCharges, color = Churn)) +
geom_point(alpha = 0.6) +
labs(title = "Tenure vs Total Charges",
x = "Tenure (months)", y = "Total Charges")
ggplot(data, aes(x = InternetService, y = TotalCharges, fill = InternetService)) +
geom_boxplot() +
labs(title = "Total Charges by Internet Service",
x = "Internet Service", y = "Total Charges")
# Hypothesis:
# H0: Churn is independent of Gender
# H1: Churn depends on Gender
tbl <- table(data$Churn, data$gender)
chisq.test(tbl)
##
## Pearson's Chi-squared test with Yates' continuity correction
##
## data: tbl
## X-squared = 0.47545, df = 1, p-value = 0.4905
# Hypothesis:
# H0: Mean TotalCharges is equal for Churn = Yes and No
# H1: Mean TotalCharges differs between Churn groups
t.test(TotalCharges ~ Churn, data = data)
##
## Welch Two Sample t-test
##
## data: TotalCharges by Churn
## t = 18.801, df = 4042.9, p-value < 2.2e-16
## alternative hypothesis: true difference in means between group No and group Yes is not equal to 0
## 95 percent confidence interval:
## 916.8121 1130.2840
## sample estimates:
## mean in group No mean in group Yes
## 2555.344 1531.796
# Hypothesis:
# H0: Mean MonthlyCharges is equal across Internet Service groups
# H1: At least one group mean differs
anova_model <- aov(MonthlyCharges ~ InternetService, data = data)
summary(anova_model)
## Df Sum Sq Mean Sq F value Pr(>F)
## InternetService 2 5221852 2610926 16065 <2e-16 ***
## Residuals 7029 1142369 163
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# Hypothesis:
# H0: No correlation between tenure and TotalCharges
# H1: Significant correlation exists
cor.test(data$tenure, data$TotalCharges)
##
## Pearson's product-moment correlation
##
## data: data$tenure and data$TotalCharges
## t = 122.81, df = 7030, p-value < 2.2e-16
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
## 0.8183033 0.8331706
## sample estimates:
## cor
## 0.8258805
# Hypothesis:
# H0: Tenure does not significantly predict TotalCharges
# H1: Tenure significantly predicts TotalCharges
model1 <- lm(TotalCharges ~ tenure, data = data)
summary(model1)
##
## Call:
## lm(formula = TotalCharges ~ tenure, data = data)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3997.2 -416.1 157.1 526.1 3382.8
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -189.529 25.254 -7.505 6.91e-14 ***
## tenure 76.271 0.621 122.810 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1278 on 7030 degrees of freedom
## Multiple R-squared: 0.6821, Adjusted R-squared: 0.682
## F-statistic: 1.508e+04 on 1 and 7030 DF, p-value: < 2.2e-16
# Testing multiple predictors
model2 <- lm(TotalCharges ~ tenure + MonthlyCharges, data = data)
summary(model2)
##
## Call:
## lm(formula = TotalCharges ~ tenure + MonthlyCharges, data = data)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1942.3 -465.4 -94.7 494.0 1911.8
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -2162.4319 21.9899 -98.34 <2e-16 ***
## tenure 65.4141 0.3683 177.62 <2e-16 ***
## MonthlyCharges 35.8789 0.3005 119.42 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 734.5 on 7029 degrees of freedom
## Multiple R-squared: 0.895, Adjusted R-squared: 0.895
## F-statistic: 2.997e+04 on 2 and 7029 DF, p-value: < 2.2e-16
# Logistic regression with key predictors
model3 <- glm(Churn ~ tenure + MonthlyCharges + TotalCharges + InternetService,
data = data,
family = "binomial")
summary(model3)
##
## Call:
## glm(formula = Churn ~ tenure + MonthlyCharges + TotalCharges +
## InternetService, family = "binomial", data = data)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 2.441e-01 1.888e-01 1.293 0.1960
## tenure -6.896e-02 5.716e-03 -12.065 < 2e-16 ***
## MonthlyCharges -8.541e-03 3.366e-03 -2.537 0.0112 *
## TotalCharges 2.863e-04 6.506e-05 4.400 1.08e-05 ***
## InternetServiceFiber optic 1.470e+00 1.236e-01 11.900 < 2e-16 ***
## InternetServiceNo -1.423e+00 1.507e-01 -9.440 < 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: 8143.4 on 7031 degrees of freedom
## Residual deviance: 6194.9 on 7026 degrees of freedom
## AIC: 6206.9
##
## Number of Fisher Scoring iterations: 6
# Odds ratio interpretation for logistic regression
exp(coef(model3))
## (Intercept) tenure
## 1.2764978 0.9333610
## MonthlyCharges TotalCharges
## 0.9914954 1.0002863
## InternetServiceFiber optic InternetServiceNo
## 4.3506086 0.2410518
# Predict churn probabilities
pred_prob <- predict(model3, type = "response")
# Classify: Yes if probability > 0.5
pred_class <- ifelse(pred_prob > 0.5, "Yes", "No")
# Confusion Matrix
table(Predicted = pred_class, Actual = data$Churn)
## Actual
## Predicted No Yes
## No 4709 1045
## Yes 454 824
library(ggplot2)
ggplot(data, aes(x = tenure, y = as.numeric(Churn == "Yes"))) +
geom_point(alpha = 0.3) +
stat_smooth(method = "glm", method.args = list(family = "binomial"), se = FALSE) +
labs(title = "Logistic Regression: Churn vs Tenure",
x = "Tenure (Months)", y = "Churn Probability")
## `geom_smooth()` using formula = 'y ~ x'