from IPython.display import display
from IPython.display import HTML
import IPython.core.display as di # Example: di.display_html('<h3>%s:</h3>' % str, raw=True)
# This line will hide code by default when the notebook is exported as HTML
di.display_html('<script>jQuery(function() {if (jQuery("body.notebook_app").length == 0) { jQuery(".input_area").toggle(); jQuery(".prompt").toggle();}});</script>', raw=True)
# This line will add a button to toggle visibility of code blocks, for use with the HTML export version
di.display_html('''<button onclick="jQuery('.input_area').toggle(); jQuery('.prompt').toggle();">Toggle code</button>''', raw=True)
Para el Hito 2 se realizarán distintos algoritmos de clasificación con el objetivo de predecir si los clientes de la compañía de telecomunicaciones se irán de esta o no, analizando las características propias de los clientes y los productos que tienen contratados con la empresa de telecomunicaciones.
Lo primero que se debe realizar es una transformación de los datos no numéricos, por lo tanto, para cada variable del dataset, se crearon tantas columnas nuevas como posibles opciones tiene dicha variable, utilizando dummies para identificar todos los casos, por ejemplo, para la variable género (gender) cuyas posibles opciones son “Male” y “Female”, se crearon dos nuevas columnas, una llamada Male que toma el valor 1 si el cliente es hombre y 0 si no, y otra para el caso contrario (“Female”), posteriormente no se consideró la columna original para ejecutar los algoritmos.
También es importante mencionar que no se consideró el código ID de las personas, debido a que, no entregaba información determinante, ni tampoco la columna del monto total pagado (TotalCharges), ya que, presentaba una alta correlación (0,83) con la variable del tiempo de la persona en la compañía (tenure).
import pandas as pd
import numpy as np
# transformamos los datos numericos representados en otro formato a float
data = pd.read_csv('churn.csv') # abrimos el archivo csv y lo cargamos en data.
data['tenure']=data['tenure'].astype('float')
data['Churn'] = data['Churn'].map({'Yes': 1, 'No': 0})
# extrae las columnas que presentan datos no-numericos
names=data.columns.values.tolist()
name=np.array([names[1],names[3],names[4],names[6],names[7],names[8],names[9],names[10],names[11],names[12],
names[13],names[14],names[15],names[16],names[17]])
# crea una columna por cada clase de dato en una columna
for i in name:
data=pd.get_dummies(data,columns=[i])
a=[]
nn=len(data.columns.values.tolist())
for i in range(1,nn):
if i == 5 or i == 4:
next
else:
a.append(i)
dchurn=data[data.columns[5]] # clases
data=data[data.columns[a]] # dataset
A continuación se puede observar el desbalanceo de las clases.
print("Distribucion de clases original")
dchurn.value_counts()
En primer lugar se corrieron los modelos de clasificación con la base original, implementando árboles de decisión, super vector machine (SVM), naive bayes, random forest y k-nearest neighbors (knn). A través de estos métodos se obtuvieron métricas accuracy promedios menores a 0.8 y una mala predicción para la fuga de los clientes, la cual es la variable más relevante a estimar para este problema. Lo anterior es producido por el desbalance que existe entre las clases, en donde la clase 0 o de los clientes que se mantienen en la compañía presenta 5174 datos, mientras que para la clase 1 o de los clientes que cambiaron de compañía solo se tienen 1869 datos. Para solucionar este problema se utilizan las técnicas de oversampling y subsampling.
## RESPUESTA A PREGUNTA 2.1
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn import metrics, model_selection
from sklearn.metrics import classification_report
from sklearn.svm import SVC # support vector machine classifier
from sklearn.naive_bayes import GaussianNB # naive bayes
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import confusion_matrix
from numpy.core.umath_tests import inner1d
# datos originales
X_orig = data # todo hasta la penultima columna
y_orig = dchurn
# Arbol de decision
clf_orig = DecisionTreeClassifier()
print("## Arbol de decisión \n")
predictions = model_selection.cross_val_predict(clf_orig, X_orig, y_orig, cv=10) ## cv es la cantidad de folds
print("Accuracy:", metrics.accuracy_score(y_orig, predictions))
print("Metricas:")
print(metrics.classification_report(y_orig, predictions))
cm1=confusion_matrix(y_orig, predictions)
# Implementacin de KNN
K = 5 # numero de vecinos
knn = KNeighborsClassifier(n_neighbors=K)
print("## KNN \n")
predictions = model_selection.cross_val_predict(knn, X_orig, y_orig, cv=10) ## cv es la cantidad de folds
print("Accuracy:", metrics.accuracy_score(y_orig, predictions))
print("Metricas:")
print(metrics.classification_report(y_orig, predictions))
cm2=confusion_matrix(y_orig, predictions)
# naives bayes
gauss = GaussianNB()
print("## Naives Bayes \n")
predictions = model_selection.cross_val_predict(gauss, X_orig, y_orig, cv=10) ## cv es la cantidad de folds
print("Accuracy:", metrics.accuracy_score(y_orig, predictions))
print("Metricas:")
print(metrics.classification_report(y_orig, predictions))
cm3=confusion_matrix(y_orig, predictions)
# SVM
#clf_svm = SVC(gamma='auto')
#print("## SVM \n")
#predictions = model_selection.cross_val_predict(clf_svm, X_orig, y_orig, cv=10) ## cv es la cantidad de folds
#print("Accuracy:", metrics.accuracy_score(y_orig, predictions))
#print("Metricas:")
#print(metrics.classification_report(y_orig, predictions))
#cm4=confusion_matrix(y_orig, predictions)
# Random Forest
clf_rf = RandomForestClassifier()
print("## Random Forest \n")
predictions = model_selection.cross_val_predict(clf_rf, X_orig, y_orig, cv=10) ## cv es la cantidad de folds
print("Accuracy:", metrics.accuracy_score(y_orig, predictions))
print("Metricas:")
print(metrics.classification_report(y_orig, predictions))
cm5=confusion_matrix(y_orig, predictions)
A continuación se presentan las matrices de confusión para cada clasificador, en donde queda en evidencia el desbalanceo de las clases, ya que, los verdaderos positivos son cerca de 5 veces los verdaderos negativos.
# print(__doc__)
import itertools
import numpy as np
import matplotlib.pyplot as plt
from sklearn import svm, datasets
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix
# import some data to play with
class_names = [0,1]
def plot_confusion_matrix(cm, classes,
normalize=False,
title='Confusion matrix',
cmap=plt.cm.Blues):
if normalize:
cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
plt.imshow(cm, interpolation='nearest', cmap=cmap)
plt.title(title)
plt.colorbar()
tick_marks = np.arange(len(classes))
plt.xticks(tick_marks, classes, rotation=45)
plt.yticks(tick_marks, classes)
fmt = '.2f' if normalize else 'd'
thresh = cm.max() / 2.
for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
plt.text(j, i, format(cm[i, j], fmt),
horizontalalignment="center",
color="white" if cm[i, j] > thresh else "black")
plt.ylabel('Clase Real')
plt.xlabel('Clase Predicta')
plt.tight_layout()
# matriz de confusion para Arbol de decision
cnf_matrix = cm1
np.set_printoptions(precision=2)
plt.figure()
plot_confusion_matrix(cnf_matrix, classes=class_names,
title='Matriz de confusión Árbol de decisión')
# matriz de confusion para KNN
cnf_matrix = cm2
np.set_printoptions(precision=2)
plt.figure()
plot_confusion_matrix(cnf_matrix, classes=class_names,
title='Matriz de confusión KNN')
# matriz de confusion para Naive Bayes
cnf_matrix = cm3
np.set_printoptions(precision=2)
plt.figure()
plot_confusion_matrix(cnf_matrix, classes=class_names,
title='Matriz de confusión Naive Bayes')
# matriz de confusion para SVM
#cnf_matrix = cm4
#np.set_printoptions(precision=2)
#plt.figure()
#plot_confusion_matrix(cnf_matrix, classes=class_names,
#title='Matriz de confusión SVM')
# matriz de confusion para Random forest
cnf_matrix = cm5
np.set_printoptions(precision=2)
plt.figure()
plot_confusion_matrix(cnf_matrix, classes=class_names,
title='Matriz de confusión Rendom Forest')
plt.show()
Se crean los dataset con oversampling y subsampling
data2 = pd.concat([data, dchurn], axis=1)
# oversampling sobre la clase 1
idx = np.random.choice(data2.loc[data2.Churn == 1].index, size=3305)
data_oversampled = pd.concat([data2, data2.iloc[idx]])
X_over = data_oversampled[data_oversampled.columns[:-1]]
y_over = data_oversampled[data_oversampled.columns[-1]]
# subsampling sobre la clase 0
idx = np.random.choice(data2.loc[data2.Churn == 0].index, size=3305, replace=False)
data_subsampled = data2.drop(data2.iloc[idx].index)
X_sub = data_subsampled[data_subsampled.columns[:-1]]
y_sub = data_subsampled[data_subsampled.columns[-1]]
Al realizar las técnicas de oversampling y subsampling, se pudo apreciar que para el árbol de decisión con subsampling los resultados fueron peores que para los datos originales, esto se debe principalmente a que en esta técnica se eliminan datos para balancear las clases, por lo tanto, se pasa de un dataset de 7043 datos a uno de 3738, lo que es casi la mitad. Los clasificadores dependen altamente de la cantidad de datos ingresados, generalmente una mayor cantidad de datos trae mejores resultados, por lo tanto, al acotar el dataset se obtienen peores resultados. Por esta razón, todos los clasificadores fueron entrenados con oversampling, ya que se presentan mejores resultados que con los datos originales, en donde los accuracys se encuentran entre 0,75 y 0,9.
## RESPUESTA A PREGUNTA 2.1
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.model_selection import cross_val_score
## oversampling
clf_over = DecisionTreeClassifier()
from sklearn import metrics, model_selection
print("## Árbol de decisión con Oversampling \n")
pred_over = model_selection.cross_val_predict(clf_over, X_over, y_over, cv=10) ## cv es la cantidad de folds
print("Accuracy:", metrics.accuracy_score(y_over, pred_over))
print("Metricas:")
print(metrics.classification_report(y_over, pred_over))
cm1=confusion_matrix(y_over, pred_over)
## subsampling
clf_sub = DecisionTreeClassifier()
from sklearn import metrics, model_selection
print("## Árbol de decisión con Subsampling \n")
pred_sub = model_selection.cross_val_predict(clf_sub, X_sub, y_sub, cv=10) ## cv es la cantidad de folds
print("Accuracy:", metrics.accuracy_score(y_sub, pred_sub))
print("Metricas:")
print(metrics.classification_report(y_sub, pred_sub))
# Implementacin de KNN
K = 5 # numero de vecinos
knn = KNeighborsClassifier(n_neighbors=K)
print("## KNN \n")
predictions = model_selection.cross_val_predict(knn, X_over, y_over, cv=10) ## cv es la cantidad de folds
print("Accuracy:", metrics.accuracy_score(y_over, predictions))
print("Metricas:")
print(metrics.classification_report(y_over, predictions))
cm2=confusion_matrix(y_over, predictions)
# naives bayes
gauss = GaussianNB()
print("## Naives Bayes \n")
predictions = model_selection.cross_val_predict(gauss, X_over, y_over, cv=10) ## cv es la cantidad de folds
print("Accuracy:", metrics.accuracy_score(y_over, predictions))
print("Metricas:")
print(metrics.classification_report(y_over, predictions))
cm3=confusion_matrix(y_over, predictions)
# SVM
#clf_svm = SVC(gamma='auto')
#print("## SVM \n")
#predictions = model_selection.cross_val_predict(clf_svm, X_over, y_over, cv=10) ## cv es la cantidad de folds
#print("Accuracy:", metrics.accuracy_score(y_over, predictions))
#print("Metricas:")
#print(metrics.classification_report(y_over, predictions))
#cm4=confusion_matrix(y_over, predictions)
# Random Forest
clf_rf = RandomForestClassifier()
print("## Random Forest \n")
predictions = model_selection.cross_val_predict(clf_rf, X_over, y_over, cv=10) ## cv es la cantidad de folds
print("Accuracy:", metrics.accuracy_score(y_over, predictions))
print("Metricas:")
print(metrics.classification_report(y_over, predictions))
cm5=confusion_matrix(y_over, predictions)
A continuación se presentan las matrices de confusión de los clasificadores con oversampling, en ellas se puede apreciar el balanceo de los datos, ya que, los verdaderos positivos y verdaderos negativos son muy semejantes.
# print(__doc__)
import itertools
import numpy as np
import matplotlib.pyplot as plt
from sklearn import svm, datasets
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix
# import some data to play with
class_names = [0,1]
# matriz de confusion para Arbol de decision
cnf_matrix = cm1
np.set_printoptions(precision=2)
plt.figure()
plot_confusion_matrix(cnf_matrix, classes=class_names,
title='Matriz de confusión Árbol de decisión')
# matriz de confusion para KNN
cnf_matrix = cm2
np.set_printoptions(precision=2)
plt.figure()
plot_confusion_matrix(cnf_matrix, classes=class_names,
title='Matriz de confusión KNN')
# matriz de confusion para Naive Bayes
cnf_matrix = cm3
np.set_printoptions(precision=2)
plt.figure()
plot_confusion_matrix(cnf_matrix, classes=class_names,
title='Matriz de confusión Naive Bayes')
# matriz de confusion para SVM
#cnf_matrix = cm4
#np.set_printoptions(precision=2)
#plt.figure()
#plot_confusion_matrix(cnf_matrix, classes=class_names,
#title='Matriz de confusión SVM')
# matriz de confusion para Random forest
cnf_matrix = cm5
np.set_printoptions(precision=2)
plt.figure()
plot_confusion_matrix(cnf_matrix, classes=class_names,
title='Matriz de confusión Rendom Forest')
plt.show()
De los resultados mostrados anteriormente, se puede observar que el modelo random forest es el que entrega mejores resultados con un 90% de accuracy. De este modelo es posible obtener la importancia de cada variable en el proceso de clasificación, a continuación se encuentran todas las variables ordenadas por su varianza explicada, es decir, su importancia dentro de la predicción, en donde se puede ver que las características más importantes son tenure, MonthlyCharges y Contract_Month-to-month, lo que tiene relación con las hipotesis planteadas anteriormente, en donde se postulaba que los clientes con mayor tiempo o mayor tenure en la compañia tenían mayor afinidad con ésta y por ende, más posibilidades de permanecer a ella. Con respecto a cargos mensuales se postuló que que las personas con mayor gastos eran mas propensas a cambiar de compañía para disminuir estos gastos. Finalmente, con el tipo de contrato, se vio en el análisis exploratorio que las personas con contratos mes a mes eran mas propensas a irse de la compañía.
clf_rf.fit(X_over,y_over)
print('\nCaracteristicas ordenadas por importancia (RF)')
feature_importances = clf_rf.feature_importances_
importance_order = np.argsort(-feature_importances)
feature_names = data.columns.values.tolist()
for index in importance_order:
print('\t%.3f %s' % (feature_importances[index], feature_names[index]))