25 de maio de 2019

Motivação

Preparar. Apontar. Fogo!

  • Manipular e visualizar dados (MVD) são atividades obrigatórias em Data Science (DS).
  • A MVD determina o sucesso de uma série de etapas.
    • Entendimento dos dados.
    • Limpeza e conciliação de dados.
    • Engenharia de características.
    • Especificação de modelos.
    • Comunicação de resultados, etc.
  • Fazer MVD de forma eficiente requer:
    • Conhecer o processo e suas etapas.
    • Dominar a tecnologia para execução.
  • Linguagens de programação oferecem uma série de vantagens: reproduzível, extensível, escalonável, integrável, portável, etc.

O tempo gasto

O quanto (não) é divertido

Python para Ciência de Dados

Principais bibliotecas

Numpy

  • Suporte de operações numéricas em Python.
  • Estruturas para arrays (vetores/matrizes) multidimensionais.
  • Operações matemáticas e estatísticas.
  • Execução vetorizada das funções.
  • Altamente otimizado para performance computacional.
  • Código crítico em Cython, C e Fortran.
  • Várias bibliotecas são baseada na Numpy.
  • https://www.numpy.org/.
  • Precursora da Numpy foi a Numeric em 1995.
  • Numpy começou em 2006.
  • Escrito em Python e C.

Scipy

  • Programação científica em Python.
  • Reúne algorítmos/procedimentos para:
    • ÁLgebra linear.
    • Equações diferenciais.
    • Integração numérica.
    • Otimização.
    • Estatística.
  • É baseada na Numpy.
  • https://www.scipy.org/.

Pandas

  • Estrutura e métodos para manipulação de dados em formato tabular.
  • Implementa a infraestrutura de data.frames do R.
  • Fornece operações para:
    • Leitura e escrita.
    • Ordenação e arranjo de disposição.
    • Seleção, fatiamento e filtragem.
    • Transformação e agregração.
    • Junções.
  • http://pandas.pydata.org/.
  • Primeira versão em Janeiro de 2008 por Wes McKinney.
  • Escrita em Python, Cython e C.

Matplotlib

  • Biblioteca de gráficos 2D.
  • Gera figuras em vários formatos.
  • Similiar ao Matlab em aparência e construção.
  • Gráficos de pontos, linhas, barras, setores, superfície, etc.
  • Relativamente de baixo nível.
  • Requer esforço para confecção de gráficos sofisticados.
  • https://matplotlib.org/

Seaborn

  • Baseada na Matplotlib.
  • Interface de alto nível para gráficos estatísticos.
  • Similar ao ggplot2 em aparência.
  • Compatibilidade com Pandas.

Numpy

Detalhes

  • Numpy é o pacote fundamental para computação científica em Python.
  • Fornece multimensional arrays com operações vetorizadas.
  • Amplo conjunto de funções para operar sobre arrays.
  • Integração com C/C++ e Fortran.
  • Recursos para álgebra linear, geração de números aleatórios e mais.
  • https://www.numpy.org/.

Importação da biblioteca

# Importa a Numpy.
import numpy as np
# Consulta a versão e caminho para arquivos.
print(np.__version__)
1.16.2
print(np.__path__)
# np.__spec__
# np.__doc__
['/home/walmes/anaconda3/lib/python3.7/site-packages/numpy']
# Conteúdo carregado.
dir(np)

Desempenho de operações vetorizadas

from timeit import default_timer as tmr
l = list(range(100000000))
v = np.array(l)
i = tmr(); sum(l);  e = tmr(); a = e - i; a
i = tmr(); v.sum(); e = tmr(); b = e - i; b
a/b
u = 999
i = tmr(); y = [x * u for x in l]; e = tmr(); a = e - i; a
i = tmr(); f = v * u; e = tmr(); b = e - i; b
a/b
u = 100000
i = tmr(); f = [x for x in l if x > u]; e = tmr(); a = e - i; a
i = tmr(); f = v[v > u]; e = tmr(); b = e - i; b
a/b

    # Soma. ---------------------------------
>>> i = tmr(); sum(l);  e = tmr(); a = e - i; a
0.5613289720001831
>>> i = tmr(); v.sum(); e = tmr(); b = e - i; b
0.06923460899997735
>>> a/b
8.107635474627532

    # Multiplicação. ------------------------
>>> u = 999
>>> i = tmr(); y = [x * u for x in l]; e = tmr(); a = e - i; a
4.931227970000009
>>> i = tmr(); f = v * u; e = tmr(); b = e - i; b
0.26028217999987646
>>> a/b
18.94569950967196

    # Teste lógico. -------------------------
>>> u = 100000
>>> i = tmr(); f = [x for x in l if x > u]; e = tmr(); a = e - i; a
3.547209543000008
>>> i = tmr(); f = v[v > u]; e = tmr(); b = e - i; b
0.6467253729999811
>>> a/b
5.484877648367929

Arrays multidimensionais

  • Arrays são estruturas que armazenam dados de mesmo tipo de valor.
  • Existem várias funções para criar arrays multimensionais.
  • São muito úteis os vetores:
    • de zeros e uns.
    • com sequências regulares.
    • repetições de vetores.
    • de números aleatórios.

Criação de arrays

Arrays a partir de listas

