Estatística para Cientistas de Dados
Carregando, aguarde alguns segundos.

2 - Análise de Dados Exploratória

2.1 - Preparação dos dados

2.1.1 - Importação dos pacotes Python

from pathlib import Path
import pandas as pd
import numpy as np
from scipy.stats import trim_mean
from statsmodels import robust
import wquantiles
import seaborn as sns
import matplotlib.pylab as plt

2.1.2 - Diretório de dados

O diretório DATA contém os arquivos .csv utilizados nos exemplos.

DATA = './'

2.1.3 - Caminhos dos conjuntos de dados

Se você não mantiver seus dados no mesmo diretório que o código, adapte os nomes dos caminhos.

AIRLINE_STATS_CSV = DATA + 'airline_stats.csv'
KC_TAX_CSV = DATA + 'kc_tax.csv.gz'
LC_LOANS_CSV = DATA + 'lc_loans.csv'
AIRPORT_DELAYS_CSV = DATA + 'dfw_airline.csv'
SP500_DATA_CSV = DATA + 'sp500_data.csv.gz'
SP500_SECTORS_CSV = DATA + 'sp500_sectors.csv'
STATE_CSV = DATA + 'state.csv'

2.2 - Estimativas de localização

Exemplo: Estimativas de Localização da População e Taxas de Homicídios.

Tabela 1-2:

state = pd.read_csv(STATE_CSV)
print(state.head(8))

Calcule a média, a média aparada e a mediana para a população.

Os métodos mean() e median() do dataframe Pandas calculam a média a mediana para a população, e o método trim_mean em scipy.stats calcula a média aparada.

print(state['Population'].mean())
print(trim_mean(state['Population'], 0.1))
print(state['Population'].median())

2.3 - Média ponderada com Numpy.

