"""
<font size="10" color="black">Análise Multivariada</font>

Eduardo Chaves Ferreira
"""
"""
## O que será tratado no curso


Correlação

Regressão

Componentes Principais

Clusterização




"""
"""
## Importação de bibliotecas usadas nos exemplos
"""
import numpy as np
import matplotlib.pyplot as plt
import math
import random
import pandas as pd
import scipy.stats as stat
from pandas.plotting import scatter_matrix
import os
import seaborn as sns
from scipy.stats import t
from scipy.stats import norm

pd.options.display.max_rows = 999
pd.options.display.max_columns = 999

path = os.environ['PATH']

if path.startswith('C'):
    IN_KAGGLE = False
else:
    IN_KAGGLE = True
"""
## Funções gerais
"""
def calcula_Z_tstudent(confianca, tamanho_amostra):
    mean = 0
    std = 1
    rv = t(df=(tamanho_amostra-1))
    return rv.interval(confianca)[1]


def calcula_Z_normal(confianca):
    mean = 0
    std = 1
    rv = norm(loc=mean, scale=std)
    return rv.interval(confianca)[1]
def recupera_amostra(populacao, tamanho_amostra):
    tamanho_amostra = int(tamanho_amostra)
    if type(populacao) is pd.DataFrame:
        return populacao.sample(tamanho_amostra)
    else:
        #return populacao[np.random.randint(0, len(populacao), tamanho_amostra)]
        return [populacao[i] for i in np.random.randint(0, len(populacao), tamanho_amostra)]
# Função que calcula intervalo de confiança para média com base em uma amostra e a confiança desejada

def calcula_intervalo_media(amostra, confianca, tamanho_populacao=0):
    tamanho_amostra = len(amostra)
    
    #1-Calcule a média da amostra ex. media_amostra = np.mean(amostra)
    media_amostra = np.mean(amostra)

    #2-Calcule o desvio da amostra ex. desvio_amostra = np.std(amostra)
    desvio_amostra = np.std(amostra)

    #3-Calcule quantos desvios precisará para seu grau de confiânça ex. numero_desvios = calcula_numero_desvios_tstudent_para_confianca(confiança, tamanho_amostra)
    numero_desvios = calcula_Z_tstudent(confianca, tamanho_amostra)

    #4-Calcule o desvio das amostras ex. desvio_amostras = desvio_amostra/np.sqrt(tamanho_amostra)
    desvio_amostras = desvio_amostra/np.sqrt(tamanho_amostra)

    #5-Calcule a margem de erro ex. margem_erro = numero_desvios*desvio_amostras
    margem_erro = numero_desvios*desvio_amostras
    
    if (tamanho_populacao!=0) & (tamanho_amostra>0.05*tamanho_populacao):
        margem_erro = margem_erro * (np.sqrt(tamanho_populacao-tamanho_amostra)/np.sqrt(tamanho_populacao-1))

    #6-Calcule o intervalo ex. inferior = media_amostra-margem_erro, superior = media_amostra+margem_erro
    inferior = media_amostra-margem_erro
    superior = media_amostra+margem_erro

    return inferior,superior
def teste_hipotese_media_duas_amostras_desvio_amostra(significancia, tipo,
                                                        media_amostra_1, tamanho_amostra_1, desvio_amostra_1,
                                                        media_amostra_2, tamanho_amostra_2, desvio_amostra_2,
                                                     diferenca = 0):
    S2 = ( (tamanho_amostra_1-1)*np.power(desvio_amostra_1,2) + (tamanho_amostra_2-1)*np.power(desvio_amostra_2,2) )/ (tamanho_amostra_1+tamanho_amostra_2-2)
    SE = np.sqrt( S2 * (1/tamanho_amostra_1 + 1/tamanho_amostra_2) )
    z = (media_amostra_1 - media_amostra_2 - diferenca)/SE
    if z<0:
        p_value = t.cdf(z, df=(tamanho_amostra_1+tamanho_amostra_2-2))
    else:
        p_value = t.sf(z, df=(tamanho_amostra_1+tamanho_amostra_2-2))
    
    
    if tipo == 'bilateral':
        significancia = significancia/2
    if p_value < significancia:
        return 'H0 rejeitada tendo em vista que p-value={} correspondente a z={} menor que significância {}'.format(p_value,z,significancia)
    else:
        return 'H0 não rejeitada tendo em vista que p-value={} correspondente a z={} maior que significância {}'.format(p_value,z,significancia)