# Um array unidimensional.
A = np.array([4, 7, 5, 1, 9, 7, 4, 0]); A
# Modifica as dimensões para redispor o conteúdo.
A.shape = (2, 4); A
# Um array tridimensional.
A.shape = (2, 2, 2); A

Zeros, uns e outros

# Vetor de 0.
np.zeros(shape = 4)
# Vetor de 1.
np.ones(shape = 4)
# Vetor vazio (nan).
np.empty(shape = 4)
# Vetor de algo qualquer.
np.full(shape = 4, fill_value = 99)

Arrays regulares

Sequências regulares

# Sequências regulares.
np.arange(start = 0, stop = 12, step = 2)
np.linspace(start = 0, stop = 12, num = 7)
np.logspace(start = -3, stop = 3, num = 7, base = 2)
np.geomspace(start = 2**(-3), stop = 2**3, num = 7)

Repetições

# Repetições.
np.repeat(a = np.array([1, 2, 3]), repeats = 2)
np.repeat(a = np.array([[1, 2], [3, 4]]), repeats = 2, axis = 0)
np.repeat(a = np.array([[1, 2], [3, 4]]), repeats = 2, axis = 1)
np.repeat(a = np.array([[1, 2], [3, 4]]), repeats = [2, 1], axis = 0)
# Isso pode ser obtido com soma direta ou Kronecker.
np.tile(A = [1, 2, 3], reps = 2)
np.tile(A = [1, 2, 3], reps = [3, 2])
np.tile(A = np.array([[1, 2], [3, 4]]), reps = [3, 2])

Números aleatórios

  • Números aleatórios são úteis em inúmeras situações em computação científica.
  • Em estatística são centrais para simulação de modelos estatísticos, inferência Bayesiana por MCMC, métodos baseados em reamostragem, etc.

Números aleatórios

Vetores com números aleatórios

# Números da distribuição uniforme contínua.
np.random.random(size = 3)
np.random.uniform(low = 0, high = 1, size = 4)
# Números da distribuição uniforme discreta.
np.random.randint(low = 0, high = 10, size = 20)
# Amostragem de um vetor de valores.
np.random.choice(a = [100, 200, 300], size = 7, replace = True)
# Números da distribuição normal.
np.random.normal(loc = 0, scale = 1, size = 4)

Matrizes com números aleatórios

# Arrays multimensionais da uniforme contínua e normal padrão.
np.random.rand(3, 4)
np.random.randn(3, 4)

Seleção em arrays

# Uma sequência regular.
A = np.linspace(start = 100, stop = 110, num = 11)
A
# Fatiamentos.
A[0:2]    # Do 0 até 1 inclusive.
A[:2]     # Idem.
A[2:]     # Do 2 até o final.
A[-2:]    # Do 2 de trás para frente até o final.
A[::2]    # Do começo ao final com passo 2.
# Conjuntos.
A[[1, 5, 9]]  # Na posição 1, 5 e 9.
A[[1, -5, 9]] # Nas posição 1, 5 vindo do final e 9.
A = np.random.rand(3, 4)
A
A[0, :]      # Linha 0 e todas as colunas.
A[0:2, :]    # Linha 0 e 1 e todas as colunas.
A[:, 0:3]    # Todas as linhas e colunas até 2.
A[0:2, 0:3]  # Junção das anteriores.

Filtros

Operadores de comparação

A = np.random.randint(low = 1, high = 5, size = 20)
A[A > 3]  # Maior.
A[A >= 3] # Maior ou igual.
A[A < 3]  # Menor.
A[A <= 3] # Menor ou igual
A[A == 3] # Igual
A[A != 3] # Diferente.

Operadores lógigos

# São as versões vetoriais de `and`, `or` e `not`.
A[(A > 8) & (A < 12)]  # Operador AND.
A[(A < 8) | (A > 12)]  # Operador OR.
A[~(A == 3)]           # Operador NOT.

Principais operações matriciais

A = np.random.rand(3, 4)
B = np.random.rand(4, 5)
C = np.random.rand(3, 3)
D = np.random.rand(4, 4)
# Operações elemento a elemento (elementwise).
A + A
C - C
D * D
D / D
# Produto matricial.
A.dot(B)
C.dot(A)
A.T              # Transposta.
np.diag(C)       # Diagonal.
np.linalg.inv(C) # Inversa.
np.linalg.det(C) # Determinante.

Operações com arrays de dimensão diferente

Operações aritméticas e estatísticas

Disponíveis via métodos

A = np.random.normal(loc = 10, scale = 1.5, size = 50)
[A.sum(), A.mean(), A.var(),
 A.std(), A.max(),  A.min()]

Funções aplicáveis em arrays

np.mean(a = A)
np.median(a = A)
np.quantile(a = A, q = [0.25, 0.75])

Aplicação por dimensão

B = np.random.rand(3, 4)
np.mean(a = B)
np.mean(a = B, axis = 0)
np.mean(a = B, axis = 1)

Considerações

  • Foi feito apenas uma revisão de Numpy.
  • Numpy é um módulo grande e é a base para outros vários módulos.
  • O Pandas é construído sobre o Numpy.

Pandas

Detalhes

  • Pandas é a biblioteca para manipulação de dados no Python.
  • Implementa a estrutura de Data Frame e métodos que atuam sobre ele.
  • Contém todas as operações usuais disponíveis em instruções SQL.
  • Além destas, dispõe de funções estatísticas e recursos de visualização.
  • Em termos de performance computacional, aproxima do data.table do R e empata com o tidyverse.

