%matplotlib inline

import os
import sys 

# Modify the path 
sys.path.append("..")

import pandas as pd
import yellowbrick as yb
import matplotlib.pyplot as plt 

from yellowbrick.classifier import ROCAUC
from sklearn.model_selection import train_test_split

occupancy = pd.read_csv('data/occupancy/occupancy.csv')
features = [
    "temperature", "relative humidity", "light", "C02", "humidity"
]
classes = ["unoccupied", "occupied"]
X = occupancy[features]
y = occupancy['occupancy']

# Create the train and test data
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

"""
Explanation: What's going on with ROCAUC for Binary Classification?
We've identified a bug in ROCAUC:
```
ERROR: Test ROCAUC with a binary classifier
Traceback (most recent call last):
  File "/Users/benjamin/Repos/ddl/yellowbrick/tests/test_classifier/test_rocauc.py", line 110, in test_binary_rocauc
    s = visualizer.score(X_test, y_test)
  File "/Users/benjamin/Repos/ddl/yellowbrick/yellowbrick/classifier/rocauc.py", line 171, in score
    self.fpr[i], self.tpr[i], _ = roc_curve(y, y_pred[:,i], pos_label=c)
IndexError: too many indices for array
```
Let's see if we can figure out where it's getting triggered.
End of explanation
"""


from sklearn.tree import DecisionTreeClassifier
from sklearn.neural_network import MLPClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.ensemble import RandomForestClassifier, AdaBoostClassifier
from sklearn.discriminant_analysis import QuadraticDiscriminantAnalysis

classifiers = [
    AdaBoostClassifier(),
    MLPClassifier(),
    DecisionTreeClassifier(),
    QuadraticDiscriminantAnalysis(),
    DecisionTreeClassifier(),
    RandomForestClassifier(),
   ]

for classifier in classifiers:
    oz = ROCAUC(classifier)
    oz.fit(X_train, y_train)
    oz.score(X_test, y_test)
    g = oz.show()

"""
Explanation: Binary Classification with 1D Coefficients or Feature Importances
When the function has 1D coefficients, we don't seem to have a problem
End of explanation
"""


from sklearn.svm import LinearSVC, NuSVC, SVC
from sklearn.linear_model import SGDClassifier
from sklearn.naive_bayes import BernoulliNB, MultinomialNB
from sklearn.linear_model import PassiveAggressiveClassifier
from sklearn.linear_model import RidgeClassifier, RidgeClassifierCV
from sklearn.linear_model import LogisticRegression, LogisticRegressionCV

"""
Explanation: Looks good; everything works!
Binary Classification with Multidimensional Coefficients or Feature Importances
What about classification with estimators that have multidimensional coefficients? Thanks to ZJ Poh for identifying these in this PR.
End of explanation
"""


classifiers = [
    BernoulliNB(),
    MultinomialNB(),
    LogisticRegression(),
    LogisticRegressionCV()
]

for classifier in classifiers:
    oz = ROCAUC(classifier)
    oz.fit(X_train, y_train)
    oz.score(X_test, y_test)
    g = oz.show()

oz = ROCAUC(LinearSVC())
oz.fit(X_train, y_train)
oz.score(X_test, y_test)
oz.show()

oz = ROCAUC(SVC())
oz.fit(X_train, y_train)
oz.score(X_test, y_test)
oz.show()

oz = ROCAUC(SGDClassifier())
oz.fit(X_train, y_train)
oz.score(X_test, y_test)
oz.show()

oz = ROCAUC(PassiveAggressiveClassifier())
oz.fit(X_train, y_train)
oz.score(X_test, y_test)
oz.show()

oz = ROCAUC(RidgeClassifier())
oz.fit(X_train, y_train)
oz.score(X_test, y_test)
oz.show()

oz = ROCAUC(RidgeClassifierCV())
oz.fit(X_train, y_train)
oz.score(X_test, y_test)
oz.show()

"""
Explanation: Some of these generate the IndexError: too many indices for array error, but not all!
These are the ones that seem to work: BernoulliNB(), MultinomialNB(), LogisticRegression(), and LogisticRegressionCV().
End of explanation
"""


attrs = (
    'predict_proba',
    'decision_function',
)

failing_classifiers = [
    LinearSVC(),                   
    SVC(),                     
    SGDClassifier(),              
    PassiveAggressiveClassifier(), 
    RidgeClassifier(),             
    RidgeClassifierCV()
]

def profile(classifiers):
    for classifier in classifiers:
        classifier.fit(X_train, y_train)
        # Return the first resolved function
        for attr in attrs:
            try:
                method = getattr(classifier, attr, None)
                if method:
                    y_pred = method(X_test)
            except AttributeError:
                continue
        print("y_pred shape for {} is {}.".format(
            classifier.__class__.__name__, y_pred.shape)
        ) 
        print(y_pred)

profile(failing_classifiers)

"""
Explanation: so what's going on here?
The Shape of y_pred
It looks like all of the classifiers that trigger the IndexError during binary classification with ROCAUC are ones that have only a decision_function and for which y_pred.shape is (n_samples,).
Classifiers that Raise the IndexError with Binary Classification & ROCAUC

LinearSVC()
SVC()
SGDClassifier()
PassiveAggressiveClassifier()
RidgeClassifier()
RidgeClassifierCV()
End of explanation
"""


working_classifiers_decision = [
    AdaBoostClassifier(),
    QuadraticDiscriminantAnalysis(),
    LogisticRegression(),
    LogisticRegressionCV()
]

profile(working_classifiers_decision)

"""
Explanation: Classifiers that Currently Work with Binary Classification & ROCAUC
The classifiers with decision functions and y_pred.shape (n_samples, ) that do work with ROCAUC for binary classification seem to work because they also have predict_proba:
End of explanation
"""


working_classifiers_proba = [
    MLPClassifier(),
    DecisionTreeClassifier(),
    RandomForestClassifier(),
    BernoulliNB(),
    MultinomialNB()
]

profile(working_classifiers_proba)

"""
Explanation: Sklearn Documentation

AdaBoostClassifier()
QuadraticDiscriminantAnalysis()
LogisticRegression()
LogisticRegressionCV()
End of explanation
"""