def Tamanho_Amostra_Valor_Pontual(Tamanho_Populacao, Grau_Confianca, Variancia, Margem_Erro):
    Numero_Desvios = calcula_Z_normal(Grau_Confianca)
    return math.ceil( 
            (Tamanho_Populacao * math.pow(Numero_Desvios,2) * Variancia) / \
            ( (Tamanho_Populacao-1)*math.pow(Margem_Erro,2) + math.pow(Numero_Desvios,2)*Variancia )
            )
"""
<font size="6" color="red">Exercício Parte A</font>


Relatório de felicidade por pais da ONU, que mede o índice de felicidade por nação e seus indicadores 

Fonte: https://www.kaggle.com/unsdsn/world-happiness

"""
"""
## Carga e preparação de dados
"""
print(os.listdir('../input'))
worldhappiness = pd.read_csv("../input/world-happiness/2016.csv")
worldhappiness.set_index('Happiness Rank', drop=True, inplace=True)
worldhappiness = worldhappiness.loc[:,[ 'Country', 'Region', 'Happiness Score',  'Economy (GDP per Capita)', 'Family',
       'Health (Life Expectancy)', 'Freedom', 'Trust (Government Corruption)',
       'Generosity']]

worldhappiness = worldhappiness.sort_values(by=['Happiness Rank'], ascending=True)


"""
## Apresentação dos dados
"""
worldhappiness.head()
worldhappiness.tail()
"""
# Distribuição do nível de felicidade no mundo
"""
plt.figure(figsize=(14,6))

_ = worldhappiness['Happiness Score'].hist( bins=50, density=False)

plt.xlabel('Índice de felicidade')
plt.ylabel('Número de países')
plt.title('Histogram do índice de felicidade (quanto maior o índice, mais feliz)')

plt.show()
"""
# EXERCÍCIO

Interprete o gráfico de distribuição de felicidade pelos países, como é a distribuição de felicidade entre os países (há mais países felizes, infelizes ou intermediários)?

Qual o país mais feliz? E o mais infeliz?
Dinamarca. Burundi.

Observação: quanto maior o índice, mais feliz o país.

"""
"""
## Felicidade por continente
"""
_ = worldhappiness.loc[:,['Region','Happiness Score']].groupby(by='Region').mean().sort_values(by=['Happiness Score'], ascending=False).plot(kind='bar',figsize=(14,6))
"""
# EXERCÍCIO
"""
"""
Calcule o índice de correlação entre as variáveis do relatório usando o Coeficiente de correlação de Pearson (utilizado para correlação LINEAR entre variáveis quantitativas). 

O Coeficiente de correlação de Pearson mede a variação conjunta das variáveis em torno da média, variações normalizadas pelo desvio padrão (mais fácil interpretação porque o resultado fica no intervalo [-1 1]). 

Utilize o comando "worldhappiness.corr()"

Qual componente do Índice de Felicidade possui maior correlação com a felicidade do país?
Economy (GDP per Capita)

Qual componente do Índice de Felicidade possui menor correlação com a felicidade do país?
Generosity	

Qual par de componentes possui maior correlação? Economy (GDP per Capita) e Health (Life Expectancy)	


Qual par possui a menor correlação? Economy (GDP per Capita) e Generosity	


"""
worldhappiness.corr()

"""
Uma forma visual e simples de analisar correlações é através de scatter plot, que mostra, de forma gráfica, a combinação de valores de duas colunas, veja o exemplo:
"""
# Aparentemente economia é fortemente correlacionada com felicidade

_ = worldhappiness.plot(figsize=(14,6), kind='scatter', x='Happiness Score', y='Economy (GDP per Capita)' )
"""
# EXERCÍCIO

No gráfico acima, como podemos identificar VISUALMENTE a correlação que verificamos no cálculo anterior (feito com a função df.corr)?
O diagrama de dispersão demonstra que quando a variável "Hapiness Score" aumenta, a variável "Economy" acompanha o crescimento. 


"""
"""
# EXERCÍCIO

Faça um gráfico semelhante ao anterior para as variáveis 'Happiness.Score' e 'Generosity'.

No gráfico é perceptível alguma correlação entre as variáveis? Explique. 

"""
_= worldhappiness.plot(figsize=(14,6), kind='scatter', x='Happiness Score', y='Generosity' )