O pacote especializado wquantiles (https://pypi.org/project/wquantiles/) permite calcular a média ponderada.

print(state['Murder.Rate'].mean())
print(np.average(state['Murder.Rate'], weights=state['Population']))
print(wquantiles.median(state['Murder.Rate'], weights=state['Population']))

2.4 - Estimativas de variabilidade

Tabela 1-2: Desvio padrão

# Desvio padrão
#
print(state['Population'].std())
#
# O intervalo interquartil é calculado como a diferença do quantil 75% e 25%.
#
print(state['Population'].quantile(0.75) - state['Population'].quantile(0.25))
#
# O desvio absoluto da mediana pode ser calculado com um método em <i>statsmodels</i>
#
print(robust.scale.mad(state['Population']))
print(abs(state['Population'] - state['Population'].median()).median() / 0.6744897501960817)

2.5 - Percentis e gráficos de caixa (Boxplots)

Pandas tem o método quantile para dataframes.

Tabela 1.4:

print(state['Murder.Rate'].quantile([0.05, 0.25, 0.5, 0.75, 0.95]))
percentages = [0.05, 0.25, 0.5, 0.75, 0.95]
df = pd.DataFrame(state['Murder.Rate'].quantile(percentages))
df.index = [f'{p * 100}%' for p in percentages]
print(df.transpose())

O Pandas fornece vários gráficos exploratórios básicos; um deles são boxplots.

ax = (state['Population']/1_000_000).plot.box(figsize=(3, 4))
ax.set_ylabel('Population (millions)')
plt.tight_layout()
plt.show()

2.6 - Tabela de Frequência e Histogramas

O método cut dos dataframes Pandas divide o conjunto de dados em compartimentos.

Existem vários argumentos para o método.

O código a seguir cria compartimentos de tamanhos iguais.

O método value_counts retorna uma tabela de frequência.

binnedPopulation = pd.cut(state['Population'], 10)
print(binnedPopulation.value_counts())

Tabela 1.5:

binnedPopulation.name = 'binnedPopulation'
df = pd.concat([state, binnedPopulation], axis=1)
df = df.sort_values(by='Population')
groups = []
for group, subset in df.groupby(by='binnedPopulation'):
    groups.append({
        'BinRange': group,
        'Count': len(subset),
        'States': ','.join(subset.Abbreviation)
    })
print(pd.DataFrame(groups))

Pandas também suporta histogramas para análise exploratória de dados.

ax = (state['Population'] / 1_000_000).plot.hist(figsize=(4, 4))
ax.set_xlabel('Population (millions)')
plt.tight_layout()
plt.show()

2.7 - Estimativas de densidade

A densidade é uma alternativa aos histogramas que pode fornecer mais informações sobre a distribuição dos pontos de dados.

Use o argumento bw_method para controlar a suavidade da curva de densidade.

ax = state['Murder.Rate'].plot.hist(
    density=True, xlim=[0, 12], 
    bins=range(1,12), figsize=(4, 4))
state['Murder.Rate'].plot.density(ax=ax)
ax.set_xlabel('Murder Rate (per 100,000)')
plt.tight_layout()
plt.show()

2.8 - Explorando dados binários e categóricos

Tabela 1-6:

dfw = pd.read_csv(AIRPORT_DELAYS_CSV)
print(100 * dfw / dfw.values.sum())

Pandas também suporta gráficos de barras para exibir uma única variável categórica.

ax = dfw.transpose().plot.bar(figsize=(4, 4), legend=False)
ax.set_xlabel('Cause of delay')
ax.set_ylabel('Count')
plt.tight_layout()
plt.show()

2.9 - Correlação

Primeiro leia os conjuntos de dados necessários.

Tabela 1-7:

sp500_sym = pd.read_csv(SP500_SECTORS_CSV)
sp500_px = pd.read_csv(SP500_DATA_CSV, index_col=0)
#
# Determinar símbolos de telecomunicações
telecomSymbols = sp500_sym[sp500_sym['sector'] == 'telecommunications_services']['symbol']

# Filtrar dados para datas de julho de 2012 a junho de 2015
telecom = sp500_px.loc[sp500_px.index >= '2012-07-01', telecomSymbols]
telecom.corr()
print(telecom)

Em seguida, focamos nos fundos negociados nas principais bolsas (sector == 'etf').

etfs = sp500_px.loc[
    sp500_px.index > '2012-07-01', 
    sp500_sym[sp500_sym['sector'] == 'etf']['symbol']]
print(etfs.head())

Devido ao grande número de colunas nesta tabela, observar a matriz de correlação é complicado e é mais conveniente plotar a correlação como um mapa de calor.

O pacote Seaborn fornece uma implementação conveniente para mapas de calor.

fig, ax = plt.subplots(figsize=(5, 4))
ax = sns.heatmap(
    etfs.corr(), vmin=-1, vmax=1, ax=ax, 
    cmap=sns.diverging_palette(20, 220, as_cmap=True))
plt.tight_layout()
plt.show()

O mapa de calor acima funciona quando você tem cores.

Para as imagens em escala de cinza, conforme usadas no livro, precisamos visualizar a direção também.

O código a seguir mostra a força da correlação usando elipses.

from matplotlib.collections import EllipseCollection
from matplotlib.colors import Normalize
#
def plot_corr_ellipses(data, figsize=None, **kwargs):
    ''' https://stackoverflow.com/a/34558488 '''
    M = np.array(data)
    if not M.ndim == 2:
        raise ValueError('data must be a 2D array')
    fig, ax = plt.subplots(1, 1, figsize=figsize, subplot_kw={'aspect':'equal'})
    ax.set_xlim(-0.5, M.shape[1] - 0.5)
    ax.set_ylim(-0.5, M.shape[0] - 0.5)
    ax.invert_yaxis()
    #
    # xy locations of each ellipse center
    xy = np.indices(M.shape)[::-1].reshape(2, -1).T
    #
    # set the relative sizes of the major/minor axes according to the strength of
    # the positive/negative correlation
    w = np.ones_like(M).ravel() + 0.01
    h = 1 - np.abs(M).ravel() - 0.01
    a = 45 * np.sign(M).ravel()
    #
    ec = EllipseCollection(widths=w, heights=h, angles=a, units='x', offsets=xy,
                           norm=Normalize(vmin=-1, vmax=1),
                           transOffset=ax.transData, array=M.ravel(), **kwargs)
    ax.add_collection(ec)
    #
    # if data is a DataFrame, use the row/column names as tick labels
    if isinstance(data, pd.DataFrame):
        ax.set_xticks(np.arange(M.shape[1]))
        ax.set_xticklabels(data.columns, rotation=90)
        ax.set_yticks(np.arange(M.shape[0]))
        ax.set_yticklabels(data.index)
    #
    return ec, ax
#
m, ax = plot_corr_ellipses(etfs.corr(), figsize=(5, 4), cmap='bwr_r')
#cb = fig.colorbar(m, ax=ax)
#cb.set_label('Correlation coefficient')
#
plt.tight_layout()
plt.show()

2.10 - Gráficos de dispersão( Scatterplots)

O Pandas suporta gráficos de dispersão simples.

Especificar o marcador como $\u25EF$ usa um círculo aberto para cada ponto.

ax = telecom.plot.scatter(x='T', y='VZ', figsize=(4, 4), marker='$\u25EF$')
ax.set_xlabel('ATT (T)')
ax.set_ylabel('Verizon (VZ)')
ax.axhline(0, color='grey', lw=1)
ax.axvline(0, color='grey', lw=1)
#
plt.tight_layout()
plt.show()

T x VZ:

ax = telecom.plot.scatter(
    x='T',
    y='VZ',
    figsize=(4, 4),
    marker='$\u25EF$',
    alpha=0.5)
ax.set_xlabel('ATT (T)')
ax.set_ylabel('Verizon (VZ)')
ax.axhline(0, color='grey', lw=1)
print(ax.axvline(0, color='grey', lw=1))

2.11 - Explorando duas ou mais variáveis

Carregue o conjunto de dados kc_tax e filtre com base em vários critérios.

kc_tax = pd.read_csv(KC_TAX_CSV)
kc_tax0 = kc_tax.loc[
    (kc_tax.TaxAssessedValue < 750000) & 
    (kc_tax.SqFtTotLiving > 100) &
    (kc_tax.SqFtTotLiving < 3500), :]
print(kc_tax0.shape)

2.11.1 - Binning hexagonal e contornos

2.11.1.1 - Plotando dados numéricos versus numéricos

Se o número de pontos de dados for grande, os gráficos de dispersão não serão mais significativos.

Aqui os métodos que visualizam as densidades são mais úteis.

O método hexbin para dataframes pandas é uma abordagem poderosa.

ax = kc_tax0.plot.hexbin(x='SqFtTotLiving', y='TaxAssessedValue',
                         gridsize=30, sharex=False, figsize=(5, 4))
ax.set_xlabel('Finished Square Feet')
ax.set_ylabel('Tax Assessed Value')
#
plt.tight_layout()
plt.show()

O método kdeplot do Seaborn é uma extensão bidimensional do gráfico de densidade.

O cálculo da densidade 2D para o conjunto de dados completo leva vários minutos.

É suficiente criar a visualização com uma amostra menor do conjunto de dados.

Com 10.000 pontos de dados, a criação do gráfico leva apenas alguns segundos.

Embora alguns detalhes possam ser perdidos, a forma geral é preservada.

fig, ax = plt.subplots(figsize=(4, 4))
sns.kdeplot(data=kc_tax0.sample(10000), x='SqFtTotLiving', y='TaxAssessedValue', ax=ax)
ax.set_xlabel('Finished Square Feet')
ax.set_ylabel('Tax Assessed Value')
#
plt.tight_layout()
plt.show()

2.11.2 - Duas variáveis categóricas

Carregue o conjunto de dados lc_loans.

lc_loans = pd.read_csv(LC_LOANS_CSV)

Tabela 1-8(1):

crosstab = lc_loans.pivot_table(index='grade', columns='status', 
                                aggfunc=lambda x: len(x), margins=True)
print(crosstab)

Tabela 1-8(2):

df = crosstab.copy().loc['A':'G',:]
df.loc[:,'Charged Off':'Late'] = df.loc[:,'Charged Off':'Late'].div(df['All'], axis=0)
df['All'] = df['All'] / sum(df['All'])
perc_crosstab = df
print(perc_crosstab)

2.11.3 - Dados categóricos e numéricos

As caixas de uma coluna podem ser agrupadas por uma coluna diferente com método boxplot do Pandas.

airline_stats = pd.read_csv(AIRLINE_STATS_CSV)
airline_stats.head()
ax = airline_stats.boxplot(
    by='airline',
    column='pct_carrier_delay',
    figsize=(5, 5))
ax.set_xlabel('')
ax.set_ylabel('Daily % of Delayed Flights')
plt.suptitle('')
#
plt.tight_layout()
plt.show()

Pandas também suporta uma variação de gráficos de caixas chamada violinplot.

fig, ax = plt.subplots(figsize=(5, 5))
sns.violinplot(
    data=airline_stats,
    x='airline',
    y='pct_carrier_delay',
    ax=ax,
    inner='quartile',
    color='white')
ax.set_xlabel('')
ax.set_ylabel('Daily % of Delayed Flights')
#
plt.tight_layout()
plt.show()

2.11.4 - Visualizando várias variáveis

zip_codes = [98188, 98105, 98108, 98126]
kc_tax_zip = kc_tax0.loc[kc_tax0.ZipCode.isin(zip_codes),:]
kc_tax_zip
#
def hexbin(x, y, color, **kwargs):
    cmap = sns.light_palette(color, as_cmap=True)
    plt.hexbin(x, y, gridsize=25, cmap=cmap, **kwargs)

g = sns.FacetGrid(kc_tax_zip, col='ZipCode', col_wrap=2)
g.map(
    hexbin,
    'SqFtTotLiving',
    'TaxAssessedValue', 
    extent=[0, 3500, 0, 700000])
g.set_axis_labels('Finished Square Feet', 'Tax Assessed Value')
g.set_titles('Zip code {col_name:.0f}')
#
plt.tight_layout()
plt.show()
Arduino
Coautor
Betobyte
Autor
Autores
||| Áreas ||| Estatística ||| Python ||| Projetos ||| Dicas & Truques ||| Quantum ||| Estatística para Cientistas de Dados || Python para Iniciantes || Python Básico || Matplotlib || Numpy || Seaborn || Pandas || Django || Estatística para Cientistas de Dados || Python com ML Básico || Python com ML Básico || Aulas | 1 (Introdução) | 2 (Análise de dados exploratória) | 3 (Dados e exemplos de distribuições) | 4 (Experimentos estatísticos e testes de significância) | 5 (Regressão e previsão) | 6 (Regressão e previsão) | 7 (Aprendizado de máquina estatístico) | 8 (Aprendizado não supervisionado) |