Operações típicas de manipulação

  1. Importar e/ou acessar dados.
  2. Ordenar os registros da tabela.
  3. Selecionar e fatiar nos índices/eixos.
  4. Filtrar registros por predicado.
  5. Renomear os índices/eixos.
  6. Modificar a disposição do conteúdo.
  7. Modificar/transformar o conteúdo.
  8. Aplicar funções/calcular medidas resumo.
  9. Agregar por categorias e aplicar.
  10. Concatenar tabelas.
  11. Juntar ou conciliar tabelas.

Importar o módulo

import pandas as pd
print(pd.__version__)
0.24.2
print(pd.__path__)
['/home/walmes/anaconda3/lib/python3.7/site-packages/pandas']
dir(pd)
Categorical, CategoricalDtype, CategoricalIndex, DataFrame,
DateOffset, DatetimeIndex, DatetimeTZDtype, ExcelFile,
ExcelWriter, Float64Index, Grouper, HDFStore, Index, IndexSlice,
..., api, array, arrays, bdate_range, compat, concat, core,
crosstab, cut, date_range, datetime, describe_option, errors,
eval, factorize, ..., to_datetime, to_msgpack, to_numeric,
to_pickle, to_timedelta, tseries, unique, util, value_counts,
wide_to_long

Leitura de arquivos de dados

  • As funções de leitura de arquivos do Pandas começam com read_.
  • O argumento obrigatório é o caminho para o arquivo.
  • Os demais são opcionais.
# Lista as funções que possuem `read_` no nome.
d = [d for d in dir(pd) if "read_" in d]
d
read_clipboard, read_csv, read_excel, read_feather, read_fwf,
read_gbq, read_hdf, read_html, read_json, read_msgpack,
read_parquet, read_pickle, read_sas, read_sql, read_sql_query,
read_sql_table, read_stata, read_table
# `pd.read_table()` é a função de leitura mais genérica.
args = pd.read_table.__code__.co_varnames
args
filepath_or_buffer, sep, delimiter, header, names, index_col,
usecols, squeeze, prefix, mangle_dupe_cols, dtype, engine, ...

Exemplo de importação

  • O caminho para o arquivo pode ser uma URL.
  • A função faz a conexão e download do arquivo.
# Endereço web do arquivo, mas poderia ser local.
url = "http://leg.ufpr.br/~walmes/data/euro_football_players.txt"
# Importa a tabela de dados.
tb = pd.read_table(filepath_or_buffer = url,
                   sep = "\t",
                   comment = "#")
tb.head(n = 6)
   country      team               name   pos   age  ...  spg    ps   aw  mom    rt
0  Austria  Salzburg         Sadio Mané  M(L)  21.0  ...  2.0  77.0  1.2  3.0  7.98
1  Austria  Salzburg        Kevin Kampl  M(R)  23.0  ...  2.0  83.9  0.3  1.0  7.93
2  Austria  Salzburg               Alan    FW  24.0  ...  4.2  60.8  3.8  2.0  7.91
3  Austria  Salzburg      André Ramalho  D(C)  22.0  ...  0.9  72.3  3.2  1.0  7.67
4  Austria  Salzburg  Stefan Hierländer     M  23.0  ...  0.5  86.3  3.0  NaN  7.59
5  Austria  Salzburg  Christoph Leitgeb  M(C)  28.0  ...  1.6  79.4  0.5  NaN  7.55

[6 rows x 17 columns]

Atributos do objeto

  • A função type() retorna a classe do objeto.
  • A função dir() exibe o conteúdo e um objeto que, no caso de DataFrame, mistura atributos, métodos e variáveis da tabela.
  • Para uma exibição mais detalhada pode se usar o módulo see.
  • A see() permite aplicação de filtros para exibir conteúdo selecionado.
  • Para acessar apenas o nome dos índices use .index e .columns
type(tb) # Classe.
dir(tb)  # Conteúdo.
# Principais atributos.
tb.shape
tb.index
tb.columns

Informações sobre a tabela

tb.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1528 entries, 0 to 1527
Data columns (total 17 columns):
country    1528 non-null object
team       1528 non-null object
name       1528 non-null object
pos        1528 non-null object
age        1524 non-null float64
cm         1444 non-null float64
kg         1417 non-null float64
apps       1326 non-null object
goal       412 non-null float64
ass        362 non-null float64
yel        729 non-null float64
red        95 non-null float64
spg        1025 non-null float64
ps         1318 non-null float64
aw         1120 non-null float64
mom        277 non-null float64
rt         1326 non-null float64
dtypes: float64(12), object(5)
memory usage: 203.0+ KB
None

Métodos e funções

import see
# Exibe todo conteúdo.
dot = see.see(tb)
dot
.*, [], in, +, +=, -, -=, *, *=, @, /, /=, //, //=, %, %=, **,
**=, &, &=, ^, ^=, |, |=, +obj, -obj, ~, <, <=, ==, !=, >, >=,
abs(), bool(), dir(), hash(), help(), iter(), len(), repr(),
round(), str(), unicode(), .T, .abs(), .add(), .add_prefix(),
.add_suffix(), .age, .agg(), .aggregate(), .align(), .all(),
.any(), .append(), .apply(), .applymap(), .apps, .as_matrix(),
.asfreq(), .asof(), .ass, .assign(), .astype(), .at(), .at_time(),
.aw, .axes, .between_time(), ...
# Exibe apenas o que termina com () -> funções.
dot.filter('/()$/')
# Exibe apenas o que NÃO termina com `()` -> atributos e operadores.
dot.filter('/[^(][^)]$/')

