import pandas as pd

iris = pd.read_csv('../datasets/iris.csv')

# Exiba informações sobre o dataset
iris.info()

# Exiba as classes presentes nesse dataset usando o método unique() na coluna "Class"
iris['Class'].unique()

# Use o método describe() para exibir estatísticas sobre o dataset
iris.describe()

"""
Explanation: Problemas de classificação representam uma ampla categoria de problemas de machine learning que envolvem a previsão de valores dentro de um conjunto finito e discreto de casos.
Neste exemplo, construiremos um classificador para prever a qual espécie uma flor pertence.
Leitura dos dados
End of explanation
"""


# Criação de um scatterplot dos valores as colunas "Sepal_length" e "Sepal_width"
import matplotlib.pyplot as plt
%matplotlib inline

sl = iris['Sepal_length']
sw = iris['Sepal_width']

# Crie um scatterplot dessas duas propriedades usando a função plt.scatter()
# Atribua cores diferentes a cada exemplo do dataset de acordo com a classe à qual ele pertence
plt.scatter(sl[iris['Class'] == 'Iris-setosa'], sw[iris['Class'] == 'Iris-setosa'], color='red')
plt.scatter(sl[iris['Class'] == 'Iris-versicolor'], sw[iris['Class'] == 'Iris-versicolor'], color='green')
plt.scatter(sl[iris['Class'] == 'Iris-virginica'], sw[iris['Class'] == 'Iris-virginica'], color='blue')
# Atribua labels aos eixos X e Y
plt.xlabel('Sepal Length')
plt.ylabel('Sepal Width')
# Exiba o gráfico
plt.show()

# Criação de um scatterplot dos valores as colunas "Petal_length" e "Pepal_width"
pl = iris['Petal_length']
pw = iris['Petal_width']

# Crie um scatterplot dessas duas propriedades usando a função plt.scatter()
# Atribua cores diferentes a cada exemplo do dataset de acordo com a classe à qual ele pertence
plt.scatter(pl[iris['Class'] == 'Iris-setosa'], pw[iris['Class'] == 'Iris-setosa'], color='red')
plt.scatter(pl[iris['Class'] == 'Iris-versicolor'], pw[iris['Class'] == 'Iris-versicolor'], color='green')
plt.scatter(pl[iris['Class'] == 'Iris-virginica'], pw[iris['Class'] == 'Iris-virginica'], color='blue')
# Atribua labels aos eixos X e Y
plt.xlabel('Petal Length')
plt.ylabel('Petal Width')
# Exiba o gráfico
plt.show()

"""
Explanation: Visualização dos dados
End of explanation
"""


X = iris.drop('Class', axis=1) # Crie um DataFrame com todas as features através da remoção da coluna "Class"
t = iris['Class'].values # Pegue os valores da coluna "Class"
RANDOM_STATE = 4321

# Use o método train_test_plit() para dividir os dados em dois conjuntos
from sklearn.model_selection import train_test_split

Xtr, Xts, ytr, yts = train_test_split(X, t, random_state=RANDOM_STATE)

# Use o conjunto de treinamento para construir um modelo LogisticRegression
from sklearn.linear_model import LogisticRegression

lr = LogisticRegression().fit(Xtr, ytr) # Crie e treine um objeto LogisticRegression aqui

# Use o método score() do objeto LogisticRegression para avaliar a acurácia do modelo
lr.score(Xtr, ytr)

# Use o método score() do objeto LogisticRegression para avaliar a acurácia
# do modelo no conjunto de teste
lr.score(Xts, yts)

"""
Explanation: Classificação de espécies
Usaremos a classe LogisticRegression do scikit-learn para construir o classificador.
End of explanation
"""


# scikit-learn implementa uma função chamada "classification_report" que calcula as três métricas acima
# para um dado classificador.
from sklearn.metrics import classification_report

# Use essa função para exibir as métricas de classificação no modelo treinado anteriormente
# http://scikit-learn.org/stable/modules/generated/sklearn.metrics.classification_report.html
print(classification_report(yts, lr.predict(Xts)))

"""
Explanation: Inspeção dos resultados
Cálculos como o realizado acima geralmente não representam bem aquilo que queremos avaliar quando estamos resolvendo um problema de classificação. Ele apenas retorna o erro médio obtido entre as previsões e as classes reais do dataset de treinamento.
Pense, por exemplo, no que aconteceria se você estivesse treinando um modelo para classificar se uma pessoa possui ou não uma doença em um contexto onde sabe-se que, normalmente, 99% ads pessoas não têm essa doença. O que poderia dar errado se calculássemos a taxa de erros e acertos do modelo como uma forma de avaliá-lo? Dica: qual seria o valor dessa taxa de erros/acertos para um classificador "hardcoded" que sempre retorna 0 (isto é, ele sempre diz que a pessoa não tem a doença)?
Métricas simples de acurácia geralmente não são recomendadas para problemas de classificação. Existem pelo menos três métricas que costumam ser usadas dependendo do contexto:

Precisão: esse número responde à seguinte pergunta: dentre os exemplos que o classificador disse que pertencem a uma classe, quantos de fato pertencem a ela?
Recall: esse número responde a uma pergunta levemente diferente da mostrada na Precisão: dentre os exemplos que realmente pertencem a uma classe, quantos o classificador conseguiu identificar?
F1-Score: essa métrica representa uma soma ponderada de precisão e recall - ela não apresenta uma interpretação intuitiva, mas a ideia é que o f1-score representa um meio-termo entre precisão e recall.

<img src='../images/Precisionrecall.svg'></img>
Source: https://en.wikipedia.org/wiki/Precision_and_recall
Outras métodos de avaliação para mdodelos de classificação incluem análise de curva ROC e, relacionada a essa técnica, o conceito de área sob a curva ROC.
Qual dessas métricas você priorizaria para o exemplo do classificador de doença descrito no parágrafo anterior? Quais são os custos para falsos positivos e falsos negativos nesse caso?
End of explanation
"""


from sklearn.metrics import confusion_matrix

# Use a função confusion_matrix para entender quais classes estão sendo classificadas incorretamente
# http://scikit-learn.org/stable/modules/generated/sklearn.metrics.confusion_matrix.html
confusion_matrix(yts, lr.predict(Xts))

"""
Explanation: Outra técnica útil para inspecionar os resultados gerados por um modelo de classificação é a checagem da matriz de confusão. A matriz de confusão é uma matriz de dimensões K x K (onde K é o número de classes que o classificador pode identificar) que mostra, na posição (i,j), quantos exemplos pertencentes à classe i foram classificados como pertencentes à classe j.
Isso pode trazer insights a respeito de quais classes possuem a maior quantidade de classificações incorretas, por exemplo, e que portanto poderiam receber uma maior atenção por parte da pessoa cientista de dados.
End of explanation
"""