"""
## Matriz de correlação
"""
"""
Uma forma simples de estudar a correlação entre muitas variáveis é através da matriz de correlação.

Na diagonal da matriz aparecem as distribuições de probabilidade das variáveis.

Nas demais células, são apresentados os gráficos de cruzamento das variáveis (scatter plots),
"""


_ = scatter_matrix(worldhappiness, figsize=(14,10), alpha=0.2, diagonal='kde')

"""
# EXERCÍCIO

Observe a correlação entre 'Happiness.Score' e 'Trust (Government Corruption)'

Qual o valor da correlação calculado pela função worldhappiness.corr()? 0.402032

Faça um scatter plot para as duas variáveis

"""
_= worldhappiness.plot(figsize=(14,6), kind='scatter', x='Happiness Score', y='Trust (Government Corruption)' )
"""
# EXERCÍCIO


Apesar da correlação de Pearson entre 'Happiness.Score' e 'Trust (Government Corruption)' não ser alta (é menor que 0,5), pelo gráfico podemos verificar uma relação entre felicidade e confiança no governo, qual é essa relação?
Quanto mais infeliz, menos confiante no governo.

Existe um país que parece sair da regra (infeliz mas confiante no governo), qual é esse país? Dica: para listar a tabela ordenada por uma coluna use o comando "worldhappiness.sort_values(by=['Trust (Government Corruption)'], ascending=False)"
Rwanda
"""
worldhappiness.sort_values(by=['Trust (Government Corruption)'], ascending=False)
"""
## Correlação não significa causalidade

A existência de correlação entre variáveis não significa, necessariamente, que uma variável afete diretamente o valor da outra.

A correlação indica que há movimentação conjunta de ambas as variáveis, efeito que pode ser produzido por uma terceira variável ou pelo acaso.



"""
"""
# Análise de componentes principais

Quando trabalhamos com múltiplas características de uma população (várias colunas), podemos querer simplificar o trabalho concentrando o estudo nas características que mais contibuem para o resultado do experimento.

No exemplo do nível de felicidade, podemos querer selecionar os indicadores que mais contribuem para o nível de felicidade do país.

A técnica de análise de componentes principais faz a seleção dos componentes com maior importância. Há vários algoritmos nessa técnica, entre eles PCA, Factor Analysis e Select K Best, este último usado no exemplo a seguir:
"""
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import f_regression

entrada = worldhappiness.loc[:,['Economy (GDP per Capita)', 'Family',
       'Health (Life Expectancy)', 'Freedom', 'Trust (Government Corruption)',
       'Generosity']]
saida = worldhappiness.loc[:,['Happiness Score']]

world_happiness_resumido_SelectKBest = SelectKBest(f_regression, k=2).fit_transform(entrada, np.ravel(saida))

fig, ax = plt.subplots(1, 1, figsize=(18,10))
plt.scatter(world_happiness_resumido_SelectKBest[:,0], 
            world_happiness_resumido_SelectKBest[:,1], 
            s=saida.values**3)
