DQLab Telco merupakan perusahaan Telco yang sudah mempunyai banyak cabang tersebar dimana-mana. Sejak berdiri pada tahun 2019, DQLab Telco konsisten untuk memperhatikan customer experience nya sehingga tidak akan di tinggalkan pelanggan. Walaupun baru berumur 1 tahun lebih sedikit, DQLab Telco sudah mempunyai banyak pelanggan yang beralih langganan ke kompetitor. Pihak management ingin mengurangi jumlah pelanggan yang beralih (churn) dengan menggunakan machine learning.
Pada Part 1 ini akan dilakukan data wrangling (data cleansing) sebelum dilakukan pemodelan agar model prediksi menjadi lebih akurat dengan kualitas data yang lebih terjamin.
Dataset
Dataset yang digunakan berasal dari DQLab Telco. Asumsikan dataset ini merupakan data yang terakhir diperbarui oleh DQLab Telco pada bulan Juni 2020.
Untuk detail datanya adalah sebagai berikut:
- UpdatedAt : Periode of Data taken
- customerID : Customer ID
- gender : Whether the customer is a male or a female (Male, Female)
- SeniorCitizen : Whether the customer is a senior citizen or not (1, 0)
- Partner : Whether the customer has a partner or not (Yes, No)
- Dependents : Whether the customer has dependents or not (Yes, No)
- tenure : Number of months the customer has stayed with the company
- PhoneService : Whether the customer has a phone service or not (Yes, No)
- MultipleLines : Whether the customer has multiple lines or not (Yes, No, No phone service)
- InternetService : Customer’s internet service provider (DSL, Fiber optic, No)
- OnlineSecurity : Whether the customer has online security or not (Yes, No, No internet service)
- OnlineBackup : Whether the customer has online backup or not (Yes, No, No internet service)
- DeviceProtection : Whether the customer has device protection or not (Yes, No, No internet service)
- TechSupport : Whether the customer has tech support or not (Yes, No, No internet service)
- StreamingTV : Whether the customer has streaming TV or not (Yes, No, No internet service)
- StreamingMovies : Whether the customer has streaming movies or not (Yes, No, No internet service)
- Contract : The contract term of the customer (Month-to-month, One year, Two year)
- PaperlessBilling : Whether the customer has paperless billing or not (Yes, No)
- PaymentMethod : The customer’s payment method (Electronic check, Mailed check, Bank transfer (automatic), Credit card (automatic))
- MonthlyCharges : The amount charged to the customer monthly
- TotalCharges : The total amount charged to the customer
- Churn : Whether the customer churned or not (Yes or No)
Import Library
Library yang digunakan dalam pembahasan ini meliputi:
- pandas untuk analisis dan manipulasi data
- matplotlib untuk visualisasi data
# Load library
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
pd.options.display.max_columns = 50
Data Cleansing
Langkah yang akan dilakukan antara lain,
- Mencari ID pelanggan (nomor telepon) yang valid.
- Mengatasi data-data yang masih kosong atau missing values.
- Mengatasi nilai-nilai pencilan (outlier) pada variabel numerik.
- Menstandarisasi nilai dari variabel kategorik.
# Load dataset
df_load = pd.read_csv('data/data_dqlab_telco.csv')
# Print first five rows
df_load.head(5)
UpdatedAt | customerID | gender | SeniorCitizen | Partner | Dependents | tenure | PhoneService | MultipleLines | InternetService | OnlineSecurity | OnlineBackup | DeviceProtection | TechSupport | StreamingTV | StreamingMovies | Contract | PaperlessBilling | PaymentMethod | MonthlyCharges | TotalCharges | Churn | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 202006 | 45759018157 | Female | 0 | Yes | No | 1.0 | No | No phone service | DSL | No | Yes | No | No | No | No | Month-to-month | Yes | Electronic check | 29.85 | 29.85 | No |
1 | 202006 | 45557574145 | Male | 0 | No | No | 34.0 | Yes | No | DSL | Yes | No | Yes | No | No | No | One year | No | Mailed check | 56.95 | 1889.50 | No |
2 | 202006 | 45366876421 | Male | 0 | No | No | 2.0 | Yes | No | DSL | Yes | Yes | No | No | No | No | Month-to-month | Yes | Mailed check | 53.85 | 108.15 | Yes |
3 | 202006 | 45779536532 | Male | 0 | No | No | 45.0 | No | No phone service | DSL | Yes | No | Yes | Yes | No | No | One year | No | Bank transfer (automatic) | 42.30 | 1840.75 | No |
4 | 202006 | 45923787906 | Female | 0 | No | No | 2.0 | Yes | No | Fiber optic | No | No | No | No | No | No | Month-to-month | Yes | Electronic check | 70.70 | 151.65 | Yes |
# Print number of rows and number of columns
print(f'Dataset ini memiliki {df_load.shape[0]} baris dan {df_load.shape[1]} kolom')
# Print number of unique Customer ID
print('Jumlah ID Pelanggan yang unik adalah',df_load.customerID.nunique())
Dataset ini memiliki 7113 baris dan 22 kolom
Jumlah ID Pelanggan yang unik adalah 7017
# View info data
df_load.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 7113 entries, 0 to 7112
Data columns (total 22 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 UpdatedAt 7113 non-null int64
1 customerID 7113 non-null object
2 gender 7113 non-null object
3 SeniorCitizen 7113 non-null int64
4 Partner 7113 non-null object
5 Dependents 7113 non-null object
6 tenure 7014 non-null float64
7 PhoneService 7113 non-null object
8 MultipleLines 7113 non-null object
9 InternetService 7113 non-null object
10 OnlineSecurity 7113 non-null object
11 OnlineBackup 7113 non-null object
12 DeviceProtection 7113 non-null object
13 TechSupport 7113 non-null object
14 StreamingTV 7113 non-null object
15 StreamingMovies 7113 non-null object
16 Contract 7113 non-null object
17 PaperlessBilling 7113 non-null object
18 PaymentMethod 7113 non-null object
19 MonthlyCharges 7087 non-null float64
20 TotalCharges 7098 non-null float64
21 Churn 7070 non-null object
dtypes: float64(3), int64(2), object(17)
memory usage: 1.2+ MB
Mencari Validitas ID Pelanggan
Memfilter ID Pelanggan dengan Format Tertentu
Format ID Pelanggan (Phone Number) yang benar, yaitu:
- Panjang karakter adalah 11-12.
- Terdiri dari angka saja, tidak diperbolehkan ada karakter selain angka.
- Diawali dengan angka 45 pada 2 digit pertama.
# Create new column to check validity Customer ID
df_load['valid_id'] = df_load['customerID'].astype('str').str.match(r'(45\d{9,10})')
# Get rows of valid data
df_load = (df_load[df_load['valid_id'] == True]).drop('valid_id', axis=1)
# Total valid Customer ID with filtering
print('Hasil jumlah ID Pelanggan yang terfilter adalah', df_load['customerID'].count())
Hasil jumlah ID Pelanggan yang terfilter adalah 7006
Memfilter Duplikasi ID pelanggan
Memastikan bahwa tidak ada ID pelanggan yang duplikat. Biasanya duplikasi ID number ini tipenya:
- Duplikasi dikarenakan inserting melebihi satu kali dengan nilai yang sama tiap kolomnya.
- Duplikasi dikarenakan inserting beda periode pengambilan data.
# Drop duplicate rows
df_load.drop_duplicates()
# Drop duplicate rows Customer ID by periods
df_load = df_load.sort_values('UpdatedAt', ascending=False).drop_duplicates(['customerID'])
# Total valid Customer ID without duplicated
print('Hasil jumlah ID Pelanggan yang sudah dihilangkan duplikasinya adalah',df_load['customerID'].count())
Hasil jumlah ID Pelanggan yang sudah dihilangkan duplikasinya adalah 6993
Validitas dari ID Pelanggan sangat diperlukan untuk memastikan bahwa data yang kita ambil sudah benar. Berdasarkan hasil tersbut, terdapat perbedaan jumlah ID Pelanggan dari data pertama kali di load sampai dengan hasil validitas. Dimana jumlah baris data ketika pertama kali di load ada sebanyak 7113 baris, lalu setelah di cek validitas dari ID pelanggan, maka terfilter 6993 baris data.
Mengecek dan Menangani Missing Values
Karena tujuan project ini adalah memprediksi churn maka data pada kolom Churn
tidak boleh kosong. Jika ditemukan missing values maka akan dilakukan penghapusan data.
# Missing values kolom Churn
print('Total missing values data dari kolom Churn', df_load['Churn'].isnull().sum())
# Dropping all Rows with spesific column
df_load.dropna(subset=['Churn'], inplace=True)
print('Total data setelah penghapusan missing values adalah', df_load.shape[0])
Total missing values data dari kolom Churn 43
Total data setelah penghapusan missing values adalah 6950
Mengatasi Missing Values dengan Pengisian Nilai tertentu
Selain dengan menghapus rows dari data, menangani missing values bisa menggunakan nilai tertentu. Pada project ini diasumsikan data modeller meminta pengisian missing values dengan kriteria berikut:
- Tenure pihak data modeller meminta setiap rows yang memiliki missing values untuk Lama berlangganan di isi dengan 11.
- Variable yang bersifat numerik selain Tenure di isi dengan median dari masing-masing variable tersebut.
print('Status Missing Values :',df_load.isnull().values.any())
print('\nJumlah Missing Values masing-masing kolom, adalah:')
print(df_load.isnull().sum().sort_values(ascending=False))
# Handling missing values Tenure fill with 11
df_load['tenure'].fillna(11, inplace=True)
# Handling missing values num vars (except Tenure)
for col_name in list(['MonthlyCharges','TotalCharges']):
median_data = df_load[col_name].median()
df_load[col_name].fillna(median_data, inplace=True)
print('\nJumlah Missing Values setelah di imputer datanya, adalah:')
print(df_load.isnull().sum().sort_values(ascending=False))
Status Missing Values : True
Jumlah Missing Values masing-masing kolom, adalah:
tenure 99
MonthlyCharges 26
TotalCharges 15
Churn 0
InternetService 0
customerID 0
gender 0
SeniorCitizen 0
Partner 0
Dependents 0
PhoneService 0
MultipleLines 0
OnlineSecurity 0
OnlineBackup 0
DeviceProtection 0
TechSupport 0
StreamingTV 0
StreamingMovies 0
Contract 0
PaperlessBilling 0
PaymentMethod 0
UpdatedAt 0
dtype: int64
Jumlah Missing Values setelah di imputer datanya, adalah:
Churn 0
TotalCharges 0
customerID 0
gender 0
SeniorCitizen 0
Partner 0
Dependents 0
tenure 0
PhoneService 0
MultipleLines 0
InternetService 0
OnlineSecurity 0
OnlineBackup 0
DeviceProtection 0
TechSupport 0
StreamingTV 0
StreamingMovies 0
Contract 0
PaperlessBilling 0
PaymentMethod 0
MonthlyCharges 0
UpdatedAt 0
dtype: int64
Setelah kita analisis lebih lanjut, ternyata masih ada missing values dari data yang kita sudah cek validitas ID Pelanggannya. Missing values terdapat pada kolom Churn, tenure, MonthlyCharges, dan TotalCharges. Setelah kita tangani dengan cara penghapusan rows dan pengisian rows dengan nilai tertentu, terbukti sudah tidak ada missing values lagi pada data, terbukti dari jumlah missing values masing-masing variable yang bernilai 0. Selanjutnya kita akan melakukan penanganan pencilan (outlier)
Mendeteksi dan Mengatasi Outlier
Mendeteksi Outlier
Mendeteksi pencilan dari suatu nilai (outlier) salah satunya bisa menggunakan Box Plot. Box Plot merupakan ringkasan distribusi sampel yang disajikan secara grafis yang bisa menggambarkan bentuk distribusi data (skewness), ukuran tendensi sentral dan ukuran penyebaran (keragaman) data pengamatan. Outlier biasanya ditemukan pada variabel bertipe numerik.
print('\nPersebaran data sebelum ditangani Outlier: ')
print(df_load.describe())
# Creating Box Plot
fig, ax = plt.subplots(nrows=1, ncols=3)
fig.set_size_inches(15, 6)
ax[0].boxplot(df_load['tenure'])
ax[0].set_xlabel('tenure')
ax[1].boxplot(df_load['MonthlyCharges'])
ax[1].set_xlabel('MonthlyCharges')
ax[2].boxplot(df_load['TotalCharges'])
ax[2].set_xlabel('TotalCharges')
plt.show()
Persebaran data sebelum ditangani Outlier:
UpdatedAt SeniorCitizen tenure MonthlyCharges TotalCharges
count 6950.0 6950.000000 6950.000000 6950.000000 6950.000000
mean 202006.0 0.162302 32.477266 65.783741 2305.083460
std 0.0 0.368754 25.188910 50.457871 2578.651143
min 202006.0 0.000000 0.000000 0.000000 19.000000
25% 202006.0 0.000000 9.000000 36.462500 406.975000
50% 202006.0 0.000000 29.000000 70.450000 1400.850000
75% 202006.0 0.000000 55.000000 89.850000 3799.837500
max 202006.0 1.000000 500.000000 2311.000000 80000.000000
Dari ketiga boxplot dengan variable tenure, MonthlyCharges, dan TotalCharges terlihat jelas terdapat adanya outlier. Hal ini bisa di identifikasi dari adanya titik titik yang berada jauh dari gambar boxplotnya. Jika kita lihat persebaran datanya dari kolom max nya juga ada nilai yang sangat tinggi sekali. Kemudian nilai outlier tersebut ditangani dengan cara merubah nilainya ke nilai Maximum & Minimum dari interquartile range (IQR).
Mengatasi Outlier
Setelah kita mengetahui variable mana saja yang terdapat pencilan (Outlier), selanjutnya kita akan atasi outlier dengan menggunakan metode interquartile range (IQR).
Tentukan:
- Nilai Minimum dan Maximum data di tolerir
- Ubah Nilai yg di luar range Minumum & Maximum ke dalam nilai Minimum dan Maximum
# Handling with IQR
Q1 = (df_load[['tenure','MonthlyCharges','TotalCharges']]).quantile(0.25)
Q3 = (df_load[['tenure','MonthlyCharges','TotalCharges']]).quantile(0.75)
IQR = Q3 - Q1
maximum = Q3 + (1.5*IQR)
print('Nilai Maximum dari masing-masing Variable adalah: ')
print(maximum)
minimum = Q1 - (1.5*IQR)
print('\nNilai Minimum dari masing-masing Variable adalah: ')
print(minimum)
more_than = (df_load > maximum)
lower_than = (df_load < minimum)
df_load = df_load.mask(more_than, maximum, axis=1)
df_load = df_load.mask(lower_than, minimum, axis=1)
print('\nPersebaran data setelah ditangani Outlier: ')
print(df_load[['tenure','MonthlyCharges','TotalCharges']].describe())
Nilai Maximum dari masing-masing Variable adalah:
tenure 124.00000
MonthlyCharges 169.93125
TotalCharges 8889.13125
dtype: float64
Nilai Minimum dari masing-masing Variable adalah:
tenure -60.00000
MonthlyCharges -43.61875
TotalCharges -4682.31875
dtype: float64
Persebaran data setelah ditangani Outlier:
tenure MonthlyCharges TotalCharges
count 6950.000000 6950.000000 6950.000000
mean 32.423165 64.992201 2286.058750
std 24.581073 30.032040 2265.702553
min 0.000000 0.000000 19.000000
25% 9.000000 36.462500 406.975000
50% 29.000000 70.450000 1400.850000
75% 55.000000 89.850000 3799.837500
max 124.000000 169.931250 8889.131250
Setelah di tangani outliernya, dan dilihat persebaran datanya, terlihat sudah tidak ada lagi nilai yang outlier.
Standarisasi Nilai dari Variabel Kategorikal
Mengecek Nilai Tidak Standard
Selanjutnya kita akan mendeteksi apakah ada nilai-nilai dari variable kategorik yang tidak standard. Hal ini biasanya terjadi dikarenakan kesalahan input data. Perbedaan istilah menjadi salah satu faktor yang sering terjadi, untuk itu dibutuhkan standarisasi dari data yang sudah terinput.
# Check standardization
for col_name in df_load.select_dtypes(include='object').columns:
print('Unique Values Count Before Standardized Variable',col_name)
print(df_load[col_name].value_counts())
print('\n')
Unique Values Count Before Standardized Variable customerID
45386549500 1
45577435073 1
45612256114 1
45896795719 1
45513613179 1
..
45996422740 1
45740109343 1
45954614082 1
45795432264 1
45077845221 1
Name: customerID, Length: 6950, dtype: int64
Unique Values Count Before Standardized Variable gender
Male 3499
Female 3431
Wanita 14
Laki-Laki 6
Name: gender, dtype: int64
Unique Values Count Before Standardized Variable Partner
No 3591
Yes 3359
Name: Partner, dtype: int64
Unique Values Count Before Standardized Variable Dependents
No 4870
Yes 2060
Iya 20
Name: Dependents, dtype: int64
Unique Values Count Before Standardized Variable PhoneService
Yes 6281
No 669
Name: PhoneService, dtype: int64
Unique Values Count Before Standardized Variable MultipleLines
No 3346
Yes 2935
No phone service 669
Name: MultipleLines, dtype: int64
Unique Values Count Before Standardized Variable InternetService
Fiber optic 3057
DSL 2388
No 1505
Name: InternetService, dtype: int64
Unique Values Count Before Standardized Variable OnlineSecurity
No 3454
Yes 1991
No internet service 1505
Name: OnlineSecurity, dtype: int64
Unique Values Count Before Standardized Variable OnlineBackup
No 3045
Yes 2400
No internet service 1505
Name: OnlineBackup, dtype: int64
Unique Values Count Before Standardized Variable DeviceProtection
No 3054
Yes 2391
No internet service 1505
Name: DeviceProtection, dtype: int64
Unique Values Count Before Standardized Variable TechSupport
No 3431
Yes 2014
No internet service 1505
Name: TechSupport, dtype: int64
Unique Values Count Before Standardized Variable StreamingTV
No 2774
Yes 2671
No internet service 1505
Name: StreamingTV, dtype: int64
Unique Values Count Before Standardized Variable StreamingMovies
No 2747
Yes 2698
No internet service 1505
Name: StreamingMovies, dtype: int64
Unique Values Count Before Standardized Variable Contract
Month-to-month 3823
Two year 1670
One year 1457
Name: Contract, dtype: int64
Unique Values Count Before Standardized Variable PaperlessBilling
Yes 4114
No 2836
Name: PaperlessBilling, dtype: int64
Unique Values Count Before Standardized Variable PaymentMethod
Electronic check 2337
Mailed check 1594
Bank transfer (automatic) 1519
Credit card (automatic) 1500
Name: PaymentMethod, dtype: int64
Unique Values Count Before Standardized Variable Churn
No 5114
Yes 1827
Churn 9
Name: Churn, dtype: int64
Ketika kita amati lebih jauh dari jumlah unique value dari masing-masing variable kategorik, terlihat jelas bahwa ada beberapa variable yang tidak standar. Variable itu adalah :
- gender (Female, Male, Wanita, Laki-Laki), yang bisa di standarkan nilainya menjadi (Female, Male) karena mempunyai makna yang sama.
- Dependents (Yes, No, Iya), yang bisa di standarkan nilainya menjadi (Yes, No) karena mempunyai makna yang sama.
- Churn (Yes, No, Churn), yang bisa di standarkan nilainya menjadi (Yes, No) karena mempunyai makna yang sama.
Menstandarisasi Variable Kategorik
Setelah kita mengetahui variable mana saja yang ada nilai tidak standar, maka kita standarkan dengan pola terbanyak nya, dengan syarat tanpa mengubah maknanya. Contoh : Iya -> Yes
# Change values
df_load = df_load.replace(['Wanita','Laki-Laki','Churn','Iya'],['Female','Male','Yes','Yes'])
# Check values after standardization
for col_name in list(['gender','Dependents','Churn']):
print('Unique Values Count After Standardized Variable',col_name)
print(df_load[col_name].value_counts())
print('\n')
Unique Values Count After Standardized Variable gender
Male 3505
Female 3445
Name: gender, dtype: int64
Unique Values Count After Standardized Variable Dependents
No 4870
Yes 2080
Name: Dependents, dtype: int64
Unique Values Count After Standardized Variable Churn
No 5114
Yes 1836
Name: Churn, dtype: int64
Terlihat bahwa nilai dari variabel sudah standar dan siap dianggap sudah bersih atau clean.
Save Clean Dataset
Setelah raw data dilakukan data cleansing, selanjutnya data ini dapat digunakan untuk pemodelan.
# Print first five rows clean data
df_load.head()
UpdatedAt | customerID | gender | SeniorCitizen | Partner | Dependents | tenure | PhoneService | MultipleLines | InternetService | OnlineSecurity | OnlineBackup | DeviceProtection | TechSupport | StreamingTV | StreamingMovies | Contract | PaperlessBilling | PaymentMethod | MonthlyCharges | TotalCharges | Churn | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 202006 | 45759018157 | Female | 0 | Yes | No | 1.0 | No | No phone service | DSL | No | Yes | No | No | No | No | Month-to-month | Yes | Electronic check | 29.85 | 29.85 | No |
4727 | 202006 | 45315483266 | Male | 0 | Yes | No | 60.0 | Yes | No | No | No internet service | No internet service | No internet service | No internet service | No internet service | No internet service | Two year | Yes | Bank transfer (automatic) | 20.50 | 1198.80 | No |
4738 | 202006 | 45236961615 | Male | 0 | No | No | 5.0 | Yes | Yes | Fiber optic | No | Yes | Yes | No | Yes | Yes | Month-to-month | No | Mailed check | 104.10 | 541.90 | Yes |
4737 | 202006 | 45929827382 | Female | 0 | Yes | No | 72.0 | Yes | Yes | Fiber optic | Yes | Yes | Yes | Yes | Yes | Yes | Two year | Yes | Bank transfer (automatic) | 115.50 | 8312.75 | No |
4736 | 202006 | 45305082233 | Female | 0 | Yes | Yes | 56.0 | Yes | Yes | DSL | Yes | Yes | Yes | Yes | Yes | No | Two year | No | Credit card (automatic) | 81.25 | 4620.40 | No |
# Save clean data
df_load.reset_index(drop=True, inplace=True)
df_load.to_csv('data/data_cleansing_telco.csv', index=False)