Criação a partir de objetos Python

Uma tabela com dados fictícios.

Uma tabela com dados fictícios.

Criação a partir de objetos Python

Por dicionário orientado à variáveis

# Cria um DataFrame a partir de um dicionário. O dicionário é de
# colunas: um dicionário de arrays.
df1 = pd.DataFrame({
    "matricula": [256, 487, 965, 125, 458, 874, 963],
    "nome": ["João", "Vanessa", "Tiago", "Luana", "Gisele",
             "Pedro", "André"],
    "curso": ["Mat", "Mat", "Est", "Est", "Est", "Mat", "Est"],
    "prova1": [80, 75, 95, 70, 45, 55, 30],
    "prova2": [90, 75, 80, 85, 50, 75, None],
    "prova3": [80, 75, 75, 50, None, 90, 30],
    "faltas": [4, 4, 0, 8, 16, 0, 20]},
                       index = list(range(1, 8)))
df2 = pd.DataFrame({
    "matricula": [505, 658, 713],
    "nome": ["Bia", "Carlos", "Cris"],
    "curso": ["Eng", "Eng", "Eng"],
    "prova1": [65, 75, 75],
    "prova2": [85, 80, 90],
    "faltas": [0, 0, 2]},
                       index = list(range(1, 4)))

Criação a partir de objetos Python

Por dicionário orientado à observações

# Cria um DataFrame a partir de um array de dicionários. Informação
# organizada por tupla ou registro.
df_extra = pd.DataFrame([
    {"mat.": 256, "nome": 'João'  , "idade": 18, "bolsista": "S"},
    {"mat.": 965, "nome": 'Tiago' , "idade": 18, "bolsista": "N"},
    {"mat.": 285, "nome": 'Tiago' , "idade": 22, "bolsista": "N"},
    {"mat.": 125, "nome": 'Luana' , "idade": 21, "bolsista": "S"},
    {"mat.": 874, "nome": 'Pedro' , "idade": 19, "bolsista": "N"},
    {"mat.": 321, "nome": 'Mia'   , "idade": 18, "bolsista": "N"},
    {"mat.": 669, "nome": 'Luana' , "idade": 19, "bolsista": "S"},
    {"mat.": 967, "nome": 'André' , "idade": 20, "bolsista": "N"},
])

Ordenação

Ordenação dos registros de uma tabela.

Ordenação dos registros de uma tabela.

Ordenação

Por uma variável

df1.sort_values(by = "matricula", ascending = True)
   matricula     nome curso  prova1  prova2  prova3  faltas
4        125    Luana   Est      70    85.0    50.0       8
1        256     João   Mat      80    90.0    80.0       4
5        458   Gisele   Est      45    50.0     NaN      16
2        487  Vanessa   Mat      75    75.0    75.0       4
6        874    Pedro   Mat      55    75.0    90.0       0
7        963    André   Est      30     NaN    30.0      20
3        965    Tiago   Est      95    80.0    75.0       0

Ordenação

Por mais de uma variável

df1.sort_values(by = ["curso", "prova1"],
                ascending = [True, True])
   matricula     nome curso  prova1  prova2  prova3  faltas
7        963    André   Est      30     NaN    30.0      20
5        458   Gisele   Est      45    50.0     NaN      16
4        125    Luana   Est      70    85.0    50.0       8
3        965    Tiago   Est      95    80.0    75.0       0
6        874    Pedro   Mat      55    75.0    90.0       0
2        487  Vanessa   Mat      75    75.0    75.0       4
1        256     João   Mat      80    90.0    80.0       4

Ordenação permanente

df1.sort_values(by = ["matricula"], inplace = True)

Seleção e fatiamento

Nomes

# Nome dos índices de coluna (axis = 1).
df1.columns
df2.columns
# Nome dos índices de linha (axis = 0).
df1.index
df2.index

Tamanho

# Número de linhas e colunas.
df1.shape
df2.shape
# Número de cédulas.
df1.size
df2.size

Seleção das variáveis (colunas)

Seleção com lista de índices

# Seleção das variáveis.
df1["nome"]
df1[["nome", "prova1", "prova2", "prova3"]]
# Usando `.loc`.
df1.loc[:, ["nome", "prova1", "prova2", "prova3"]]

Seleção pela posição

# Usando `.iloc`.
df1.iloc[:, :3]
df1.iloc[:, -3:]
df1.iloc[:, [1, 4]]
df1.iloc[:, range(0, 3)]

Questão!

  1. Como selecionar apenas as posições pares?
  2. Como selecionar um conjunto de intervalos: 1 a 3 e 5 a 8?
  3. Como selecionar todas exceto as variáveis de uma lista?
  4. Como selecionar de acordo com o tipo de valor: apenas a variáveis de tipo numérico?
  5. Como selecionar variáveis baseado em padrões de caracteres (regex)?

Solução

