"""
## Regressão Linear
"""
"""
Até agora foram vistos alguns conceitos de classificação. Outro importante aspecto do aprendizado supervisionado são os modelos de aprendizagem para problemas de regressão. Nestes problemas, a saída esperada é contínua, em vez de valores discretos como na classificação.

Um conjunto de dados de propaganda é disponibilizado junto com esse notebook, onde cada linha indica um tipo de mercado diferente. Importe os dados para visualizar a estrutura deles:
"""
import pandas as pd

data = pd.read_csv('../input/ad.data', index_col=0)
display(data.head())
data.shape
"""
Quais são os atributos?
- **TV:** dólares gastos em propagandas veiculadas na TV para um produto em determinado mercado (em milhões)
- **Radio:** dólares gastos em propagandas veiculadas em rádio
- **Newspaper:** dólares gastos com propaganda em jornais

Qual o rótulo da amostra?
- **Sales:** vendas (em milhares) de um determinado item em um mercado específico
"""
"""
Para facilitar, os dados também poderiam ser visualizados para compreender melhor com a distribuição, e se há alguma tendência.
"""
import seaborn as sns
%matplotlib inline

sns.pairplot(data, x_vars=['TV','Radio','Newspaper'], y_vars='Sales', size=7, aspect=0.7, kind='reg')
"""
** Exercícios **

1- O que é possível perceber com relação à linha de tendência entre as variáveis do eixo X (atributos) e o eixo Y (rótulo)?

Resposta: Foi possível perceber que as linhas de tendencia demonstram com clareza o quão dispersos estão os valores dos respectivos atributos. Por exemplo, com o atributo "TV" os pontos se aglomeram entorno da linha de tendencia, produzindo assim uma linha de tendencia mais precisaa e com uma margem de erro menor. Pegando-se como exemplo o atributo "Newpapers" com um valor elevador terá algumas possíveis respostas, onde os resultados são distintos por terem uma maior taxa de erro e uma pior distribuição.


2- Visualmente, qual atributo parece ter uma correlação maior com a variável de saída "Sales"?

Resposta: O atributo "TV" aparenta ter uma correlação muito grande com a variável de saída "Sales", visto que os pontos estão melhores distribuidos, produzindo uma linha de tendencia com uma margem de erro menor do que os demais atributos.

"""
"""
## Regressão Linear

Uma das formas de resolver um problema desse tipo é aplicando a regressão linear. Nesse método, procura-se uma função capaz de traçar uma linha de tendência que servirá para prever um valor futuro em determinadas condições. A vantagem é que é um modelo matematicamente simples de interpretar. A desvantagem é que dificilmente levará a uma acurácia satisfatória.
"""
"""
### Forma da regressão linear:

$y = \beta_0 + \beta_1x_1 + \beta_2x_2 + ... + \beta_nx_n$

- $y$ é a saída
- $\beta_0$ é um valor de bias (deslocamento)
- $\beta_1$ é o coeficiente de $x_1$ (do primeiro atributo)
- $\beta_n$ é o coeficiente de $x_n$ (do n-ésimo atributo)

Assim, para esse problema, tem-se:

$y = \beta_0 + \beta_1 \times TV + \beta_2 \times Radio + \beta_3 \times Newspaper$

Os valores $\beta$ são chamados de **coeficientes de modelo**. Esses valores são "aprendidos" durante a etapa de aprendizagem, a qual acontece com o ajuste do modelo utilizando o critério dos "quadrados mínimos". Uma vez aprendidos esses parâmetros, o modelo pode ser usado de forma preditiva!
"""
"""
## Separando os atributos da classe

É importante separar, assim como na classificação, quais são os atributos e quais são as classes.
Veja uma forma diferente de fazer isso:
"""
atributos = ['TV', 'Radio', 'Newspaper']

X = data[atributos]
X.head()
y = data['Sales']
y.head()
"""
Em seguida, é necessário dividir quais dados serão apresentados na etapa de treino e quais dados serão apresentados na etapa de teste:
"""
from sklearn.model_selection import train_test_split

# omitindo o parametro test_size, considera-se 30%
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.3, random_state=1) 

print(X_train.shape)
print(y_train.shape)
print(X_test.shape)
print(y_test.shape)
"""
## Regressão linear no scikit-learn

Para aplicar o método de regressão linear (LinearRegression), será feito algo bem similar ao kNN.
"""
from sklearn.linear_model import LinearRegression

reglin = LinearRegression()

