Introdução à Ciência de Dados

Análise Exploratória de Dados com python

Prof. Jodavid Ferreira

UFPE

O que é Análise Exploratória de Dados?



  • A Análise Exploratória de Dados não é um processo com um conjunto de regras rígidas;

  • Mais do que qualquer coisa é um estado de espírito;

  • Você deve sentir-se livre para investigar cada ideia que lhe venha a mente, considerando fazer sentido a problemática em estudo;
    • Algumas darão resultados;
    • Outras particularmente não levarão a lugar nenhum;

Análise Exploratória de Dados






  • Não há regras sobre quais perguntas você deve fazer para guiar sua pesquisa.


  • Mas existe dois entendimentos essências para uma boa Análise Exploratória:



Compreensão da problemática e

Compreensão do contexto



Análise Exploratória de Dados



Compreensão da Problemática

  • É preciso ter uma visão clara do problema de negócio a ser resolvido.

Análise Exploratória de Dados



Compreensão do Contexto



  • Quem é seu público?


  • O que você precisa que ele saiba?


Compreender os dados e descobrir o que pode ser digno de nota ou interessante a destacar para outras pessoas.

Análise Exploratória de Dados



em termos quantitavivos e de nosso dia a dia


podemos pensar em iniciar nossa AED por esses dois caminhos:


  1. Que tipo de variação ocorre dentro de minhas variáveis?
  2. Que tipo de covariação ocorre entre minhas variáveis?

O que geralmente é utilizada na AED



  • Medidas de tendência central;
  • Medidas de dispersão;
  • Análise Gráfica;
  • Análise de correlação;
  • Alguns possíveis testes de associação;


Através destes pontos, é possível extrair informações interessantes sobre os dados em estudo.

Dados Tabulados (Tidy Data)




Reflexão:



Conjuntos de dados arrumados são todos iguais, mas cada conjunto de dados bagunçado, é bagunçado de sua própria maneira.

- Hadley Wickham



Dados Tabulados (Tidy Data)



Há três regras inter-relacionadas que tornam um conjunto de dados arrumados (tidy):

  1. Cada variável deve ter sua própria coluna.

  2. Cada observação deve ter sua própria linha.

  3. Cada valor deve ter sua própria célula.






Vamos ao python?

Python - Base de dados



DataSet utilizado: used_cars_data.csv


link: https://www.kaggle.com/datasets/sukhmanibedi/cars4u


Problemática: Esta base de dados é referente a carros usados na ‘Índia’. Através dos dados existentes, quais informações relevantes poderíamos obter sobre esses carros usados do país em questão?


Python - Base de dados




=> id: Número único por linha para representar cada observação;
=> nome: Nome do carro usado
=> localizacao: Cidade de localização do carro na Índia
=> ano: Ano de fabricação do carro
=> quilometros_percorridos: Quilômetros percorridos pelo carro em km
=> tipo_de_combustivel: Tipo de combustível utilizado pelo carro
=> transmissao: Transmissão do carro
=> tipo_proprietario: Quantos donos o carro já possuiu
=> quilometragem_por_litro: Quantos km o carro faz com 1 lt
=> motor: Quantidade de cilindradas do motor
=> potencia: Potência do motor em bhp
=> assentos: Quantidade de assentos do carro
=> novo_preco: Variável que possui um preço do carro em LAKh
=> preco: Variável que possui preco do carro também em LAKh


Obs.: 1 Lakh, chamado de um laque, é uma unidade do sistema de numeração indiana. Um laque indiano equivale a 100.000 (cem mil) rupias indianas. E 1 rupia equivale a 0,06 centavos. Assim, 1 Lakh = 6.415,62 reais.

Python - Importação das Bibliotecas



O primeiro passo nesse caso é importar bibliotecas necessárias para análise:

# -----------------
# Bibliotecas necessarias para Analise dos Dados
import pandas as pd
import numpy as np
import scipy as sp
# -----------------
# Bibliotecas Gráficas
import matplotlib.pyplot as plt
import seaborn as sns
# -----------------
import plotly.express as px
# -----------------