plt.grid(True)
plt.xlabel('Economy (GDP per Capita)')
plt.ylabel('Health (Life Expectancy)')
plt.title('Distribuição de felicidade (o tamanho do ponto indica o nível de felicidade)')
plt.tight_layout()
"""
# Exercício

Os dois componentes selecionados como os que mais contribuem para a felicidade foram "Economy (GDP per Capita)" e "Health (Life Expectancy)".

No gráfico acima, cada país aparece como um ponto, cujo tamanho indica o nível de felicidade.

Responda: pelo gráfico é perceptível correlação entre as duas variáveis selecionadas? Sim. Quanto maior o valor de uma variável, maior o da outra.

Pela análise do gráfico percebe-se correlação entre as duas variáveis e o nível de felicidade? Sim, quanto maior o valor das duas variáveis, maior a área do ponto (nível de felicidade).
"""
"""
# Regressão

Regressão significa traçar uma curva que melhor se adapta aos pontos capturados na análise

A curva pode ser uma reta ou outro formato qualquer

O objetivo é estimar valores da variável dependente para valores da variável independente não capturados na amostra

Vamos fazer um exemplo relacionando nível de felicidade e 'Trust (Government Corruption)'. Vamos inicialmente fazer um gráfico somente com os pontos levantados no estudo:

"""
_ = worldhappiness.plot(figsize=(14,6), kind='scatter', x='Happiness Score', y='Trust (Government Corruption)' )
"""
Vamos agora traçar três curvas de regressão: uma reta (azul), uma curva de segundo grau (parábola verde) e uma curva de terceiro grau (amarela):

"""
x = worldhappiness['Happiness Score'].values
y = worldhappiness['Trust (Government Corruption)'].values
f1 = np.poly1d(np.polyfit(x, y, 1))
f2 = np.poly1d(np.polyfit(x, y, 2))
f3 = np.poly1d(np.polyfit(x, y, 3))

fig, ax = plt.subplots(1,1,figsize=(14,6))
plt.plot(x, y, 'ro')
simulacao = np.linspace(x.min(),x.max(),300)
plt.plot(simulacao, f1(simulacao), 'b.', label='Linear')
plt.plot(simulacao, f2(simulacao), 'g.', label='2')
plt.plot(simulacao, f3(simulacao), 'y.', label='3')
plt.xlabel('Happiness Score')
plt.ylabel('Trust (Government Corruption)')
plt.title('Relação felicidade X confiança no governo')
plt.show()
"""
# EXERCÍCIO

Na sua opinião, qual curva melhor se adapta aos dados? A azul, pois reflete a relação entre as variáveis de modo geral: quanto maior a confiança maior a felicidade.

O traçado da curva verde parecece fazer sentido? Não, porque as variáveis se mostram negativamente correlacionadas na primeira metade do gráfico.


"""
"""
<font size="6" color="red">Exercício Parte B</font>


Dados sobre municípios brasileiros

Obs: veja que o IDHM_Rank classifica os municípios do menor valor (melhor IDH) para o maior valor (pior IDH)
"""
"""
## Carga de dados
"""
municipios = pd.read_csv('../input/municpios-brasileiro-resumido/municipios_brasileiro.csv', delimiter=';')

municipios.Educacao = (municipios.Educacao.str.replace(',','.')).astype('float')

municipios.Renda = (municipios.Renda.str.replace(',','.')).astype('float')

municipios = municipios.sort_values(by=['IDHM_Rank'], ascending=True)

municipios.head()
"""
## Correlações
"""
municipios.corr()
"""
# EXERCÍCIO

Explique as correlações altas (acima de 0,9 ou abaixo de -0,9)
Quanto maior a quantidade de residentes, maior a quantidade de carros. 
Quanto maior a renda, mais alta é a posição no ranking (ou seja, menor o número indicador do ranking).
Quanto maior a educação, mais alta é a posição no ranking (ou seja, menor o número indicador do ranking).

"""
"""
## Clusterização

Clusterizar é o processo de agrupar. Quando temos grande quantidade de dados, ao invés de analisarmos individualmente cada linha, podemos agregar as informações em grandes grupos e tentar analisar as características dos grupos

Neste exemplo, vamos agrupar os municípios brasileiros em cinco grupos, considerando população, renda, educação e número de carros:
"""
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler

municipios_resumido = municipios.loc[:,['Pop_residente',  'Renda', 'Educacao',   'CARRO']]
scaler = StandardScaler()
scaler.fit(municipios_resumido)
municipios_resumido = scaler.transform(municipios_resumido)

y_pred = KMeans(n_clusters=5, random_state=1).fit_predict(municipios_resumido)


municipios['Cluster'] = y_pred