# Apenas as pares.
df1.iloc[:, ::2]
# Concatenação de listas vindas de intervalos.
df1.iloc[:, list(range(0, 2)) + list(range(3, 4))]
# Compreensão de lista.
v = ["prova1", "prova2", "prova3"]
sel = [i for i in df1.columns if not i in v]
df1.loc[:, sel]                  # Seleção.
df1.loc[:, ~df1.columns.isin(v)] # Negação (`~`).
# Apenas as de tipo numérico (`float64`).
type(df1.dtypes)
sel = df1.dtypes == "int64"
df1.loc[:, sel]
# Seleção de variáveis por padrão de caracteres (regex).
df1.filter(regex = "^prova") # Começa com "prova".
df1.filter(regex = "\d$")    # Termina com número.
df1.filter(regex = "^.{6}$") # Palavra tem 6 algarismos.

Seleção de observações (linhas)

Usando fatiadores

# Seleção por posição (só vale slicing, i.e. criados com `:`).
df1[:1]
df1[-2:]
df1[0:4:2]
df1[slice(0, 4, 2)]

Por lista de posições

# Seleção por posição mais geral.
df1.iloc[0:2, ]
df1.iloc[-2:, ]
df1.iloc[range(0, 2), ]
df1.iloc[list(range(0, 2)) + list(range(3, 4)), ]

Seleção de observações (linhas)

Pelo nome dos índices

# Convertendo uma coluna para índice dos registros.
df1.set_index("nome", inplace = True)
df1.index
# Seleciona pelo nome dos índices.
df1.loc[["Aline", "Vanessa"], :]
# Restaura a indexão.
df1.reset_index(inplace = True)
df1.index

Por máscara lógica

# Vetor aleatório de valores lógicos.
i = np.random.choice([True, False], size = df1.shape[0],
                     replace = True, p = [0.4, 0.6])
# Mantém posições que aparecem o True.
df1[i]

Seleção de registros

# Só permite o uso de escalares.
df1.at[3, "nome"] # Com índices rotulados.
df1.iat[3, 0]     # Com posição.

Filtros

Filtro dos registros de uma tabela.

Filtro dos registros de uma tabela.

Filtros

Um vetor lógico

# Usando regras lógicas (máscaras lógicas).
df1[df1.curso == "Est"]
df1[df1.faltas == 0]
df1[df1.faltas != 0]
df1[df1.nome.isin(["Aline", "Vanessa"])]

Combinando vetores lógicos

df1[(df1.prova1 >= 7) & (df1.prova2 >= 7)]
df1[(df1.prova1 + df1.prova2 + df1.prova3)/3 >= 7]
# Lista dos aprovados e reprovados em cálculo.
mask = ((df1.prova1 + df1.prova2 + df1.prova3) >= 21) &\
       (df1.faltas < 15)
df1[mask]  # Não reprovados (aprovados e exame).
df1[~mask] # Os reprovados (`~` é o operador `not` vetorial).

Questão!

  1. Existe algum jeito de não ficar precedendo o nome das variáveis na construção da regra lógica?
  2. Tem como aplicar regras lógicas sobre variáveis criadas on the fly?

Solução

df1.query("prova1 >= 6")
df1.query("prova1 >= 6 & prova2 >= 6")
df1.query("(prova1 + prova2)/2 >= 6")
df1.query("(prova1 + prova2)/2 >= 6 & faltas < 2")
# Seleção de variáveis por padrão de nome.
df1.filter(regex = "^prova") # Começa com "prova".
df1.filter(regex = "\d$")    # Termina com número.
df1.filter(regex = "^.{6}$") # Palavra tem 6 algarismos.

Renomeação

Formas de renomear as colunas de uma tabela.

Formas de renomear as colunas de uma tabela.

Renomeação

Via dicionário de substituição

# Renomeia índices de coluna (variáveis).
df1.rename(columns = {"matricula": "mat.", "faltas": "fl"})
# Renomeia índices de linhas (index).
df1.rename(index = {0: "10", 1: "20"})

Função de transformação de strings

# Usando métodos para strings. Deixa caixa alta, capitaliza e trunca.
df1.rename(mapper = str.upper, axis = "columns")
df1.rename(mapper = str.capitalize, axis = "columns")
df1.rename(mapper = lambda x: x[:3] + x[-1:], axis = "columns")
df1.rename(mapper = lambda x: x.replace("prova", "p"), axis = "columns")

Renomeação

Operações de expressão regular

# Usando expressão regular (regex).
import re
# Separa o número do texto com um underline.
df1.rename(columns = lambda x: re.sub(pattern = "^(.*)(\d)$",
                                      repl = "\\1_\\2",
                                      string = x))

Transformação

As operações podem modificar a tabela com a:

  1. Criação de novas variáveis.
  2. Remoção de variáveis.
  3. Transformação de variáveis.

As operações de criação/transformação podem ser:

  1. Matemáticas: aritméticas, potência, logarítmicas, trigonométricas, etc.
  2. Compartimentação (binning): agrupar em classes.
  3. Conversão de tipo de valor: i.e. de int \(\rightarrow\) str.
  4. Substituição: i.e. preencher um valor ausente.

Transformação

As transformações podem ser:

  1. Uma \(\rightarrow\) uma: \[ y = \log(x). \]
  2. Várias \(\rightarrow\) uma: \[ z = x/y^2. \]
  3. Várias \(\rightarrow\) várias: \[ y_1, \cdots, y_k = f_1(x_1,\cdots,x_m), \cdots, f_k(x_1, \cdots, x_m). \]