Python - Lendo base de dados


# -----------------
# Lendo a Base de dados
dados = pd.read_csv("dataset/used_cars_data.csv")
# -----------------

Visualizando um cabeçalho dos dados

# -----------------
# Visualizando um cabeçalho dos dados
dados.head(1)
# -----------------
id nome localizacao ano quilometros_percorridos tipo_de_combustivel transmissao tipo_proprietario quilometragem_por_litro motor potencia assentos novo_preco preco
0 0 Maruti Wagon R LXI CNG Mumbai 2010 72000 CNG Manual First 26.6 km/kg 998 CC 58.16 bhp 5.0 NaN 1.75

. . .

# -----------------
# Visualizando os últimos dados
dados.tail()
# -----------------

Python - Tipos das variáveis


# -----------------
# Visualizando Informações das colunas
dados.info()
# -----------------
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 7253 entries, 0 to 7252
Data columns (total 14 columns):
 #   Column                   Non-Null Count  Dtype  
---  ------                   --------------  -----  
 0   id                       7253 non-null   int64  
 1   nome                     7253 non-null   object 
 2   localizacao              7253 non-null   object 
 3   ano                      7253 non-null   int64  
 4   quilometros_percorridos  7253 non-null   int64  
 5   tipo_de_combustivel      7253 non-null   object 
 6   transmissao              7253 non-null   object 
 7   tipo_proprietario        7253 non-null   object 
 8   quilometragem_por_litro  7251 non-null   object 
 9   motor                    7207 non-null   object 
 10  potencia                 7207 non-null   object 
 11  assentos                 7200 non-null   float64
 12  novo_preco               1006 non-null   object 
 13  preco                    6019 non-null   float64
dtypes: float64(2), int64(3), object(9)
memory usage: 793.4+ KB


Python - Tamanho da base de dados



# -----------------
# Visualizando Quantidades de Linhas e colunas
len(dados)
# -----------------
7253
# -----------------
len(dados.columns)
# -----------------
14
# -----------------
dados.columns
# -----------------
Index(['id', 'nome', 'localizacao', 'ano', 'quilometros_percorridos',
       'tipo_de_combustivel', 'transmissao', 'tipo_proprietario',
       'quilometragem_por_litro', 'motor', 'potencia', 'assentos',
       'novo_preco', 'preco'],
      dtype='object')
# -----------------
dados.shape
# -----------------
(7253, 14)

Python - Dados únicos por variável



# -----------------
# Checando dados únicos por variável
dados.nunique()
# -----------------
id                         7253
nome                       2041
localizacao                  11
ano                          23
quilometros_percorridos    3660
tipo_de_combustivel           5
transmissao                   2
tipo_proprietario             4
quilometragem_por_litro     450
motor                       150
potencia                    386
assentos                      9
novo_preco                  625
preco                      1373
dtype: int64


Python - Valores nulos nas colunas



# -----------------
# Checando variáveis que possuem dados ausentes
dados.isnull().sum()
# -----------------
id                            0
nome                          0
localizacao                   0
ano                           0
quilometros_percorridos       0
tipo_de_combustivel           0
transmissao                   0
tipo_proprietario             0
quilometragem_por_litro       2
motor                        46
potencia                     46
assentos                     53
novo_preco                 6247
preco                      1234
dtype: int64


Python - Removendo colunas


# -----------------
# Remover coluna 'S.No.' dos dados
dados = dados.drop(['id'], axis = 1)
dados.info()
# -----------------

# -----------------
# Remover coluna 'S.No.' dos dados
dados = dados.drop(columns = ['id'])
dados.info()
# -----------------
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 7253 entries, 0 to 7252
Data columns (total 13 columns):
 #   Column                   Non-Null Count  Dtype  
---  ------                   --------------  -----  
 0   nome                     7253 non-null   object 
 1   localizacao              7253 non-null   object 
 2   ano                      7253 non-null   int64  
 3   quilometros_percorridos  7253 non-null   int64  
 4   tipo_de_combustivel      7253 non-null   object 
 5   transmissao              7253 non-null   object 
 6   tipo_proprietario        7253 non-null   object 
 7   quilometragem_por_litro  7251 non-null   object 
 8   motor                    7207 non-null   object 
 9   potencia                 7207 non-null   object 
 10  assentos                 7200 non-null   float64
 11  novo_preco               1006 non-null   object 
 12  preco                    6019 non-null   float64