municipios.head(10)
"""
Quantidade de municípos por grupo:
"""
municipios.loc[:,['Cluster','Educacao']].groupby(by='Cluster').count()
"""
Desvio padrão do nível de educação em cada cluster
"""
municipios.loc[:,['Cluster','Educacao']].groupby(by='Cluster').std()
"""
Média do nível de educação em cada cluster
"""
municipios.loc[:,['Cluster','Educacao', 'Renda', 'Pop_residente', 'CARRO', 'IDHM_Rank']].groupby(by='Cluster').mean()
"""
Classificação do IDH por grupo:
"""
f, (ax) = plt.subplots(1, 1, figsize=(18,10))
_ = sns.boxplot(x="Cluster", y="IDHM_Rank", data=municipios,  ax=ax)
"""
O grupo 2 possui apenas um município:
"""
municipios.loc[municipios.Cluster == 2,:]
"""
# EXERCÍCIO

Explique como os grupos foram formados (o que caracteriza cada grupo)?
O critério de clusterização foi a semelhança entre as variáveis 'Educacao', 'Renda', 'Pop_residente', 'CARRO' e 'IDHM_Rank'.

"""
"""
<font size="6" color="red">Exercício Parte C - Recaptulação</font>
"""
"""
## Teste de hipótese
"""
"""
# EXERCÍCIO

No exercício anterior verificamos que os municípios dos cluster 0 e 1 têm nível de educação muito diferentes. 

Faça um teste de hipótese para verificar se os dois conjuntos de municípios pertencem à mesma população. 

Use significância de 0.05. 

O teste é de diferença de médias, ou seja, "bilateral". 

Use a função teste_hipotese_media_duas_amostras_desvio_amostra.

Dado o resultado do teste, podemos afirmar que os dois grupos pertencem à mesma população?
"""
significancia = 0.05
tipo = 'bilateral'
media_amostra_1 = 0.462509
tamanho_amostra_1 = 1913
desvio_amostra_1 = 0.053227
media_amostra_2 = 0.662219
tamanho_amostra_2 = 1771
desvio_amostra_2 = 0.042420
      
teste_hipotese_media_duas_amostras_desvio_amostra(significancia, tipo,
                                                        media_amostra_1, tamanho_amostra_1, desvio_amostra_1,
                                                        media_amostra_2, tamanho_amostra_2, desvio_amostra_2,
                                                     diferenca = 0)
"""
# EXERCÍCIO

Para verificar os dados relatados na pesquisa anterior, queremos fazer um levantamento de campo para medir o nível de educação nos clusters 0 e 1.

Verifique o tamanho da amostra necessária para o levantamento em cada cluster.

Lembre que a variância corresponde ao desvio padrão elevado ao quadrado

Use 0.95 para confança e 0.01 para margem de erro. Utilize a função Tamanho_Amostra_Valor_Pontual
"""
Tamanho_Populacao_0 = 1913
Grau_Confianca = 0.95
Variancia = 0.002833
Margem_Erro = 0.01
Tamanho_Amostra_Valor_Pontual(Tamanho_Populacao_0, Grau_Confianca, Variancia, Margem_Erro)
Tamanho_Populacao_1 = 1771
Grau_Confianca = 0.95
Variancia = 0.001799
Margem_Erro = 0.01

Tamanho_Amostra_Valor_Pontual(Tamanho_Populacao_1, Grau_Confianca, Variancia, Margem_Erro)
"""
# EXERCÍCIO

Dadas as duas populações dos clusters 0 e 1 calculadas abaixo, retire uma amostra de cada uma com o número de elementos calculado acima

Use a função recupera_amostra
"""
Populacao_0 = municipios.loc[municipios.Cluster==0,['Educacao']]
Populacao_1 = municipios.loc[municipios.Cluster==1,['Educacao']]
tamanho_amostra_0 = 104
recupera_amostra(Populacao_0, tamanho_amostra_0)
tamanho_amostra_1 = 67
recupera_amostra(Populacao_1, tamanho_amostra_1)
"""
# EXERCÍCIO

Usando as amostras do exercício anterior, calcule o intervalo de confiânça para a média do nível de educação nas duas populações

Use confiança de 0.95

Use a função calcula_intervalo_media

Verifique se o intervalo encontrado engloba os valores indicados na pesquisa acima
"""
amostra = recupera_amostra(Populacao_0, tamanho_amostra_0)
confianca = 0.95
tamanho_populacao= 1913
calcula_intervalo_media(amostra, confianca, tamanho_populacao)
amostra = recupera_amostra(Populacao_1, tamanho_amostra_1)
confianca = 0.95
tamanho_populacao= 1771
calcula_intervalo_media(amostra, confianca, tamanho_populacao)