Transformações

Criação e deleção de variáveis em uma tabela.

Criação e deleção de variáveis em uma tabela.

Transformações

Transformações matemáticas

# Adiciona a coluna com a média.
df1["media"] = (df1.prova1 + df1.prova2 + df1.prova3)/3
# Outra forma de fazer.
df1["media"] = df1.loc[:, ["prova1", "prova2", "prova3"]].\
                   transform(sum, axis = 1)/3
df1["media"] = df1.media.round(decimals = 2)
df1
# Outra forma de fazer usando função anônima.
df1.apply(func = lambda r: (r.prova1 + r.prova2 + r.prova3)/3, axis = 1)

Transformações

Transformações matemáticas homogêneas

# Transformações com funções de uma variável.
df1.faltas.transform(np.sqrt)
df1.faltas.transform([np.sqrt, np.log]) # Funções Numpy.
df1.faltas.transform(["sqrt", "log"])   # Métodos Pandas.
df1.faltas.transform(lambda x: np.power(x, 2))
# Funções aplicadas em várias variáveis
df1.loc[:, ["prova1", "prova2"]].transform([np.sqrt, np.log])
df1.loc[:, ["prova1", "prova2"]].apply([np.sqrt, np.log])

Transformações

Compartimentação

# Intervalos para corte e rótulos.
inter = [0, 4, 7, np.inf]
condi = ["reprovado", "exame", "aprovado"]
# Cria a variável que é a condição.
df1["condicao"] = pd.cut(x = df1["media"],
                         bins = inter,
                         labels = condi,
                         right = False,
                         include_lowest = True)
df1

Transformações

Conversão ou coerção

df1.matricula.transform(str)
df1.matricula.transform(float)
df1.matricula.astype(str)
df1.matricula.astype(float)

Transformações

Substituição

# Máscara lógica.
df1.prova2.isnull()
df1.prova2.notnull()
# Troca NaN por 0.
df1.loc[:, ["prova1", "prova2", "prova3"]].fillna(0)
# Troca o NaN por 0 permanente.
df1.fillna({"prova1": 0, "prova2": 0, "prova3": 0},
           inplace = True)
df1

Transformações

Remoção

df1["media"] = (df1.prova1 + df1.prova2 + df1.prova3)/3
df1
del df1["media"]
del df1["condicao"]
df1

Variáveis de tipo especial

O Pandas tem recursos para trabalhar com variáveis de tipo especial. Neste tutorial elas não serão abordadas. Consulte a documentação disponível.

  1. Variáveis categóricas: https://pandas.pydata.org/pandas-docs/stable/user_guide/categorical.html.
  2. Variáveis cronológicas: https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html.
  3. Variáveis textuais (strings): https://pandas.pydata.org/pandas-docs/stable/user_guide/text.html.
  4. Valores ausentes: https://pandas.pydata.org/pandas-docs/stable/user_guide/missing_data.html.
  5. Estruturas esparsas: https://pandas.pydata.org/pandas-docs/stable/user_guide/sparse.html.

Rearranjo

  • São operações de reshaping da tabela.
  • Modificam a disposição dos registros.
    • Empilhar ou amontoar um conjunto de variáveis.
    • Desempilhar ou esparramar os níveis de uma variável.

Rearranjo · Empilhar

Modificação da disposição com empilhamento.

Modificação da disposição com empilhamento.

Rearranjo · Esparramar

Modificação da disposição com desempilhamento.

Modificação da disposição com desempilhamento.

Rearranjo

Usando o índice de linhas

# Use um index apropriado.
df1.set_index("nome", inplace = True)
df1
# As funções stack() e unstack().
df1_s = df1.loc[:, ["prova1", "prova2", "prova3"]].stack()
df1_s
df1_s.index
df1_u = df1_s.unstack()
df1_u
df1_u.index
  • Os NaN são removidos.
  • df1_s é de classe Series com index multinível.
  • df1_u é de classe DataFrame.

Rearranjo

Não usando o índice de linhas

# As funções melt() e pivot().
df1.reset_index(inplace = True)
df1_m = df1.melt(id_vars = ["nome", "curso"],
                 value_vars = ["prova1", "prova2", "prova3"],
                 var_name = "exame",
                 value_name = "nota")
df1_m
df1_p = df1_m.pivot(index = "nome",
                    columns = "exame",
                    values = "nota").reset_index()
df1_p.columns.name = None
df1_p
  • df1_m e df1_p são de classe DataFrame.
  • NaN são mantidos.
  • A .pivot() só permite uma variável em index =.

Medidas resumo

  • Operações para determinar estatísticas descritivas.
    • Soma, média, mediana, quartis, quantis, etc.
    • Variância, desvio-padrão, amplitude, desvio absoluto da mediana, coeficiente de variação, etc.
    • Número de níveis distintos, frequências absolutas/relativas, etc.
  • Elas podem ser marginais ou considerar a estratificação conforme uma ou mais variáveis categóricas.
  • Podem ser aplicadas em todas as variáveis de um mesmo tipo (homegêneo).

Medidas resumo

Cálculo de medidas resumo.

Cálculo de medidas resumo.

Medidas resumo

Uma variável ou uma estatística