dtypes: float64(2), int64(2), object(9)
memory usage: 736.8+ KB


Python - Métricas Estatísticas


# -----------------
dados.describe()
# -----------------
ano quilometros_percorridos assentos preco
count 7253.000000 7.253000e+03 7200.000000 6019.000000
mean 2013.365366 5.869906e+04 5.279722 9.479468
std 3.254421 8.442772e+04 0.811660 11.187917
min 1996.000000 1.710000e+02 0.000000 0.440000
25% 2011.000000 3.400000e+04 5.000000 3.500000
50% 2014.000000 5.341600e+04 5.000000 5.640000
75% 2016.000000 7.300000e+04 5.000000 9.950000
max 2019.000000 6.500000e+06 10.000000 160.000000


OBS.: Ele considera os tipos de dados numéricos e calcula as métricas como int, float.

Python - Medidas de tend. central


Calculando Média de diferentes formas:

Usando Pandas

# -----------------
dados["preco"].mean()
# -----------------
9.47946835022429

Usando numpy

# -----------------
np.mean(dados["preco"])
# -----------------
9.47946835022429

Python - Medidas de tend. central


Calculando Mínimo de diferentes formas:

Usando Pandas

# -----------------
dados["preco"].min()
# -----------------
0.44

Usando numpy

# -----------------
np.min(dados["preco"])
# -----------------
0.44

Python - Medidas de tend. central


Calculando quantis:

Usando Pandas

# -----------------
dados["preco"].quantile()
# -----------------
5.64
# -----------------
dados["preco"].median()
# -----------------
5.64

Usando numpy

# -----------------
np.quantile(dados["preco"], 0.5)
# -----------------
nan

Porque com numpy retornou nan?

Python - Medidas de tend. central


Calculando quantis:

Corrigindo numpy

# -----------------
np.nanquantile(dados["preco"], 0.5)
# -----------------
5.64
considerando mais de um quantil
# -----------------
np.nanquantile(dados["preco"], [0,0.25,0.5,0.75,1]) #Numpy
# -----------------
array([  0.44,   3.5 ,   5.64,   9.95, 160.  ])

Usando pandas

# -----------------
dados["preco"].quantile([0,0.25,0.5,0.75,1]) #Pandas
# -----------------
0.00      0.44
0.25      3.50
0.50      5.64
0.75      9.95
1.00    160.00
Name: preco, dtype: float64

Python - Medidas de tend. central



Calculando quantis em mais de uma coluna:

# -----------------
# Quantis mais colunas
# -----------------
dados[["quilometros_percorridos","preco"]].quantile([0,0.25,0.5,0.75,1]) #Pandas
# -----------------
quilometros_percorridos preco
0.00 171.0 0.44
0.25 34000.0 3.50
0.50 53416.0 5.64
0.75 73000.0 9.95
1.00 6500000.0 160.00

OBS.: Para não ficar sempre mostrando mais de uma solução, vamos focar no Pandas e seguir com ela até o fim desse minicurso.

Python - Medidas de dispersão



Calculando variância das variáveis

# -----------------
dados.var(numeric_only=True) # O padrão  é False
# -----------------
ano                        1.059125e+01
quilometros_percorridos    7.128040e+09
assentos                   6.587914e-01
preco                      1.251695e+02
dtype: float64


OBS.: Ele considera os tipos de dados numéricos e calcula as métricas como int, float.

Python - Medidas de dispersão



Calculando covariância entre as variáveis

# -----------------
dados.cov(numeric_only=True) # O padrão é False
# -----------------
ano quilometros_percorridos assentos preco
ano 10.591255 -5.161682e+04 0.021573 11.169366
quilometros_percorridos -51616.822165 7.128040e+09 6201.221577 -11735.383472
assentos 0.021573 6.201222e+03 0.658791 0.473281
preco 11.169366 -1.173538e+04 0.473281 125.169489