reglin.fit(X_train, y_train)
"""
O modelo, uma vez treinado, permite que seja acessado os valores do modelo matemático:
"""
print(reglin.intercept_)
print(reglin.coef_)
list(zip(atributos, reglin.coef_)) # o comando zip junta dois vetores e forma tuplas
"""
$$y = 2.93 + 0.0466 \times TV + 0.179 \times Radio + 0.00345 \times Newspaper$$

Como interpretar **TV coefficient** (0.0466)?

- Para uma dada quantia gasta com Radio e Newspaper, **uma "unidade" a mais em gasto com TV** está associada com **0.0466 "unidades" a mais de vendas**.
- Ou simplificando: Para uma dada quantia gasta com de Radio e Newspaper, **um adicional de $1.000 gastos com TV** reflete diretamente na **venda de 46.6 itens**, segundo esse modelo.
"""
"""
E como o modelo poderia ser utilizado para fazer predições?
Suponha que tenha sido gasto 100 milhões com propagandas em TV, 20 milhões em rádios e 15 milhões em jornais.
"""
reglin.predict([[100,20,15]])
"""
Um comentário importante é que o método predict espera um vetor 2d, ou seja, mesmo que seja desejado prever o valor de 1 amostra, é importante passá-la dentro de um vetor externo (por isso dois colchetes no começo e no fim).

No cenário dado, era esperado a venda de **11,1 milhões** de itens de produto.
"""
"""
**Exercícios**

3 - Qual cenário seria suficiente para que um produto vendesse mais de 30 milhões de unidades? Utilize a célula seguinte para mostrar o código
"""
# CÓDIGO DO EXERCÍCIO 3
reglin.predict([[200,100,80]])
"""
4 - Simule dois cenários ao mesmo tempo utilizando uma única chamada de reglin.predict. Utilize a célula abaixo.
"""
# CÓDIGO DO EXERCÍCIO 4
reglin.predict([[200,100,80],[100,20,15]])
"""
5 - Qual é a saída para um cenário que foi gasto 0 dólares tanto com TV, como Radio e Newspaper? Qual a explicação para o valor? Utilize a célula abaixo para simular.
"""
"""
$$y = 2.93 + 0.0466 \times TV + 0.179 \times Radio + 0.00345 \times Newspaper$$

O valor mostrado é representado pelo fato da função possuir um deslocamento na qual a reta se inicia, no caso 2,93.. podendo ser observado na função descrita acima 
"""
# CÓDIGO DO EXERCÍCIO 5
reglin.predict([[0,0,0]])
"""
6 - De acordo com os coeficientes aprendidos, ordene do maior para o menor a ordem de impacto deles no número de vendas:

Resposta: Radio Possuí um impacto em vendas de 17,9%, TV possuí um impacto em vendas de 4,66%, Newspaper possuí um impacto em vendas de 0,345%, segundo a função de regressão linear.
"""
"""
## Avaliação de modelos de regressão

Métricas como acurácia são comuns em problemas de classificação, mas não se aplicam em problemas de regressão. No lugar desta, são utilizadas métricas específicas para valores contínuos.

Serão utilizados alguns exemplos para observar o comportamento de **três métricas comuns** para problemas de regressão:
"""
true = [100, 50, 30, 20]
pred = [90, 50, 50, 30]
"""
**Mean Absolute Error** (MAE) é a média absoluta dos erros:

$$\frac 1n\sum_{i=1}^n|y_i-\hat{y}_i|$$
"""
# calculando MAE na mão
print((10 + 0 + 20 + 10)/4.)

# calculando MAE com scikit
from sklearn import metrics
print(metrics.mean_absolute_error(true, pred))
"""
**Mean Squared Error** (MSE) é a média do quadrado dos erros:

$$\frac 1n\sum_{i=1}^n(y_i-\hat{y}_i)^2$$
"""
# calculando MSE na mão
print((10**2 + 0**2 + 20**2 + 10**2)/4.)

# calculando MSE com scikit
print(metrics.mean_squared_error(true, pred))
"""
**Root Mean Squared Error** (RMSE) é a raiz quadrada da média do quadrado dos erros:

$$\sqrt{\frac 1n\sum_{i=1}^n(y_i-\hat{y}_i)^2}$$
"""
# calculando RMSE na mão
import numpy as np
print(np.sqrt((10**2 + 0**2 + 20**2 + 10**2)/4.))

# calculando RMSE com scikit
print(np.sqrt(metrics.mean_squared_error(true, pred)))
"""
A comparação entre as diferentes métricas:

- **MAE** é a mais fácil de entender, porque é uma média.
- **MSE** é mais popular que a MAE porque pune erros maiores.
- **RMSE** é ainda mais popular porque, além das características anteriores, é interpretável em termos da variável de saída.
"""
"""
### Calculando RMSE para as predições de Vendas
"""
y_pred = reglin.predict(X_test)

