Visualização e Análise de Dados
Nesse artigo vamos falar sobre exploração de dados, pegaremos duas bases de dados (dataset) e veremos como gerar alguns gráficos com o objetivo de entender a estrutura dos dados e achar padrões.
As bibliotecas utilizadas para visualizar os dados serão:
Matplotlib: é uma biblioteca utilizada para plotar os mais variados tipos de gráficos.
Seaborn: utiliza o Matplotlib como base e fornece funções simples de usar para criação de gráficos que seriam complexos apenas com o Matplotlib.
Instalação:
pip install numpy pandas matplotlib seaborn
Mercado financeiro
Podemos facilmente plotar gráficos de ações de empresas na bolsa de valores.
Vamos utilizar a biblioteca pandas-datareader para pegar informações sobre ações e preencher nosso dataset. Essa biblioteca não pega dados da Bovespa.
Vamos instalá-la:
pip install pandas-datareader
Uma vez instalada, vamos ao nosso código Python.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
A linha logo abaixo faz os gráficos aparecerem no Jupyter notebook, não é necessária caso não esteja utilizando ele.
%matplotlib inline
Vamos pegar os valores das ações do Google da API do Google Finances de hoje até um ano atrás. A documentação do DataReader pode ser vista aqui.
import pandas_datareader.data as pdr
from datetime import datetime, timedelta
end_date = datetime.now()
start_date = end_date - timedelta(days=365)
df_goog = pdr.DataReader('GOOG', 'google', start_date, end_date)
df_goog.tail()
Uma vez que estamos com as informações em mãos, vamos aos gráficos. Exibiremos os valores máximos e mínimos de cada dia de todo o período.
df_goog[['High', 'Low']].plot(figsize=(15, 4), title='Google Stocks', grid=True)
Criaremos algumas novas colunas com base nos dados. Média móvel e retorno diário são métricas comumentes analisadas em ações.
# Média movel de 14 dias do Fechamento
df_goog['MovingMean14'] = df_goog.Close.rolling(14).mean()
# Média movel de 30 dias do Fechamento
df_goog['MovingMean30'] = df_goog.Close.rolling(30).mean()
# Retorno diário percentual
df_goog['DailyReturn'] = df_goog.Close.pct_change()
df_goog.tail()
Vamos plotar o valor de fechamento e suas médias móveis para compararmos.
columns = ['Close','MovingMean14', 'MovingMean30']
graph = df_goog[columns].plot(figsize=(15, 4), grid=True)
Caso queira salvar o gráfico em arquivo, basta guardar o retorno em uma variável como no exemplo anterior e chamar o método savefig do figure.
graph.figure.savefig('graph.png')
Vamos ver como ficou o retorno diário das ações no decorrer do ano.
df_goog.DailyReturn.plot(figsize=(15, 4), grid=True)
Ao plotar a distribuição desses dados, analisaremos mais facilmente se é uma ação com um bom retorno diário.
fig, ax1 = plt.subplots(1, 1, figsize=(15, 4))
sns.distplot(df_goog.DailyReturn.dropna(), bins=100, ax=ax1)
Quando já temos o figure, como é o caso acima, basta chamar o método savefig para salvar em disco.
fig.savefig('graph2.png')
Com uma coluna que mostra somente ano e mês podemos agrupar os dados e gerar um boxplot para ver a distribuição dos dados em cada mês.
fig, ax1 = plt.subplots(1, 1, figsize=(15, 4))
df_goog['Month'] = df_goog.index.to_period('M')
sns.boxplot('Month', 'Close', data=df_goog, ax=ax1)
Transformaremos a data que é o index de nosso DataFrame em uma coluna normal para facilitar a criação de alguns gráficos, também criaremos uma coluna com a data em formato numérico.
import matplotlib.dates as mdates
df_goog.reset_index(inplace=True)
df_goog['DateAsNumber'] = df_goog.Date.apply(mdates.date2num)
Com o Matplotlib conseguimos plotar um gráfico do tipo candlestick, que é muito comum na analise de ações. Usaremos os últimos 15 dias para o gráfico ficar com um tamanho legível.
from matplotlib.finance import candlestick_ohlc
def plot_candle_stick(df):
fig, ax1 = plt.subplots(1, 1, figsize=(15, 4))
candlestick_ohlc(ax1, df.values, width=.6, colorup='g', colordown='r')
ax1.xaxis_date()
ax1.xaxis.set_major_formatter(mdates.DateFormatter('%y-%m-%d'))
ax1.set_xlabel('Date')
ax1.set_ylabel('Stock Price')
columns = ['DateAsNumber', 'Open', 'High', 'Low', 'Close', 'Volume']
plot_candle_stick(df_goog[columns].tail(15))
Com uma curva de tendência para o valor de fechamento das ações, podemos ter uma projeção para os próximos valores. Uma regressão linear será usada para criar essa curva.
lm = sns.lmplot('DateAsNumber', 'Close', data=df_goog, aspect=2.5, order=3)
lm.ax.xaxis.set_major_formatter(mdates.DateFormatter('%y-%m-%d'))
lm.ax.set_xlabel('Date')
Os gráficos do Seaborn retornam um objeto onde tem o figure que usamos para salvar em disco:
lm.fig.savefig('graph3.png')
Múltiplas ações
Podemos plotar gráficos de múltiplas ações simultaneamente, o que é muito útil para fins comparativos. Vamos pegar um período de 5 anos.
end_date = datetime.now()
start_date = end_date - timedelta(days=5*365)
stock_names = ['GOOG', 'MSFT', 'AMZN', 'AAPL','TSLA', 'XOM', 'GE', 'EBAY']
df_stocks = pdr.DataReader(stock_names, 'google', start_date, end_date)
df_stocks.Close.tail()
df_stocks.Close.plot(figsize=(15, 4), grid=True, colormap='Set1')
No código acima usamos o Set1 no colormap, é a paleta de cor que usamos para colorir o gráfico. No caso do Seaborn a paleta pode ser passada pelas variáveis cmap ou palette. As diversas paletas existentes podem ser vistas aqui. Quando o parâmetro for apenas color, usamos cores puras (red, blue, black etc) ao invés de paletas.
Geraremos um gráfico que mostra a correlação entre as ações.
def plot_corr(corr):
# Cortaremos a metade de cima pois é o espelho da metade de baixo
mask = np.zeros_like(corr, dtype=np.bool)
mask[np.triu_indices_from(mask, 1)] = True
sns.heatmap(corr, mask=mask, cmap='RdBu', square=True, linewidths=.5)
# Calculando a correlação
corr = df_stocks.Close.corr()
plot_corr(corr)
Vemos no exemplo que empresas de tecnologia (GOOG, MSFT, AMZN, AAPL e TSLA) possuem alta correlação entre sim, enquanto empresas de outros ramos, como energia (GE) e petróleo (XOM) possuem correlação baixa com essas empresas de tecnologia. Totalmente azul (1) é correlação máxima, totalmente vermelho (-1) é correlação inversa e branco (0) é sem qualquer correlação. Vemos que as ações possuem correlação máxima com elas mesmas e estão azul.
Titanic
É um famoso problema do site de competições Kaggle. É uma base de dados que contém diversas informações sobre os passageiros do Titanic, baseado nessas informações devemos criar um modelo capaz de dizer se determinada pessoa morreu ou não no naufrágio. Mais informações e a base de dados para download podem ser encontradas aqui.
Temos diversas informações nesse dataset, como idade, sexo, cabine, cidade de embarque, número de acompanhantes, valor pago, classe e se a pessoa morreu ou não no acidente. Para uma parte dessas pessoas o site não fornece essa última informação, dessa forma o site consegue validar se o seu modelo é capaz de prever corretamente algo que ele não conhece. Nesse primeiro artigo vamos apenas visualizar e analisar os dados, em um próximo treinaremos um modelo com ele.
df = pd.read_csv('train.csv')
df.head()
Criaremos um gráfico mostrando a distribuição do sexo masculino e feminino, podemos usar a função plot do próprio Pandas ou o factorplot do Seaborn.
# Com Pandas
df.Sex.value_counts().plot(kind='pie', autopct='%.2f%%')
plt.axis('equal') # Para deixar o gráfico redondo
# Com Seaborn
sns.factorplot('Sex', data=df, kind='count')
Faremos o mesmo para a classe onde o passageiro se encontra.
df.Pclass.value_counts().plot(kind='pie', autopct='%.2f%%')
plt.axis('equal');
sns.factorplot('Pclass',data=df, kind='count')
Vejamos como ficou a distribuição entre sobreviventes e não sobreviventes.
# Para trocar os valores numéricos pelos nomes nos gráficos
survived_map = {0: 'Died', 1: 'Survived'}
df.Survived.map(survived_map).value_counts().plot(kind='pie', autopct='%.2f%%')
plt.axis('equal');
sns.factorplot('Survived',data=df, kind='count').set_xticklabels(survived_map.values())
Podemos usar o campo hue para dividir as barras por categorias.
# Distribuição do sexo dividido por classe.
sns.factorplot('Sex', data=df, hue='Pclass', kind='count')
# Distribuição da classe dividida por sexo.
sns.factorplot('Pclass', data=df, hue='Sex', kind='count')
Geraremos uma coluna dizendo se é adulto ou não para vermos a distribuição de adultos e crianças em cada sexo.
df['is_adult'] = df.Age.apply(lambda age: age >= 18)
sns.factorplot('Sex', data=df, hue='is_adult', kind='count')
Com um histograma, podemos ver a distribuição da idade dos passageiros. Vemos que a maioria está por volta dos 20, 30 anos.
df.Age.hist(bins=int(df.Age.max()), figsize=(15, 4))
Olhando a cidade de embarque dos passageiros, vemos que a maioria embarcou em Southampton.
city_map = {'C': 'Cherbourg', 'Q': 'Queenstown', 'S': 'Southampton'}
df.Embarked.map(city_map).value_counts().plot(kind='pie', autopct='%.2f%%')
plt.axis('equal');
Criaremos uma nova coluna agrupando as pessoas em homem, mulher, criança e idoso baseado no sexo e na idade.
def get_description(row):
if row.Age < 18:
return 'child'
elif row.Age >= 65:
return 'elderly'
return 'man' if row.Sex == 'male' else 'woman'
df['Description'] = df.apply(get_description, axis=1)
df.Description.value_counts().plot(kind='pie', autopct='%.2f%%')
plt.axis('equal')
A taxa de sobrevivência desses novos grupos ficam assim:
sns.factorplot('Survived', data=df, hue='Description', kind='count').set_xticklabels(survived_map.values())
O gráfico mostra que a taxa de mortalidade é muito maior entre os homens. Isso provavelmente é devido à política de “mulheres e crianças na frente”.
Com as novas colunas abaixo veremos quem viaja acompanhado e com quantos membros da familía está.
# Coluna membros da família = irmãos/parceiros + país/filhos.
df['FamilyMember'] = df.SibSp + df.Parch
# Coluna dizendo se a pessoa está sozinho ou não.
df['IsAlone'] = df.FamilyMember == 0
df.IsAlone.value_counts().plot(kind='pie', autopct='%.2f%%')
plt.axis('equal')
Vemos que a maior parte das pessoas viajavam sozinhas.
sns.factorplot('Survived', data=df, hue='IsAlone', kind='count').set_xticklabels(survived_map.values())
Também vemos que a taxa de mortalidade foi bem maior entre os que viajavam sozinhos.
sns.factorplot('Survived',data=df, hue='Pclass', kind='count', aspect=3, order=survived_map.keys()).set_xticklabels(survived_map.values())
Entre as classes, vemos que a primeira teve mais sobreviventes do que mortos, a segunda foi equilibrada e a terceira teve um número bem maior de morto. Isso provavelmente é devido à posição das cabines com relação ao ponto de impacto do icerberg.
Cruzaremos alguns dados com à cidade de embarque.
sns.factorplot('Embarked',data=df, hue='Pclass', kind='count', aspect=3, order=city_map.keys()).set_xticklabels(city_map.values())
sns.factorplot('Embarked',data=df, hue='IsAlone', kind='count', aspect=3, order=city_map.keys()).set_xticklabels(city_map.values())
sns.factorplot('Embarked',data=df, hue='Description', kind='count', aspect=3, order=city_map.keys()).set_xticklabels(city_map.values())
A cidade de Southampton é na sua maioria composta de pessoas da terceira classe, sozinhas e homens. Pelo perfil é bem provável que tenha alguma indústria nessa cidade e sejam pessoas voltando do trabalho.
Vamos ver quem viajou sozinho.
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 8))
df[df.IsAlone].Description.value_counts().plot(kind='pie', autopct='%.2f%%', ax=ax1, title='Alone people')
ax1.axis('equal')
df[~df.IsAlone].Description.value_counts().plot(kind='pie', autopct='%.2f%%', ax=ax2, title='People with Family')
ax2.axis('equal')
A maioria dos viajantes sozinhos são homens e quase não temos crianças sozinhas. Já nas pessoas com família, temos uma incidência maior de mulheres e muitas crianças. Temos poucos idosos no navio, tanto sozinhos, quanto acompanhados.
Com um gráfico de linha veremos a distribuição dos dados.
def plot_line_graph(x, hue):
fig = sns.FacetGrid(df, hue=hue, aspect=4)
fig.map(sns.kdeplot, x, shade=True)
fig.set(xlim=(0, df[x].max()))
fig.add_legend()
return fig
plot_line_graph('Fare', 'Pclass')
Distribuindo o valor das taxas de embarque de acordo com a classe, vemos que as maiores são as da primeira classe.
fig = plot_line_graph('Age', 'Sex')
fig.ax.axvline(18, color='r')
fig.ax.axvline(65, color='gray')
Plotando as idades de acordo com o sexo, vemos que a distribuição de idade entre homens e mulheres é bem próxima.
Por último, criaremos gráficos que mostram a taxa de sobrevivência baseado em algumas das características que temos.
sns.factorplot('Pclass', 'Survived', data=df)
sns.factorplot('Description', 'Survived', data=df)
sns.factorplot('Embarked', 'Survived', data=df, order=city_map.keys()).set_xticklabels(city_map.values())
sns.factorplot('IsAlone', 'Survived', data=df)
O grupo que mais sobreviveu possui as seguintes características: está na primeira classe, é mulher, embarcou em Cherbourg e não viajava sozinha.
Diversos outros gráficos podem ser encontrados na documentação de Matplotlib e Seaborn, além de existirem dezenas de outras bibliotecas para esse fim.
O objetivo desse artigo foi mostrar como plotar os dados para melhor entendê-los e através disso criar melhores modelos de Machine Learning.
O Jupyter notebook com todo o código pode ser visto aqui.
Voltar para página inicial: Carlos Baia