OBS.: Ele considera os tipos de dados numéricos e calcula as métricas como int, float.

Python - Medidas de dispersão



Calculando correlação entre as variáveis

# -----------------
# Quantis mais colunas
# -----------------
dados.corr(numeric_only=True) # O padrão é False e correlação de Pearson
# -----------------
ano quilometros_percorridos assentos preco
ano 1.000000 -0.187859 0.008216 0.305327
quilometros_percorridos -0.187859 1.000000 0.090221 -0.011493
assentos 0.008216 0.090221 1.000000 0.052225
preco 0.305327 -0.011493 0.052225 1.000000


OBS.: Ele considera os tipos de dados numéricos e calcula as métricas como int, float.

Python - Variáveis Qualitativas


Calculando frequências quando as variáveis são categóricas

# ---------------------
dados["nome"].value_counts()
# ---------------------
nome
Mahindra XUV500 W8 2WD                  55
Maruti Swift VDI                        49
Maruti Swift Dzire VDI                  42
Honda City 1.5 S MT                     39
Maruti Swift VDI BSIV                   37
                                        ..
Chevrolet Beat LT Option                 1
Skoda Rapid 1.6 MPI AT Elegance Plus     1
Ford EcoSport 1.5 TDCi Ambiente          1
Hyundai i10 Magna 1.1 iTech SE           1
Hyundai Elite i20 Magna Plus             1
Name: count, Length: 2041, dtype: int64

Python - Variáveis Qualitativas


Calculando frequências quando as variáveis são categóricas

# ---------------------
dados.groupby(["nome"])["nome"] \
      .count() \
      .reset_index(name='count') \
      .sort_values(['count'], ascending=False)
# ---------------------
nome count
1017 Mahindra XUV500 W8 2WD 55
1265 Maruti Swift VDI 49
1241 Maruti Swift Dzire VDI 42
457 Honda City 1.5 S MT 39
1266 Maruti Swift VDI BSIV 37
... ... ...
939 Mahindra NuvoSport N8 1
937 Mahindra Logan Petrol 1.4 GLE 1
936 Mahindra Logan Diesel 1.5 DLS 1
935 Mahindra KUV 100 mFALCON G80 K8 1
1020 Mahindra Xylo D2 BS III 1

2041 rows × 2 columns

Python - Variáveis Qualitativas


Criando tabelas cruzadas entre as variáveis


# ---------------------
tabela_cruzada = pd.crosstab(dados['transmissao'], dados['tipo_proprietario']) 
# -------
tabela_cruzada
# ---------------------
tipo_proprietario First Fourth & Above Second Third
transmissao
Automatic 1682 1 332 34
Manual 4270 11 820 103

Python - Variáveis Qualitativas



# ---------------------
# Teste de Qui-Quadrado
from scipy.stats import chisquare
from scipy.stats import chi2_contingency

# Aplicando Teste Qui-Quadrado na Tabela. 
c, p, dof, expected = chi2_contingency(tabela_cruzada)
print(p)
# ---------------------
0.34358871263824625

Python - Variáveis Qualitativas


Criando tabelas cruzadas entre as variáveis


# ---------------------
# Teste de Qui-Quadrado com Transmissao e Localização
tabela_cruzada = pd.crosstab(dados['transmissao'], dados['localizacao']) 
# ---
c, p, dof, expected = chi2_contingency(tabela_cruzada)
print(p)
# ---------------------
1.3894902634085349e-55
# ---------------------
# Tabela Cruzada para Transmissao e Localização
tabela_cruzada
# --------
localizacao Ahmedabad Bangalore Chennai Coimbatore Delhi Hyderabad Jaipur Kochi Kolkata Mumbai Pune
transmissao
Automatic 72 179 136 303 204 232 62 245 88 359 169
Manual 203 261 455 469 456 644 437 527 566 590 596

Python - Criação de variáveis