# Retornam um valor.
[df1.prova1.sum(), df1.prova1.mean(),
 df1.prova1.min(), df1.prova1.max(),
 df1.prova1.median(), df1.prova1.count(), # Valores não nulos.
 len(df1.prova1)]    # Comprimento do vetor.
# Retornam um vetor de valores.
df1.prova1.describe()     # Várias medidas resumo.
df1.prova1.value_counts() # Tabela de frequência.
df1.prova1.quantile([0.25, 0.75]) # Percentis.
# Aplicando em várias variáveis.
df1.loc[:, ['prova1', 'prova2']].mean()
df1.loc[:, ['prova1', 'prova2']].std()

Medidas resumo

Estatísticas definidas pelo usuário

# agg() é um alias para aggregate(). Tanto faz qual usar mas prefira o
# curto.
df1["prova1"].agg(lambda x: 100 * x.std()/x.mean())
# Duas versões do coeficiente de variação.
def cv1(x):
    return(100 * np.std(x, ddof = 1)/np.mean(x))
def cv2(x):
    return(100 * x.std()/x.mean())
# Funcionam igual.
df1.loc[:, ["prova1", "prova2", "prova3", "faltas"]].agg(cv1)
df1.loc[:, ["prova1", "prova2", "prova3", "faltas"]].agg(cv2)

Medidas resumo

Várias estatísticas para várias variáveis

# As mesmas estatísticas para todas as variáveis.
# ATTENTION: só funciona se for com `cv2`.
df1.loc[:, ["prova1", "prova2", "prova3", "faltas"]].\
    agg([np.min, np.max, np.mean, cv2])
# Estatísticas diferentes para cada variável.
df1.agg({"prova1": ["sum", "mean"],
         "prova2": [np.sum, np.mean],
         "faltas": ["min", "max"]},
        axis = 0)

Agregação

  • Consiste em aplicar estatísticas em variáveis fazendo a extratificação por outras variáveis.
  • São tarefas conhecidas como split-apply-combine.
  • Ou também chamadas de GROUP BY.

Agregação

Agregação de uma tabela.

Agregação de uma tabela.

Agregação

Agregação de uma tabela.

Agregação de uma tabela.

Agregação

Agregação de uma tabela.

Agregação de uma tabela.

Agregação

# Agregações para conforme o curso.
df1.groupby(["curso"]).agg({"prova1": ["mean", "std"],
                            "prova2": ["mean", "std"],
                            "prova3": ["mean", "std"],
                            "faltas": ["mean", "std"]})
v = ["prova1", "prova2", "prova3", "faltas"]
df1.groupby(["curso"])[v].agg(["mean", "std"])
# Empilhar nas provas.
df1_m = pd.melt(frame = df1,
                id_vars = ["curso"],
                value_vars = ["prova1", "prova2", "prova3"],
                var_name = "exame",
                value_name = "nota")
df1_m.groupby(["curso", "exame"])["nota"].\
    agg(["mean", "std"]).reset_index()

Agregação

  • Uma vez que uma tabela é agrupada, várias informações e métodos estão disponíveis.
df1_by = df1.groupby("curso")
df1_by
# Propriedades da tabela agrupada.
df1_by.groups
df1_by.groups.keys()
df1_by.get_group("Est")
df1_by.get_group("Est").agg(["count", "mean", "std"])
# Estatísticas por grupo.
df1_by.prova1.count()
df1_by.prova1.mean()

Concatenação

  • A concatenação permite adicionar novas observações a uma tabela ou novas variáveis.
  • O parâmetro axis é usado para indicar a direção: 0 - linhas, 1 - colunas.
  • Seja por linha ou colunas, entradas com NaN são criadas para os índices que não foram especificados.
  • Veja também a função .append().

Concatenação

Concatenação de duas tabelas.

Concatenação de duas tabelas.

Concatenação

# Concatena com a tabela dos alunos de Eng.
pd.concat([df1, df2],
          axis = 0,
          ignore_index = True,
          sort = False)
# Ilustrando como fazer para colunas.
pd.concat([df1.iloc[:5, :4],
           df1.iloc[2:, 4:]],
          axis = 1,
          ignore_index = False)

Junções

  • Junções permitem parear dados de tabelas separadas quando elas possuem uma chave (ou chave primária).
  • As operações de junção podem ser inicialmente de 4 tipos:
    • Junção por interseção (inner join).
    • Junção por união (full join).
    • Junção à esquerda (left join).
    • Junção à direita (right join).
    • Existe também os exclusive joins.

Junções

Tipos de junções de tabelas ilustrado com diagramas de Veen.

Tipos de junções de tabelas ilustrado com diagramas de Veen.

Junções

Junções de tabelas do tipo inclusivas.

Junções de tabelas do tipo inclusivas.

Junções

df1 = df1.loc[:, ["matricula", "curso", "nome",
                  "prova1", "prova2", "prova3", "faltas"]]
df_extra.rename(columns = {"mat.": "matricula"},
                inplace = True)
# How pode ser: inner, outer, left ou right.
pd.merge(left = df1, right = df_extra,
         how = "inner", on = ["nome", "matricula"])
pd.merge(left = df1, right = df_extra,
         how = "outer", on = ["nome", "matricula"])
pd.merge(left = df1, right = df_extra,
         how = "left", on = ["nome", "matricula"])
pd.merge(left = df1, right = df_extra,
         how = "right", on = ["nome", "matricula"])

