library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.2.1 ✔ readr 2.2.0
## ✔ forcats 1.0.1 ✔ stringr 1.6.0
## ✔ ggplot2 4.0.3 ✔ tibble 3.3.1
## ✔ lubridate 1.9.5 ✔ tidyr 1.3.2
## ✔ purrr 1.2.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
raw_data <- read_csv("Churn_Modelling.csv")
## Rows: 10000 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.
#Se realizó una limpieza de datos minimos, entre estos, se eliminaros las columnas CustomerID, Surname, index, únicamente para no hacer errar al modelo. Además los datos no tienen valores NA, esto logra una integridad de datos.
#El primer hallazgo relevante parece al desglozar la tasa de abandono por geografÃa. Aunque Francia tiene mayor cantidad de clientes, AlemanÃa presenta una fuerte tasa de abandono, comparando 32% frente al 16% de Francia. ESto nos lleva a evaluar las acciones que se están tomando en AlemanÃa, porque hay má fuga cuando su comparación tiene muchos más clientes.
#se guardó la data de ID y Surname si fuese necesario usarla posteriormente
directory_data <- raw_data %>%
select(CustomerId, Surname)
# Estructura de datos
glimpse(raw_data)
## Rows: 10,000
## Columns: 14
## $ RowNumber <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,…
## $ CustomerId <dbl> 15634602, 15647311, 15619304, 15701354, 15737888, 1557…
## $ Surname <chr> "Hargrave", "Hill", "Onio", "Boni", "Mitchell", "Chu",…
## $ CreditScore <dbl> 619, 608, 502, 699, 850, 645, 822, 376, 501, 684, 528,…
## $ Geography <chr> "France", "Spain", "France", "France", "Spain", "Spain…
## $ Gender <chr> "Female", "Female", "Female", "Female", "Female", "Mal…
## $ Age <dbl> 42, 41, 42, 39, 43, 44, 50, 29, 44, 27, 31, 24, 34, 25…
## $ Tenure <dbl> 2, 1, 8, 1, 2, 8, 7, 4, 4, 2, 6, 3, 10, 5, 7, 3, 1, 9,…
## $ Balance <dbl> 0.00, 83807.86, 159660.80, 0.00, 125510.82, 113755.78,…
## $ NumOfProducts <dbl> 1, 1, 3, 2, 1, 2, 2, 4, 2, 1, 2, 2, 2, 2, 2, 2, 1, 2, …
## $ HasCrCard <dbl> 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, …
## $ IsActiveMember <dbl> 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, …
## $ EstimatedSalary <dbl> 101348.88, 112542.58, 113931.57, 93826.63, 79084.10, 1…
## $ Exited <dbl> 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, …
# Conteo valores NA
raw_data %>%
summarise(across(everything(), ~sum(is.na(.))))
## # A tibble: 1 × 14
## RowNumber CustomerId Surname CreditScore Geography Gender Age Tenure Balance
## <int> <int> <int> <int> <int> <int> <int> <int> <int>
## 1 0 0 0 0 0 0 0 0 0
## # ℹ 5 more variables: NumOfProducts <int>, HasCrCard <int>,
## # IsActiveMember <int>, EstimatedSalary <int>, Exited <int>
# Antes del borrado de NA, se guarda la data CustomerID y Surname, para análisis porterior si se requiere
directory_data <- raw_data %>%
select(CustomerId, Surname)
directory_data
## # A tibble: 10,000 × 2
## CustomerId Surname
## <dbl> <chr>
## 1 15634602 Hargrave
## 2 15647311 Hill
## 3 15619304 Onio
## 4 15701354 Boni
## 5 15737888 Mitchell
## 6 15574012 Chu
## 7 15592531 Bartlett
## 8 15656148 Obinna
## 9 15792365 He
## 10 15592389 H?
## # ℹ 9,990 more rows
# Remover columnas innecesarias para el análisis
df_clean <- raw_data %>%
select(-CustomerId, -Surname, -RowNumber) %>%
# Transform the category variables into a factor
mutate(
Geography = as.factor(Geography),
Gender = as.factor(Gender),
HasCrCard = as.factor(HasCrCard),
IsActiveMember = as.factor(IsActiveMember),
Exited = factor(Exited, levels=c(0,1), labels= c("Stays", "Leaves"))) %>%
# Renombrar columnas para una mejor descripción
rename(Churn = Exited)
# data limpia
colSums(is.na(df_clean))
## CreditScore Geography Gender Age Tenure
## 0 0 0 0 0
## Balance NumOfProducts HasCrCard IsActiveMember EstimatedSalary
## 0 0 0 0 0
## Churn
## 0
# DESCRIPCIÓN: Este gráfico demuestra que la mediana para ambos grups es similar, situándose cerca de los 10K, lo que indica que, el saldo promedio por su solo no es un factor determinante de abandono, porque los que se quedan tienen un promedio de 10K en sus cuentas y los que se van manejan los mismos datos.
# Adicional, se puede notar que los clientes con saldo abajo de 5K son una cantidad considerable y los que se van tiene un mÃnimo de 5K en sus cuentas, las cuentas a valor cero se mantienen por falta de uso muy probablemente.
ggplot(df_clean, aes(y= Balance, x=Churn, fill= Churn)) +
geom_boxplot()+
theme_minimal()+
labs(title = "Balance Distribution by Costumer Status", subtitle = "Detection of Outliers")
## AlemanÃa tiene la mayor cantidad de abandono de clientes, a diferencia de Francia y España, lo que no es coherente porque su cartera es similar.
ggplot(df_clean, aes(x=Geography, fill = Churn))+
geom_bar(position = "fill")+
scale_y_continuous(labels = scales::percent)+
theme_minimal()+
labs(title = "Dropout Rate by Country", y= "Percentaje")
#El abandono de cleintes es debido a su récord crediticio?, se busca saber si la solvencia crediticia. Sin embargo se puede observar que, tanto con buen récord o bajo récord, el cliente se va. #Adicional, que, se obvervan unos outliers en Leaves, dando a notas que hay clientes con un récord inusualmente bajo que dejaron el banco o fueron dados de baja.
# Analysis of Numerical Variables
# Is customer churn due the credit score ?
ggplot(df_clean, aes(x= Churn, y=CreditScore , fill= Churn))+
geom_boxplot()+
theme_minimal()+
labs(title = "Credit Score vs Customer Churn")
#Podemos notar que de un total de 10K registros un aproximado de 20% se va, está cifra es significativa, lo que requerirÃa acciones inmediatas.
# Churn Distribution
ggplot(df_clean, aes(x= Churn, fill = Churn))+
geom_bar()+
geom_text(stat = 'count', aes(label = ..count..), vjust=-0.5)+
theme_minimal()+
labs(title = "Churn Distribution", y= "Number of clientes")
## Warning: The dot-dot notation (`..count..`) was deprecated in ggplot2 3.4.0.
## ℹ Please use `after_stat(count)` instead.
## This warning is displayed once per session.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
# Corr Matrix, we need to convert the text data into a numeric data
cor_data <- df_clean %>%
select(CreditScore, Age, Tenure, Balance, NumOfProducts, EstimatedSalary) %>%
mutate(across(everything(), as.numeric))
cor_matrix <- cor(cor_data)
cor_df <- as.data.frame(cor_matrix)
cor_df <- rownames_to_column(cor_df, var= "Variable1")
cor_long <- pivot_longer(cor_df, cols = -Variable1, names_to = "Variable2", values_to = "Correlacion")
ggplot(cor_long, aes(x = Variable1, y = Variable2, fill = Correlacion)) +
geom_tile() +
geom_text(aes(label = round(Correlacion, 2)), size = 3) +
scale_fill_gradient2(low = "#075AFF", high = "#FF0000", mid = "white", limit = c(-1,1)) +
theme_minimal()