# -----------------
# Criação da variável 'Car_Age'como uma novas variável
from datetime import date
# ---
print("O ano atual é: " + str(date.today().year) )
# --
dados['idade_carro'] = date.today().year - dados['ano']
# --
dados[["nome", "preco", "ano", "idade_carro"]]
# -----------------
O ano atual é: 2026
nome preco ano idade_carro
0 Maruti Wagon R LXI CNG 1.75 2010 16
1 Hyundai Creta 1.6 CRDi SX Option 12.50 2015 11
2 Honda Jazz V 4.50 2011 15
3 Maruti Ertiga VDI 6.00 2012 14
4 Audi A4 New 2.0 TDI Multitronic 17.74 2013 13
... ... ... ... ...
7248 Volkswagen Vento Diesel Trendline NaN 2011 15
7249 Volkswagen Polo GT TSI NaN 2015 11
7250 Nissan Micra Diesel XV NaN 2012 14
7251 Volkswagen Polo GT TSI NaN 2013 13
7252 Mercedes-Benz E-Class 2009-2013 E 220 CDI Avan... NaN 2014 12

7253 rows × 4 columns


Python - Criação de variáveis


# -----------------
# Criação da variável 'Marca' do carro
# str.split: quebra as strings
# str.get(0): pega todas as posições 0 de cada lista criada da linha
dados['marca'] = dados.nome.str.split().str.get(0)
# --
dados[['nome','marca']].head()
# -----------------
nome marca
0 Maruti Wagon R LXI CNG Maruti
1 Hyundai Creta 1.6 CRDi SX Option Hyundai
2 Honda Jazz V Honda
3 Maruti Ertiga VDI Maruti
4 Audi A4 New 2.0 TDI Multitronic Audi

OBS.: Para retornar data.frames com duas ou mais colunas selecionadas, é necessário utilizar dois pares de [], ou seja, df[[colname(s)]]. O primeiro par retorna uma série, enquanto os colchetes duplos retornam um dataframe.

Python - Verificando variáveis


Antes dos gráficos, vamos verificar quais variáveis são numéricas e quais são categóricas


# -----------------
cat_cols = dados.select_dtypes(include=['object']).columns
num_cols = dados.select_dtypes(include=np.number).columns.tolist()
print("Variáveis Categóricas: " + str(cat_cols))
print("Variáveis Numéricas:"+ str(num_cols))
# -----------------
Variáveis Categóricas: Index(['nome', 'localizacao', 'tipo_de_combustivel', 'transmissao',
       'tipo_proprietario', 'quilometragem_por_litro', 'motor', 'potencia',
       'novo_preco', 'marca'],
      dtype='object')
Variáveis Numéricas:['ano', 'quilometros_percorridos', 'assentos', 'preco', 'idade_carro']


Python - Gráficos


Histogramas e BoxPlot

Antes dos gráficos, vamos verificar quais variáveis são numéricas e quais são categóricas


# -----------------
for col in num_cols:
    print(col)
    print('Assimetria :', round(dados[col].skew(), 2))
    plt.figure(figsize = (15, 4))
    plt.subplot(1, 2, 1)
    dados[col].hist(grid=False)
    plt.ylabel('Frequência')
    plt.subplot(1, 2, 2)
    sns.boxplot(x=dados[col])
    plt.show()
# -----------------

Python - Gráficos


Histogramas e BoxPlot

idade_carro
Assimetria : 0.84


Python - Gráficos


Gráficos de Barras

# -----------------
fig, axes = plt.subplots(3, 2, figsize = (18, 18))
fig.suptitle('Gráficos de Barras para todas as variáveis categóricas da base de dados')
sns.countplot(ax = axes[0, 0], x = 'tipo_de_combustivel', data = dados, color = 'blue', 
              order = dados['tipo_de_combustivel'].value_counts().index);
sns.countplot(ax = axes[0, 1], x = 'transmissao', data = dados, color = 'blue', 
              order = dados['transmissao'].value_counts().index);
sns.countplot(ax = axes[1, 0], x = 'tipo_proprietario', data = dados, color = 'blue', 
              order = dados['tipo_proprietario'].value_counts().index);