Considerações

  • Foram vistas as principais operações com dados tabulares.
  • Para mais detalhes consulte a documentação e livros dedicados.
  • Exercícios serão aplicados para fixação do conteúdo.
  • Links para guias de referência e tutoriais serão dados no final.
  • Conexão com banco de dados será visto em detalhe em outra disciplina.

Gráficos

Matplotlib e Seaborn

  • Matplotlib tem os recursos básicos para gráficos no Python.
  • Seaborn é feita sobre a Matplotlib e constrói gráficos estatísticos.
  • Objetos Pandas possuem alguns métodos gráficos.
import matplotlib.pyplot as plt
import seaborn as sns

Histograma

tb["cm"].plot.hist(bins = 20)
plt.show()

Densidade

plt.close()
tb["cm"].plot.density(color = "red")
plt.show()
plt.close()
tb2 = tb[tb.country.isin(["Italy", "England"])]
tb2.groupby(["country"])["cm"].plot.density()
plt.legend(); plt.show()

Histograma e densidade

plt.close()
sns.distplot(tb.loc[tb.cm.notnull(), ["cm"]],
             hist = True, kde = True, rug = True)
plt.show()

Gráfico de barras

plt.close()
sns.barplot(tb2.country, tb2.yel)
plt.show()

Diagrama de dispersão

plt.close()
sns.scatterplot(tb2.cm, tb2.kg, hue = tb2.country)
plt.show()

Boxplot

plt.close()
sns.boxplot(tb2.country, tb2.cm)
plt.show()

Violin plot

plt.close()
sns.violinplot(tb2.country, tb2.kg)
plt.show()

Matriz de diagramas de dispersão

plt.close()
sns.pairplot(data = tb2, vars = ["cm", "kg", "age"], hue = "country")
plt.show()

Considerações

  • Matplotlib e Seaborn confeccionam os principais gráficos.
  • Eles estão prototipados como métodos ou funções.
  • Os recursos não foram explorados em profundidade aqui.
  • A customização dos gráficos requer maior conhecimento.
  • A combinação de gráficos, adornos e anotações em gráficos também.

Referências

Referências

Referências

Referências

Principais livros

  1. McKinney, Wes. Python for data analysis: data wrangling with Pandas, NumPy, and IPython. Sebastopol, CA: O'Reilly Media, Inc, 2017. Print.
  2. Chen, Daniel Y. Pandas for everyone: Python data analysis. Boston: Addison-Wesley, 2018. Print.
  3. Anthony, Femi. Mastering pandas: master the features and capabilities of pandas, a data analysis toolkit for Python. Birmingham, UK: Packt Publishing, 2015. Print.
  4. Dale, Kyran. Data visualization with Python and JavaScript: scrape, clean, explore & transform your data. Sebastopol, CA: O'Reilly Media, 2016. Print.

Exercícios

Dados dos jogadores da liga européia (1)

  1. Fazer a importação do arquivo disponível em http://leg.ufpr.br/~walmes/data/euro_football_players.txt.
  2. Determinar o número de registros por pais (country).
  3. Determinar o número de registros por time (team).
  4. A média de idade (age) geral dos jogadores.
  5. A mediana de peso (kg) geral dos jogadores.
  6. Os percentis de 10 e 90% da distribuição da altura (cm).
  7. Obter a correlação entre peso e altura.
  8. A tupla do jogador com o maior número de gols (goal).
  9. A tupla do jogador mais alto (cm).
  10. Os 10 jogadores de maior rating (rt).
  11. Os 10 jogadores mais altos (cm).

Dados dos jogadores da liga européia (2)

  1. Filtrar para jogadores que entraram em campo (apps > 0).
  2. Imputar 0 no lugar dos valores ausentes em cartões amarelos (yel), vermelhos (red), gols (goal), e assistências a gol (ass).
  3. Criar um variável com faixa de idade (age) em grupos de 5 anos.
  4. Determinar o número de jogadores em cada faixa de idade.

Dados dos jogadores da liga européia (3)

  1. Criar a variável índice de massa corporal \[ \text{bmi} = \dfrac{\text{peso (kg)}}{\text{altura (m)}^2}. \]
  2. Criar as faixa de índice de massa corporal.

    BMI < 18.5           : Below normal weight
    BMI >= 18.5 and < 25 : Normal weight
    BMI >= 25 and < 30   : Overweight
    BMI >= 30 and < 35   : Class I Obesity
    BMI >= 35 and < 40   : Class II Obesity
    BMI >= 40            : Class III Obesity

Dados dos jogadores da liga européia (4)

  1. Obter a média de idade por time.
  2. Obter a mediana de peso por pais.
  3. Obter a média de altura por posição em que joga.
  4. Obter o rating médio por time.
  5. Obter o jogador com mais gols em cada time.
  6. Obter a proporção de jogadores italianos por time.
  7. Obter a média, desvio-padrão e amplitude para número de gols por time.
  8. Obter por time o total de gols, média de altura e amplitude de bmi.

Próximo encontro e atividades

Próximo encontro e atividades

Será visto

  1. Manipulação e visualização de dados no ambiente R.
  2. Pacotes mais usados.
  3. A abordagem tidyverse.

Atividades no moodle

  1. Fazer o quiz no moodle.
  2. Fazer lista de exercícios extra.