print(np.sqrt(metrics.mean_squared_error(y_test, y_pred)))
"""
## Seleção de atributos

Alguns métodos não lidam muito bem com atributos que não contribuem para o modelo. Nesses casos, o modelo acaba se prejudicando com atributos particularmente inúteis. Um atributo só perde completamente o valor na regressão linear quando seu coeficiente zera. 

O atributo **Newspaper** tem um valor bem próximo de zero. Vale a pena verificar como o modelo fica sem utilizar esse atributo no treino. Esse tipo de pensamento caracteriza a seleção de atributos. 
"""
atributos = ['TV', 'Radio']

X = data[atributos]
y = data.Sales

X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=1)

reglin.fit(X_train, y_train)

y_pred = reglin.predict(X_test)

print(np.sqrt(metrics.mean_squared_error(y_test, y_pred)))
"""
Como o valor de RMSE **caiu**, é perceptível que nesse cenário testado o atributo não tinha grande contribuição para o modelo. É importante notar que diferente da acurácia (que quanto mais alta melhor), o erro é esperado que seja sempre o menor possível.
"""
"""
** Exercícios **

7 - Para fazer uma análise melhor se o atributo Newspaper realmente é descartável, considere treinar três modelos com separação de treino e teste diferentes de 25%. Considere dividir em 50% e 90% para treino também. Simule os cenários **com** e **sem** o atributo Newspaper. Implemente o código na célula seguinte, preencha com os dados obtidos na tabela da célula em sequência e faça os devidos comentários das suas conclusões e percepções a respeito do experimento.
"""
# CÓDIGO DO EXERCÍCIO 7


import pandas as pd

data = pd.read_csv('../input/ad.data', index_col=0)

atributos = ['TV', 'Radio', 'Newspaper']

X = data[atributos]
X.head()

y = data['Sales']
y.head()


from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.1, random_state=1) # test_size = tamanho do teste

from sklearn.linear_model import LinearRegression

reglin = LinearRegression()

reglin.fit(X_train, y_train)

list(zip(atributos, reglin.coef_))

y_pred = reglin.predict(X_test)

print(np.sqrt(metrics.mean_squared_error(y_test, y_pred)))

"""
Resposta:
    
|Cenário              |  RMSE  |
|---------------------|--------|
| Com, Treino: 75%    | 1,4046 |
| Sem, Treino: 75%     | 1,3879 |
| Com, Treino: 50%    | 1,5248 |
| Sem, Treino: 50%    | 1,5144 |
| Com, Treino: 90%    | 1,5821 |
| Sem, Treino: 90%    | 1,5756 |

Comentários: Notou-se que o atributo 'Newspaper' em comparação com os demais causa um variação relativamente pequena não tendo tanta relevancia para o resultado, podendo ser observado na tabela acima, onde mesmo retirando tal atributo o valor do RMSE permanece em uma faixa quase constante, mas é importante salientar que um valor só perde o valor da regressão linear quando seu coeficiente é zero, tendo isso em mente pode-se dizer que o atributo Newspaper acaba prejudicando o modelo.
"""
"""
## Validação Cruzada com Scikit

Outra forma de analisar os resultados é garantindo que todas as amostras em algum momento façam parte tanto do treino quanto do teste. Para isso, é possível empregar a validação cruzada disponível no Scikit.
"""


from sklearn.model_selection import cross_val_score

X = data[atributos]
y = data.Sales

scores = cross_val_score(LinearRegression(), X, y, cv=10, scoring='neg_mean_squared_error')
scores = np.sqrt(-scores)

print(scores)
print("RMSE: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2))
"""
A interpretação desses resultados é a seguinte: na primeira rodada de 10 (cv=10), o RMSE foi de 1.83; na segunda, foi de 1.81, e assim por diante. É possível observar que a rodada mais difícil para o método foi a sétima, com RMSE de 2.82.

A média e desvio padrão diz bastante também sobre a dificuldade de utilizar esse conjunto de dados: o desvio padrão é quase 50% do valor original da média.

"""
"""
** Exercícios **

8 - Remova a coluna Newspaper e execute o mesmo experimento de validação cruzada. Verifique se há uma melhora geral no desempenho da regressão.
"""
# CÓDIGO DO EXERCÍCIO 8
import pandas as pd

data = pd.read_csv('../input/ad.data', index_col=0)

atributos = ['TV', 'Radio']

X = data[atributos]
X.head()

y = data['Sales']
y.head()

from sklearn.model_selection import cross_val_score

X = data[atributos]
y = data.Sales

scores = cross_val_score(LinearRegression(), X, y, cv=10, scoring='neg_mean_squared_error')
scores = np.sqrt(-scores)

print(scores)
print("RMSE: %0.2f (+/- %0.2f)" % (scores.mean(), scores.std() * 2))
"""
Comentários: O resultado encontrado sofreu uma pequena alteração diminuindo o erro em +/- 0,01, e tambem foi reduzido o RMSE em 0,01, podendo assim concluir, que o atributo Newspaper está prejudicando o modelo.
"""