sns.countplot(ax = axes[1, 1], x = 'localizacao', data = dados, color = 'blue', 
              order = dados['localizacao'].value_counts().index);
sns.countplot(ax = axes[2, 0], x = 'marca', data = dados, color = 'blue', 
              order = dados['marca'].head(20).value_counts().index);
sns.countplot(ax = axes[2, 1], x = 'ano', data = dados, color = 'blue', 
              order = dados['ano'].head(20).value_counts().index);
axes[1][1].tick_params(labelrotation=45);
axes[2][0].tick_params(labelrotation=90);
axes[2][1].tick_params(labelrotation=90);
# -----------------


Python - Gráficos


Gráficos de Barras


Python - Gráficos


Gráficos Bivariados

# -----------------
sns.pairplot(data=dados[['quilometros_percorridos','preco','transmissao']], \
             height=4.5, \
             corner = True,\
             hue="transmissao")
# -----------------


Python - Gráficos


Gráficos Bivariados

Python - Gráficos


Gráficos de Barras entre variável categórica e uma numérica

# -----------------
fig, axarr = plt.subplots(1,1, figsize=(7, 8))
dados_agrupados = dados.groupby('transmissao')['preco'].mean().sort_values(ascending=False)
dados_agrupados.plot.bar(fontsize=12) \
  .set_title("Transmissao Vs Preço", fontsize=18)
# -----------------
Text(0.5, 1.0, 'Transmissao Vs Preço')

Python - Gráficos


Gráficos de Barras entre duas variáveis categóricas e uma numérica

# -------------------
# Criando uma Tabela para geração do gráfico distinta por categorias
dfp = dados.pivot_table(index='ano', columns='transmissao', values='preco', aggfunc='mean')
# -------------------
dfp.head(10)
# -----------
transmissao Automatic Manual
ano
1998 3.900000 0.610000
1999 NaN 0.835000
2000 NaN 1.175000
2001 NaN 1.543750
2002 NaN 1.294000
2003 11.305000 1.258000
2004 3.931667 1.463600
2005 4.467778 1.569167
2006 10.853636 2.124925
2007 6.825000 2.514286

Python - Gráficos


Gráficos de Barras entre duas variáveis categóricas e uma numérica

dfp.plot.barh(fontsize=10, rot = 0) \
  .set_title("Transmissao Vs Preço", fontsize=18)
Text(0.5, 1.0, 'Transmissao Vs Preço')

Python - Gráficos


HeatMap - Mapa de calor para correlação

sns.heatmap(dados[['quilometros_percorridos','assentos', 'idade_carro','preco']].corr(), \
            annot = True, vmin = -1, vmax = 1)

Python - Outras bibliotecas Gráficas


import plotly.express as px
# ------
dados_agrupados_p = dados_agrupados.reset_index(name = 'preco_medio')
# ------
fig = px.bar(dados_agrupados_p, x='transmissao', y = 'preco_medio')
fig.show()

Python - Outras bibliotecas Gráficas


from plotnine import ggplot, geom_bar, aes
# ------
dados_agrupados_p = dados_agrupados.reset_index(name = 'preco_medio')
# ------
ggplot(dados_agrupados_p, aes(x = 'transmissao', y = 'preco_medio')) \
+ geom_bar(stat = "identity")

Python - Relatório com o SweetViz



# ---------
# Importanto a biblioteca
import sweetviz as sv
# ---------
report = sv.analyze(dados)
report.show_html()
# ---------


Link: SWEETVIZ_REPORT.html

Conclusões



O que podemos concluir de forma breve até esse momento com o estudo:


  • Existem uma frequência maior de carros manuais;
  • Os carros de transmissão automáticos são mais caros que os carros manuais;
  • Existe uma correlação fraca/moderada negativa, entre o ano dos carros e os preços, ou seja, quanto maior a idade do carro, mais barato ele é.
  • Existe uma correlação fraca entre idade do carro e quilometragem percorrida, ou seja, quanto mais antigo for o carro, mais quilometragem provavelmente percorreu.




OBRIGADO!


Slide produzido com quarto