Operações com DataFrames

Como dissemos anterioremente, o DataFrame é a segunda estrutura basilar do pandas. Um DataFrame:

  • é uma tabela, ou seja, é bidimensional;

  • tem cada coluna formada como uma Series do pandas;

  • pode ter Series contendo tipos de dado diferentes.

import numpy as np
import pandas as pd

Criando um DataFrame

O método padrão para criarmos um DataFrame é através de uma função com mesmo nome.

df_exemplo = pd.DataFrame(dados_de_interesse, index = indice_de_interesse, 
                          columns = colunas_de_interesse)

Ao criar um DataFrame, podemos informar

  • index: rótulos para as linhas (atributos index das Series).

  • columns: rótulos para as colunas (atributos name das Series).

No template, dados_de_interesse pode ser

  • um dicionário de:

    • arrays unidimensionais do numpy;

    • listas;

    • dicionários;

    • Series do pandas.

  • um array bidimensional do numpy;

  • uma Series do Pandas;

  • outro DataFrame.

Criando um DataFrame a partir de dicionários de Series

Neste método de criação, as Series do dicionário não precisam possuir o mesmo número de elementos. O index do DataFrame será dado pela união dos index de todas as Series contidas no dicionário.

Exemplo:

serie_Idade = pd.Series({'Ana':20, 'João': 19, 'Maria': 21, 'Pedro': 22}, name="Idade")
serie_Peso = pd.Series({'Ana':55, 'João': 80, 'Maria': 62, 'Pedro': 67, 'Túlio': 73}, name="Peso")
serie_Altura = pd.Series({'Ana':162, 'João': 178, 'Maria': 162, 'Pedro': 165, 'Túlio': 171}, name="Altura")
dicionario_series_exemplo = {'Idade': serie_Idade, 'Peso': serie_Peso, 'Altura': serie_Altura}
df_dict_series = pd.DataFrame(dicionario_series_exemplo)
df_dict_series
Idade Peso Altura
Ana 20.0 55 162
João 19.0 80 178
Maria 21.0 62 162
Pedro 22.0 67 165
Túlio NaN 73 171

Compare este resultado com a criação de uma planilha pelos métodos usuais. Veja que há muita flexibilidade para criarmos ou modificarmos uma tabela.

Vejamos exemplos sobre como acessar intervalos de dados na tabela.

pd.DataFrame(dicionario_series_exemplo, index=['Ana','Maria'])
Idade Peso Altura
Ana 20 55 162
Maria 21 62 162
pd.DataFrame(dicionario_series_exemplo, index=['Ana','Maria'], columns=['Peso','Altura'])
Peso Altura
Ana 55 162
Maria 62 162

Neste exemplo, adicionamos a coluna IMC, ainda sem valores calculados.

pd.DataFrame(dicionario_series_exemplo, index=['Ana','Maria','Paula'], 
             columns=['Peso','Altura','IMC'])
Peso Altura IMC
Ana 55.0 162.0 NaN
Maria 62.0 162.0 NaN
Paula NaN NaN NaN
df_exemplo_IMC = pd.DataFrame(dicionario_series_exemplo, 
             columns=['Peso','Altura','IMC'])

Agora, mostramos como os valores do IMC podem ser calculados diretamente por computação vetorizada sobre as Series.

df_exemplo_IMC['IMC']=round(df_exemplo_IMC['Peso']/(df_exemplo_IMC['Altura']/100)**2,2)
df_exemplo_IMC
Peso Altura IMC
Ana 55 162 20.96
João 80 178 25.25
Maria 62 162 23.62
Pedro 67 165 24.61
Túlio 73 171 24.96

Criando um DataFrame a partir de dicionários de listas ou arrays do numpy:

Neste método de criação, os arrays ou as listas devem possuir o mesmo comprimento. Se o index não for informado, o index será dado de forma similar ao do objeto tipo Series.

Exemplo com dicionário de listas:

dicionario_lista_exemplo = {'Idade': [20,19,21,22,20],
                            'Peso': [55,80,62,67,73],
                            'Altura': [162,178,162,165,171]}
pd.DataFrame(dicionario_lista_exemplo)
Idade Peso Altura
0 20 55 162
1 19 80 178
2 21 62 162
3 22 67 165
4 20 73 171

Mais exemplos:

pd.DataFrame(dicionario_lista_exemplo, index=['Ana','João','Maria','Pedro','Túlio'])
Idade Peso Altura
Ana 20 55 162
João 19 80 178
Maria 21 62 162
Pedro 22 67 165
Túlio 20 73 171

Exemplos com dicionário de arrays do numpy:

dicionario_array_exemplo = {'Idade': np.array([20,19,21,22,20]),
                            'Peso': np.array([55,80,62,67,73]),
                            'Altura': np.array([162,178,162,165,171])}
pd.DataFrame(dicionario_array_exemplo)
Idade Peso Altura
0 20 55 162
1 19 80 178
2 21 62 162
3 22 67 165
4 20 73 171

Mais exemplos:

pd.DataFrame(dicionario_array_exemplo, index=['Ana','João','Maria','Pedro','Túlio'])
Idade Peso Altura
Ana 20 55 162
João 19 80 178
Maria 21 62 162
Pedro 22 67 165
Túlio 20 73 171

Criando um DataFrame a partir de uma Series do pandas

Neste caso, o DataFrame terá o mesmo index que a Series do pandas e apenas uma coluna.

series_exemplo = pd.Series({'Ana':20, 'João': 19, 'Maria': 21, 'Pedro': 22, 'Túlio': 20})
pd.DataFrame(series_exemplo)
0
Ana 20
João 19
Maria 21
Pedro 22
Túlio 20

Caso a Series possua um atributo name especificado, este será o nome da coluna do DataFrame.

series_exemplo_Idade = pd.Series({'Ana':20, 'João': 19, 'Maria': 21, 'Pedro': 22, 'Túlio': 20}, name="Idade")
pd.DataFrame(series_exemplo_Idade)
Idade
Ana 20
João 19
Maria 21
Pedro 22
Túlio 20

Criando um DataFrame a partir de lista de Series do pandas

Neste caso, a entrada dos dados da lista no DataFrame será feita por linha.

pd.DataFrame([serie_Peso, serie_Altura, serie_Idade])
Ana João Maria Pedro Túlio
Peso 55.0 80.0 62.0 67.0 73.0
Altura 162.0 178.0 162.0 165.0 171.0
Idade 20.0 19.0 21.0 22.0 NaN

Podemos corrigir a orientação usando o método transpose.

pd.DataFrame([serie_Peso, serie_Altura, serie_Idade]).transpose()
Peso Altura Idade
Ana 55.0 162.0 20.0
João 80.0 178.0 19.0
Maria 62.0 162.0 21.0
Pedro 67.0 165.0 22.0
Túlio 73.0 171.0 NaN

Criando um DataFrame a partir de arquivos

Para criar um DataFrame a partir de um arquivo, precisamos de funções do tipo pd.read_FORMATO, onde FORMATO indica o formato a ser importado sob o pressuposto de que a biblioteca pandas foi devidamente importada com pd.

Os formatos mais comuns são:

  • csv (comma-separated values),

  • xls ou xlsx (formatos do Microsoft Excel),

  • hdf5 (comumente utilizado em big data),

  • json (comumente utilizado em páginas da internet).

As funções para leitura correspondentes são:

  • pd.read_csv,

  • pd.read_excel,

  • pd.read_hdf,

  • pd.read_json,

respectivamente.

De todas elas, a função mais utilizada é read_csv. Ela possui vários argumentos. Vejamos os mais utilizados:

  • file_path_or_buffer: o endereço do arquivo a ser lido. Pode ser um endereço da internet.

  • sep: o separador entre as entradas de dados. O separador padrão é ,.

  • index_col: a coluna que deve ser usada para formar o index. O padrão é None. Porém pode ser alterado para outro. Um separador comumente encontrado é o \t (TAB).

  • names: nomes das colunas a serem usadas. O padrão é None.

  • header: número da linha que servirá como nome para as colunas. O padrão é infer (ou seja, tenta deduzir automaticamente). Se os nomes das colunas forem passados através do names, então header será automaticamente considerado como None.

Exemplo: considere o arquivo data/exemplo_data.csv contendo:

,coluna_1,coluna_2
2020-01-01,-0.4160923582996922,1.8103644347460834
2020-01-02,-0.1379696602473578,2.5785204825192785
2020-01-03,0.5758273450544708,0.06086648807755068
2020-01-04,-0.017367186564883633,1.2995865328684455
2020-01-05,1.3842792448510655,-0.3817320973859929
2020-01-06,0.5497056238566345,-1.308789022968975
2020-01-07,-0.2822962331437976,-1.6889791765925102
2020-01-08,-0.9897300598660013,-0.028120707936426497
2020-01-09,0.27558240737928663,-0.1776585993494299
2020-01-10,0.6851316082235455,0.5025348904591399

Para ler o arquivo acima basta fazer:

df_exemplo_0 = pd.read_csv('data/exemplo_data.csv')
df_exemplo_0
Unnamed: 0 coluna_1 coluna_2
0 2020-01-01 -0.416092 1.810364
1 2020-01-02 -0.137970 2.578520
2 2020-01-03 0.575827 0.060866
3 2020-01-04 -0.017367 1.299587
4 2020-01-05 1.384279 -0.381732
5 2020-01-06 0.549706 -1.308789
6 2020-01-07 -0.282296 -1.688979
7 2020-01-08 -0.989730 -0.028121
8 2020-01-09 0.275582 -0.177659
9 2020-01-10 0.685132 0.502535

No exemplo anterior, as colunas receberam nomes corretamentes exceto pela primeira coluna que gostaríamos de considerar como index. Neste caso fazemos:

df_exemplo = pd.read_csv('data/exemplo_data.csv', index_col=0)
df_exemplo
coluna_1 coluna_2
2020-01-01 -0.416092 1.810364
2020-01-02 -0.137970 2.578520
2020-01-03 0.575827 0.060866
2020-01-04 -0.017367 1.299587
2020-01-05 1.384279 -0.381732
2020-01-06 0.549706 -1.308789
2020-01-07 -0.282296 -1.688979
2020-01-08 -0.989730 -0.028121
2020-01-09 0.275582 -0.177659
2020-01-10 0.685132 0.502535

O método head do DataFrame

O método head, sem argumento, permite que visualizemos as 5 primeiras linhas do DataFrame.

df_exemplo.head()
coluna_1 coluna_2
2020-01-01 -0.416092 1.810364
2020-01-02 -0.137970 2.578520
2020-01-03 0.575827 0.060866
2020-01-04 -0.017367 1.299587
2020-01-05 1.384279 -0.381732

Se for passado um argumento com valor n, as n primeiras linhas são impressas.

df_exemplo.head(2)
coluna_1 coluna_2
2020-01-01 -0.416092 1.810364
2020-01-02 -0.137970 2.578520
df_exemplo.head(7)
coluna_1 coluna_2
2020-01-01 -0.416092 1.810364
2020-01-02 -0.137970 2.578520
2020-01-03 0.575827 0.060866
2020-01-04 -0.017367 1.299587
2020-01-05 1.384279 -0.381732
2020-01-06 0.549706 -1.308789
2020-01-07 -0.282296 -1.688979

O método tail do DataFrame

O método tail, sem argumento, retorna as últimas 5 linhas do DataFrame.

df_exemplo.tail()
coluna_1 coluna_2
2020-01-06 0.549706 -1.308789
2020-01-07 -0.282296 -1.688979
2020-01-08 -0.989730 -0.028121
2020-01-09 0.275582 -0.177659
2020-01-10 0.685132 0.502535

Se for passado um argumento com valor n, as n últimas linhas são impressas.

df_exemplo.tail(2)
coluna_1 coluna_2
2020-01-09 0.275582 -0.177659
2020-01-10 0.685132 0.502535
df_exemplo.tail(7)
coluna_1 coluna_2
2020-01-04 -0.017367 1.299587
2020-01-05 1.384279 -0.381732
2020-01-06 0.549706 -1.308789
2020-01-07 -0.282296 -1.688979
2020-01-08 -0.989730 -0.028121
2020-01-09 0.275582 -0.177659
2020-01-10 0.685132 0.502535

Atributos de Series e DataFrames

Atributos comumente usados para Series e DataFrames são:

  • shape: fornece as dimensões do objeto em questão (Series ou DataFrame) em formato consistente com o atributo shape de um array do numpy.

  • index: fornece o índice do objeto. No caso do DataFrame são os rótulos das linhas.

  • columns: fornece as colunas (apenas disponível para DataFrames)

Exemplo:

df_exemplo.shape
(10, 2)
serie_1 = pd.Series([1,2,3,4,5])
serie_1.shape
(5,)
df_exemplo.index
Index(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04', '2020-01-05',
       '2020-01-06', '2020-01-07', '2020-01-08', '2020-01-09', '2020-01-10'],
      dtype='object')
serie_1.index
RangeIndex(start=0, stop=5, step=1)
df_exemplo.columns
Index(['coluna_1', 'coluna_2'], dtype='object')

Se quisermos obter os dados contidos nos index ou nas Series podemos utilizar a propriedade .array.

serie_1.index.array
<PandasArray>
[0, 1, 2, 3, 4]
Length: 5, dtype: int64
df_exemplo.columns.array
<PandasArray>
['coluna_1', 'coluna_2']
Length: 2, dtype: object

Se o interesse for obter os dados como um array do numpy, devemos utilizar o método .to_numpy().

Exemplo:

serie_1.index.to_numpy()
array([0, 1, 2, 3, 4])
df_exemplo.columns.to_numpy()
array(['coluna_1', 'coluna_2'], dtype=object)

O método .to_numpy() também está disponível em DataFrames:

df_exemplo.to_numpy()
array([[-0.41609236,  1.81036443],
       [-0.13796966,  2.57852048],
       [ 0.57582735,  0.06086649],
       [-0.01736719,  1.29958653],
       [ 1.38427924, -0.3817321 ],
       [ 0.54970562, -1.30878902],
       [-0.28229623, -1.68897918],
       [-0.98973006, -0.02812071],
       [ 0.27558241, -0.1776586 ],
       [ 0.68513161,  0.50253489]])

A função do numpy asarray() é compatível com index, columns e DataFrames do pandas:

np.asarray(df_exemplo.index)
array(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04',
       '2020-01-05', '2020-01-06', '2020-01-07', '2020-01-08',
       '2020-01-09', '2020-01-10'], dtype=object)
np.asarray(df_exemplo.columns)
array(['coluna_1', 'coluna_2'], dtype=object)
np.asarray(df_exemplo)
array([[-0.41609236,  1.81036443],
       [-0.13796966,  2.57852048],
       [ 0.57582735,  0.06086649],
       [-0.01736719,  1.29958653],
       [ 1.38427924, -0.3817321 ],
       [ 0.54970562, -1.30878902],
       [-0.28229623, -1.68897918],
       [-0.98973006, -0.02812071],
       [ 0.27558241, -0.1776586 ],
       [ 0.68513161,  0.50253489]])

Informações sobre as colunas de um DataFrame

Para obtermos uma breve descrição sobre as colunas de um DataFrame utilizamos o método info.

Exemplo:

df_exemplo.info()
<class 'pandas.core.frame.DataFrame'>
Index: 10 entries, 2020-01-01 to 2020-01-10
Data columns (total 2 columns):
 #   Column    Non-Null Count  Dtype  
---  ------    --------------  -----  
 0   coluna_1  10 non-null     float64
 1   coluna_2  10 non-null     float64
dtypes: float64(2)
memory usage: 240.0+ bytes

Criando arquivos a partir de DataFrames

Para criar arquivos a partir de DataFrames, basta utilizar os métodos do tipo pd.to_FORMATO, onde FORMATO indica o formato a ser exportado e supondo que a biblioteca pandas foi importada com pd.

Com relação aos tipos de arquivo anteriores, os métodos para exportação correspondentes são:

  • .to_csv (‘endereço_do_arquivo’),

  • .to_excel (‘endereço_do_arquivo’),

  • .to_hdf (‘endereço_do_arquivo’),

  • .to_json(‘endereço_do_arquivo’),

onde endereço_do_arquivo é uma str que contém o endereço do arquivo a ser exportado.

Exemplo:

Para exportar para o arquivo exemplo_novo.csv, utilizaremos o método .to_csv ao DataFrame df_exemplo:

df_exemplo.to_csv('data/exemplo_novo.csv')

Exemplo COVID-19 PB

Dados diários de COVID-19 do estado da Paraíba:

Fonte: https://superset.plataformatarget.com.br/superset/dashboard/microdados/

dados_covid_PB = pd.read_csv('https://superset.plataformatarget.com.br/superset/explore_json/?form_data=%7B%22slice_id%22%3A1550%7D&csv=true', 
                             sep=',', index_col=0)
dados_covid_PB.info()
<class 'pandas.core.frame.DataFrame'>
Index: 331 entries, 2021-02-09 to 2020-03-16
Data columns (total 7 columns):
 #   Column            Non-Null Count  Dtype  
---  ------            --------------  -----  
 0   casosAcumulados   331 non-null    int64  
 1   casosNovos        331 non-null    int64  
 2   descartados       331 non-null    int64  
 3   recuperados       331 non-null    int64  
 4   obitosAcumulados  331 non-null    int64  
 5   obitosNovos       331 non-null    int64  
 6   Letalidade        331 non-null    float64
dtypes: float64(1), int64(6)
memory usage: 20.7+ KB
dados_covid_PB.head()
casosAcumulados casosNovos descartados recuperados obitosAcumulados obitosNovos Letalidade
data
2021-02-09 199706 971 237369 152779 4171 13 0.0209
2021-02-08 198735 611 237189 151879 4158 12 0.0209
2021-02-07 198124 664 237072 151535 4146 11 0.0209
2021-02-06 197460 918 236774 150175 4135 12 0.0209
2021-02-05 196542 1060 236216 150169 4123 13 0.0210
dados_covid_PB.tail()
casosAcumulados casosNovos descartados recuperados obitosAcumulados obitosNovos Letalidade
data
2020-03-20 0 0 0 0 0 0 0.0
2020-03-19 0 0 0 0 0 0 0.0
2020-03-18 0 0 0 0 0 0 0.0
2020-03-17 0 0 0 0 0 0 0.0
2020-03-16 0 0 0 0 0 0 0.0
dados_covid_PB['estado'] = 'PB'
dados_covid_PB.head()
casosAcumulados casosNovos descartados recuperados obitosAcumulados obitosNovos Letalidade estado
data
2021-02-09 199706 971 237369 152779 4171 13 0.0209 PB
2021-02-08 198735 611 237189 151879 4158 12 0.0209 PB
2021-02-07 198124 664 237072 151535 4146 11 0.0209 PB
2021-02-06 197460 918 236774 150175 4135 12 0.0209 PB
2021-02-05 196542 1060 236216 150169 4123 13 0.0210 PB
dados_covid_PB.to_csv('data/dadoscovidpb.csv')

Índices dos valores máximos ou mínimos

Os métodos idxmin() e idxmax() retornam o index cuja entrada fornece o valor mínimo ou máximo da Series ou DataFrame. Se houver múltiplas ocorrências de mínimos ou máximos, o método retorna a primeira ocorrência.

Vamos recriar um DataFrame genérico.

serie_Idade = pd.Series({'Ana':20, 'João': 19, 'Maria': 21, 'Pedro': 22, 'Túlio': 20}, name="Idade")
serie_Peso = pd.Series({'Ana':55, 'João': 80, 'Maria': 62, 'Pedro': 67, 'Túlio': 73}, name="Peso")
serie_Altura = pd.Series({'Ana':162, 'João': 178, 'Maria': 162, 'Pedro': 165, 'Túlio': 171}, name="Altura")
dicionario_series_exemplo = {'Idade': serie_Idade, 'Peso': serie_Peso, 'Altura': serie_Altura}
df_dict_series = pd.DataFrame(dicionario_series_exemplo)
df_dict_series
Idade Peso Altura
Ana 20 55 162
João 19 80 178
Maria 21 62 162
Pedro 22 67 165
Túlio 20 73 171

Assim, podemos localizar quem possui menores idade, peso e altura.

df_dict_series.idxmin()
Idade     João
Peso       Ana
Altura     Ana
dtype: object

De igual forma, localizamos quem possui maiores idade, peso e altura.

df_dict_series.idxmax()
Idade     Pedro
Peso       João
Altura     João
dtype: object

Exemplo: Aplicaremos as funções idxmin() e idxmax() aos dados do arquivo data/exemplo_data.csv para localizar entradas de interesse.

df_exemplo = pd.read_csv('data/exemplo_data.csv', index_col=0); df_exemplo
coluna_1 coluna_2
2020-01-01 -0.416092 1.810364
2020-01-02 -0.137970 2.578520
2020-01-03 0.575827 0.060866
2020-01-04 -0.017367 1.299587
2020-01-05 1.384279 -0.381732
2020-01-06 0.549706 -1.308789
2020-01-07 -0.282296 -1.688979
2020-01-08 -0.989730 -0.028121
2020-01-09 0.275582 -0.177659
2020-01-10 0.685132 0.502535
df_exemplo = pd.DataFrame(df_exemplo, columns=['coluna_1','coluna_2','coluna_3'])

Inserimos uma terceira coluna com dados fictícios.

df_exemplo['coluna_3'] = pd.Series([1,2,3,4,5,6,7,8,np.nan,np.nan],index=df_exemplo.index)
df_exemplo
coluna_1 coluna_2 coluna_3
2020-01-01 -0.416092 1.810364 1.0
2020-01-02 -0.137970 2.578520 2.0
2020-01-03 0.575827 0.060866 3.0
2020-01-04 -0.017367 1.299587 4.0
2020-01-05 1.384279 -0.381732 5.0
2020-01-06 0.549706 -1.308789 6.0
2020-01-07 -0.282296 -1.688979 7.0
2020-01-08 -0.989730 -0.028121 8.0
2020-01-09 0.275582 -0.177659 NaN
2020-01-10 0.685132 0.502535 NaN

Os index correspondentes aos menores e maiores valores são datas, evidentemente.

df_exemplo.idxmin()
coluna_1    2020-01-08
coluna_2    2020-01-07
coluna_3    2020-01-01
dtype: object
df_exemplo.idxmax()
coluna_1    2020-01-05
coluna_2    2020-01-02
coluna_3    2020-01-08
dtype: object

Reindexação de DataFrames

No pandas, o método reindex

  • reordena o DataFrame de acordo com o conjunto de rótulos inserido como argumento;

  • insere valores faltantes caso um rótulo do novo index não tenha valor atribuído no conjunto de dados;

  • remove valores correspondentes a rótulos que não estão presentes no novo index.

Exemplos:

df_dict_series.reindex(index=['Victor', 'Túlio', 'Pedro', 'João'], columns=['Altura','Peso','IMC'])
Altura Peso IMC
Victor NaN NaN NaN
Túlio 171.0 73.0 NaN
Pedro 165.0 67.0 NaN
João 178.0 80.0 NaN

Remoção de linhas ou colunas de um DataFrame

Para remover linhas ou colunas de um DataFrame do pandas podemos utilizar o método drop. O argumento axis identifica o eixo de remoção: axis=0, que é o padrão, indica a remoção de uma ou mais linhas; axis=1 indica a remoção de uma ou mais colunas.

Nos exemplos que segue, note que novos DataFrames são obtidos a partir de df_dict_series sem que este seja sobrescrito.

df_dict_series.drop('Túlio') # axis=0 implícito 
Idade Peso Altura
Ana 20 55 162
João 19 80 178
Maria 21 62 162
Pedro 22 67 165
df_dict_series.drop(['Ana','Maria'], axis=0)
Idade Peso Altura
João 19 80 178
Pedro 22 67 165
Túlio 20 73 171
df_dict_series.drop(['Idade'], axis=1)
Peso Altura
Ana 55 162
João 80 178
Maria 62 162
Pedro 67 165
Túlio 73 171

Renomeando index e columns

O método rename retorna uma cópia na qual o index (no caso de Series e DataFrames) e columns (no caso de DataFrames) foram renomeados. O método aceita como entrada um dicionário, uma Series do pandas ou uma função.

Exemplo:

serie_exemplo = pd.Series([1,2,3], index=['a','b','c'])
serie_exemplo
a    1
b    2
c    3
dtype: int64
serie_exemplo.rename({'a':'abacaxi', 'b':'banana', 'c': 'cebola'})
abacaxi    1
banana     2
cebola     3
dtype: int64

Exemplo:

df_dict_series
Idade Peso Altura
Ana 20 55 162
João 19 80 178
Maria 21 62 162
Pedro 22 67 165
Túlio 20 73 171
df_dict_series.rename(index = {'Ana':'a', 'João':'j', 'Maria':'m', 'Pedro':'p','Túlio':'t'},
                     columns = {'Idade':'I', 'Peso':'P','Altura':'A'})
I P A
a 20 55 162
j 19 80 178
m 21 62 162
p 22 67 165
t 20 73 171

No próximo exemplo, usamos uma Series para renomear os rótulos.

indice_novo = pd.Series({'Ana':'a', 'João':'j', 'Maria':'m', 'Pedro':'p','Túlio':'t'})
df_dict_series.rename(index = indice_novo)
Idade Peso Altura
a 20 55 162
j 19 80 178
m 21 62 162
p 22 67 165
t 20 73 171

Neste exemplo, usamos a função str.upper (altera a str para “todas maiúsculas”) para renomear colunas.

df_dict_series.rename(columns=str.upper)
IDADE PESO ALTURA
Ana 20 55 162
João 19 80 178
Maria 21 62 162
Pedro 22 67 165
Túlio 20 73 171

Ordenando Series e DataFrames

É possível ordenar ambos pelos rótulos do index (para tanto é necessário que eles sejam ordenáveis) ou por valores nas colunas.

O método sort_index ordena a Series ou o DataFrame pelo index. O método sort_values ordena a Series ou o DataFrame pelos valores (escolhendo uma ou mais colunas no caso de DataFrames). No caso do DataFrame, o argumento by é necessário para indicar qual(is) coluna(s) será(ão) utilizada(s) como base para a ordenação.

Exemplos:

serie_desordenada = pd.Series({'Maria': 21, 'Pedro': 22, 'Túlio': 20, 'João': 19, 'Ana':20}); 
serie_desordenada
Maria    21
Pedro    22
Túlio    20
João     19
Ana      20
dtype: int64
serie_desordenada.sort_index() # ordenação alfabética
Ana      20
João     19
Maria    21
Pedro    22
Túlio    20
dtype: int64

Mais exemplos:

df_desordenado = df_dict_series.reindex(index=['Pedro','Maria','Ana','Túlio','João'])
df_desordenado
Idade Peso Altura
Pedro 22 67 165
Maria 21 62 162
Ana 20 55 162
Túlio 20 73 171
João 19 80 178
df_desordenado.sort_index()
Idade Peso Altura
Ana 20 55 162
João 19 80 178
Maria 21 62 162
Pedro 22 67 165
Túlio 20 73 171

Mais exemplos:

serie_desordenada.sort_values()
João     19
Túlio    20
Ana      20
Maria    21
Pedro    22
dtype: int64
df_desordenado.sort_values(by=['Altura']) # ordena por 'Altura'
Idade Peso Altura
Maria 21 62 162
Ana 20 55 162
Pedro 22 67 165
Túlio 20 73 171
João 19 80 178

No caso de “empate”, podemos utilizar outra coluna para desempatar.

df_desordenado.sort_values(by=['Altura','Peso']) # usa a coluna 'Peso' para desempatar
Idade Peso Altura
Ana 20 55 162
Maria 21 62 162
Pedro 22 67 165
Túlio 20 73 171
João 19 80 178

Os métodos sort_index e sort_values admitem o argumento opcional ascending, que permite inverter a ordenação caso tenha valor False.

df_desordenado.sort_index(ascending=False)
Idade Peso Altura
Túlio 20 73 171
Pedro 22 67 165
Maria 21 62 162
João 19 80 178
Ana 20 55 162
df_desordenado.sort_values(by=['Idade'], ascending=False)
Idade Peso Altura
Pedro 22 67 165
Maria 21 62 162
Ana 20 55 162
Túlio 20 73 171
João 19 80 178

Comparando Series e DataFrames

Series e DataFrames possuem os seguintes métodos de comparação lógica:

  • eq (igual);

  • ne (diferente);

  • lt (menor do que);

  • gt (maior do que);

  • le (menor ou igual a);

  • ge (maior ou igual a)

que permitem a utilização dos operadores binários ==, !=, <, >, <= e >=, respectivamente. As comparações são realizadas em cada entrada da Series ou do DataFrame.

Observação: Para que esses métodos sejam aplicados, todos os objetos presentes nas colunas do DataFrame devem ser de mesma natureza. Por exemplo, se um DataFrame possui algumas colunas numéricas e outras com strings, ao realizar uma comparação do tipo > 1, um erro ocorrerá, pois o pandas tentará comparar objetos do tipo int com objetos do tipo str, assim gerando uma incompatibilidade.

Exemplos:

serie_exemplo
a    1
b    2
c    3
dtype: int64
serie_exemplo == 2
a    False
b     True
c    False
dtype: bool

De outra forma:

serie_exemplo.eq(2)
a    False
b     True
c    False
dtype: bool
serie_exemplo > 1
a    False
b     True
c     True
dtype: bool

Ou, na forma funcional:

serie_exemplo.gt(1)
a    False
b     True
c     True
dtype: bool
df_exemplo > 1
coluna_1 coluna_2 coluna_3
2020-01-01 False True False
2020-01-02 False True True
2020-01-03 False False True
2020-01-04 False True True
2020-01-05 True False True
2020-01-06 False False True
2020-01-07 False False True
2020-01-08 False False True
2020-01-09 False False False
2020-01-10 False False False

Importante: Ao comparar np.nan, o resultado tipicamente é falso:

np.nan == np.nan
False
np.nan > np.nan
False
np.nan >= np.nan
False

Só é verdadeiro para indicar que é diferente:

np.nan != np.nan
True

Neste sentido, podemos ter tabelas iguais sem que a comparação usual funcione:

# 'copy', como o nome sugere, gera uma cópia do DataFrame
df_exemplo_2 = df_exemplo.copy() 
(df_exemplo == df_exemplo_2)
coluna_1 coluna_2 coluna_3
2020-01-01 True True True
2020-01-02 True True True
2020-01-03 True True True
2020-01-04 True True True
2020-01-05 True True True
2020-01-06 True True True
2020-01-07 True True True
2020-01-08 True True True
2020-01-09 True True False
2020-01-10 True True False

O motivo de haver entradas como False ainda que df_exemplo_2 seja uma cópia exata de df_exemplo é a presença do np.nan. Neste caso, devemos utilizar o método equals para realizar a comparação.

df_exemplo.equals(df_exemplo_2)
True

Os métodos any, all e a propriedade empty

O método any é aplicado a entradas booleanas (verdadeiras ou falsas) e retorna verdadeiro se existir alguma entrada verdadeira, ou falso, se todas forem falsas. O método all é aplicado a entradas booleanas e retorna verdadeiro se todas as entradas forem verdadeiras, ou falso, se houver pelo menos uma entrada falsa. A propriedade empty retorna verdadeiro se a Series ou o DataFrame estiver vazio, ou falso caso contrário.

Exemplos:

serie_exemplo
a    1
b    2
c    3
dtype: int64
serie_exemplo_2 = serie_exemplo-2; 
serie_exemplo_2
a   -1
b    0
c    1
dtype: int64
(serie_exemplo_2 > 0).any()
True
(serie_exemplo > 1).all()
False

Este exemplo reproduz um valor False único.

(df_exemplo == df_exemplo_2).all().all()
False
serie_exemplo.empty
False

Mais exemplos:

(df_exemplo == df_exemplo_2).any()
coluna_1    True
coluna_2    True
coluna_3    True
dtype: bool
df_exemplo.empty
False
df_vazio = pd.DataFrame()
df_vazio.empty
True

Selecionando colunas de um DataFrame

Para selecionar colunas de um DataFrame, basta aplicar colchetes a uma lista contendo os nomes das colunas de interesse.

No exemplo abaixo, temos um DataFrame contendo as colunas 'Idade', 'Peso' e 'Altura'. Selecionaremos 'Peso' e 'Altura', apenas.

df_dict_series[['Peso','Altura']]
Peso Altura
Ana 55 162
João 80 178
Maria 62 162
Pedro 67 165
Túlio 73 171

Se quisermos selecionar apenas uma coluna, não há necessidade de inserir uma lista. Basta utilizar o nome da coluna:

df_dict_series['Peso']
Ana      55
João     80
Maria    62
Pedro    67
Túlio    73
Name: Peso, dtype: int64

Para remover colunas, podemos utilizar o método drop.

df_dict_series.drop(['Peso','Altura'], axis=1)
Idade
Ana 20
João 19
Maria 21
Pedro 22
Túlio 20

Criando novas colunas a partir de colunas existentes

Um método eficiente para criar novas colunas a partir de colunas já existentes é eval. Neste método, podemos utilizar como argumento uma string contendo uma expressão matemática envolvendo nomes de colunas do DataFrame.

Como exemplo, vamos ver como calcular o IMC no DataFrame anterior:

df_dict_series.eval('Peso/(Altura/100)**2')
Ana      20.957171
João     25.249337
Maria    23.624447
Pedro    24.609734
Túlio    24.964946
dtype: float64

Se quisermos obter um DataFrame contendo o IMC como uma nova coluna, podemos utilizar o método assign (sem modificar o DataFrame original):

df_dict_series.assign(IMC=round(df_dict_series.eval('Peso/(Altura/100)**2'),2))
Idade Peso Altura IMC
Ana 20 55 162 20.96
João 19 80 178 25.25
Maria 21 62 162 23.62
Pedro 22 67 165 24.61
Túlio 20 73 171 24.96
df_dict_series # não modificado
Idade Peso Altura
Ana 20 55 162
João 19 80 178
Maria 21 62 162
Pedro 22 67 165
Túlio 20 73 171

Se quisermos modificar o DataFrame para incluir a coluna IMC fazemos:

df_dict_series['IMC']=round(df_dict_series.eval('Peso/(Altura/100)**2'),2)
df_dict_series # modificado "in-place"
Idade Peso Altura IMC
Ana 20 55 162 20.96
João 19 80 178 25.25
Maria 21 62 162 23.62
Pedro 22 67 165 24.61
Túlio 20 73 171 24.96

Selecionando linhas de um DataFrame:

Podemos selecionar linhas de um DataFrame de diversas formas diferentes. Veremos agora algumas dessas formas.

Diferentemente da forma de selecionar colunas, para selecionar diretamente linhas de um DataFrame devemos utilizar o método loc (fornecendo o index, isto é, o rótulo da linha) ou o iloc (fornecendo a posição da linha):

Trabalharemos a seguir com um banco de dados atualizado sobre a COVID-19. Para tanto, importaremos o módulo datetime que nos auxiliará com datas.

import datetime
dados_covid_PB = pd.read_csv('https://superset.plataformatarget.com.br/superset/explore_json/?form_data=%7B%22slice_id%22%3A1550%7D&csv=true', 
                             sep=',', index_col=0)

# busca o banco na data D-1, visto que a atualização
# ocorre em D
ontem = (datetime.date.today() - datetime.timedelta(days=1)).strftime('%Y-%m-%d') 
dados_covid_PB.head(1)
casosAcumulados casosNovos descartados recuperados obitosAcumulados obitosNovos Letalidade
data
2021-02-09 199706 971 237369 152779 4171 13 0.0209

Podemos ver as informações de um único dia como argumento. Para tanto, excluímos a coluna 'Letalidade' (valor não inteiro) e convertemos o restante para valores inteiros:

dados_covid_PB.loc[ontem].drop('Letalidade').astype('int')
casosAcumulados     199706
casosNovos             971
descartados         237369
recuperados         152779
obitosAcumulados      4171
obitosNovos             13
Name: 2021-02-09, dtype: int64

Podemos selecionar um intervalo de datas como argumento (excluindo letalidade):

dados_covid_PB.index = pd.to_datetime(dados_covid_PB.index) # Convertendo o index de string para data
dados_covid_PB.loc[pd.date_range('2021-02-01',periods=5,freq="D")].drop('Letalidade',axis=1) 
                #função pd.date_range é muito útil para criar índices a partir de datas.
casosAcumulados casosNovos descartados recuperados obitosAcumulados obitosNovos
2021-02-01 192598 1014 234215 149235 4068 12
2021-02-02 193465 867 234366 149242 4082 14
2021-02-03 194519 1054 234902 149248 4096 14
2021-02-04 195482 963 235319 149792 4110 14
2021-02-05 196542 1060 236216 150169 4123 13

Podemos colocar uma lista como argumento:

dados_covid_PB.loc[pd.to_datetime(['2021-01-01','2021-02-01'])]
casosAcumulados casosNovos descartados recuperados obitosAcumulados obitosNovos Letalidade
2021-01-01 167062 578 219890 127388 3680 8 0.0220
2021-02-01 192598 1014 234215 149235 4068 12 0.0211

Vamos agora examinar os dados da posição 100 (novamente excluindo a coluna letalidade e convertendo para inteiro):

dados_covid_PB.iloc[100].drop('Letalidade').astype('int')
casosAcumulados     133220
casosNovos              71
descartados         185162
recuperados         108737
obitosAcumulados      3107
obitosNovos              6
Name: 2020-11-01 00:00:00, dtype: int64

Podemos colocar um intervalo como argumento:

dados_covid_PB.iloc[50:55].drop('Letalidade', axis=1).astype('int') 
casosAcumulados casosNovos descartados recuperados obitosAcumulados obitosNovos
data
2020-12-21 159639 203 213617 123430 3552 13
2020-12-20 159436 327 213356 123417 3539 10
2020-12-19 159109 660 213149 123295 3529 6
2020-12-18 158449 1053 212439 122935 3523 16
2020-12-17 157396 1274 210628 122219 3507 20

Selecionando colunas pelos métodos loc e iloc

Podemos selecionar colunas utilizando os métodos loc e iloc utilizando um argumento adicional.

dados_covid_PB.loc[:,['casosNovos','obitosNovos']]
casosNovos obitosNovos
data
2021-02-09 971 13
2021-02-08 611 12
2021-02-07 664 11
2021-02-06 918 12
2021-02-05 1060 13
... ... ...
2020-03-20 0 0
2020-03-19 0 0
2020-03-18 0 0
2020-03-17 0 0
2020-03-16 0 0

331 rows × 2 columns

dados_covid_PB.iloc[:,4:6] # fatiamento na coluna
obitosAcumulados obitosNovos
data
2021-02-09 4171 13
2021-02-08 4158 12
2021-02-07 4146 11
2021-02-06 4135 12
2021-02-05 4123 13
... ... ...
2020-03-20 0 0
2020-03-19 0 0
2020-03-18 0 0
2020-03-17 0 0
2020-03-16 0 0

331 rows × 2 columns

Selecionando linhas e colunas específicas pelos métodos loc e iloc:

Usando o mesmo princípio de fatiamento aplicado a arrays do numpy, podemos selecionar linhas e colunas em um intervalo específico de forma a obter uma subtabela.

dados_covid_PB.iloc[95:100,4:6]
obitosAcumulados obitosNovos
data
2020-11-06 3146 8
2020-11-05 3138 8
2020-11-04 3130 11
2020-11-03 3119 11
2020-11-02 3108 1

Neste exemplo um pouco mais complexo, buscamos casos novos e óbitos novos em um período específico e ordenamos a tabela da data mais recente para a mais antiga.

dados_covid_PB.loc[pd.date_range('2020-04-06','2020-04-10'),['casosNovos','obitosNovos']].sort_index(ascending=False)
casosNovos obitosNovos
2020-04-10 6 0
2020-04-09 24 4
2020-04-08 14 3
2020-04-07 5 0
2020-04-06 1 0

Suponha que o peso de Ana foi medido corretamente, mas registrado de maneira errônea no DataFrame df_dict_series como 55.

df_dict_series
Idade Peso Altura IMC
Ana 20 55 162 20.96
João 19 80 178 25.25
Maria 21 62 162 23.62
Pedro 22 67 165 24.61
Túlio 20 73 171 24.96

Supondo que, na realidade, o valor é 65, alteramos a entrada específica com um simples loc. Em seguida, atualizamos a tabela.

df_dict_series.loc['Ana','Peso'] = 65

df_dict_series = df_dict_series.assign(IMC=round(df_dict_series.eval('Peso/(Altura/100)**2'),2)) # O IMC mudou
df_dict_series
Idade Peso Altura IMC
Ana 20 65 162 24.77
João 19 80 178 25.25
Maria 21 62 162 23.62
Pedro 22 67 165 24.61
Túlio 20 73 171 24.96

Selecionando linha através de critérios lógicos ou funções

Vamos selecionar quais os dias em que houve mais de 40 mortes registradas:

dados_covid_PB.loc[dados_covid_PB['obitosNovos']>40]
casosAcumulados casosNovos descartados recuperados obitosAcumulados obitosNovos Letalidade
data
2020-07-21 68844 1164 78605 25028 1558 41 0.0226
2020-07-15 63939 1477 74399 23695 1383 41 0.0216
2020-07-02 49536 1361 48272 16349 1044 42 0.0211
2020-06-30 46957 1900 43070 14930 977 46 0.0208

Selecionando os dias com mais de 25 óbitos e mais de 1500 casos novos:

dados_covid_PB.loc[(dados_covid_PB.obitosNovos > 25) & (dados_covid_PB.casosNovos>1500)]
casosAcumulados casosNovos descartados recuperados obitosAcumulados obitosNovos Letalidade
data
2020-08-04 85760 1549 106500 38554 1901 31 0.0222
2020-07-31 82794 1686 96753 35971 1811 26 0.0219
2020-07-23 73104 2132 84047 28566 1618 37 0.0221
2020-07-10 59118 1504 69567 21481 1229 33 0.0208
2020-07-08 56344 1542 67549 19999 1171 26 0.0208
2020-07-07 54802 1651 64933 19373 1145 27 0.0209
2020-06-30 46957 1900 43070 14930 977 46 0.0208
2020-06-09 22452 1501 20650 4671 534 27 0.0238

Obs.: Note que podemos utilizar o nome da coluna como um atributo.

Vamos inserir uma coluna sobrenome no df_dict_series:

df_dict_series['Sobrenome'] = ['Silva', 'PraDo', 'Sales', 'MachadO', 'Coutinho']
df_dict_series
Idade Peso Altura IMC Sobrenome
Ana 20 65 162 24.77 Silva
João 19 80 178 25.25 PraDo
Maria 21 62 162 23.62 Sales
Pedro 22 67 165 24.61 MachadO
Túlio 20 73 171 24.96 Coutinho

Vamos encontrar as linhas cujo sobrenome termina em “do”. Para tanto, note que a função abaixo retorna True se o final é “do” e False caso contrário.

def verifica_final_do(palavra):
    return palavra.lower()[-2:] == 'do'

Obs.: Note que convertemos tudo para minúsculo.

Agora vamos utilizar essa função para alcançar nosso objetivo:

# 'map' aplica a função lambda a cada elemento da *Series*
df_dict_series['Sobrenome'].map(lambda palavra: palavra.lower()[-2:]=='do') 
Ana      False
João      True
Maria    False
Pedro     True
Túlio    False
Name: Sobrenome, dtype: bool
# procurando no df inteiro
df_dict_series.loc[df_dict_series['Sobrenome'].map(lambda palavra: palavra.lower()[-2:]=='do')]
Idade Peso Altura IMC Sobrenome
João 19 80 178 25.25 PraDo
Pedro 22 67 165 24.61 MachadO

Vamos selecionar as linhas do mês 2 (fevereiro) usando index.month:

dados_covid_PB.loc[dados_covid_PB.index.month==2].head()
casosAcumulados casosNovos descartados recuperados obitosAcumulados obitosNovos Letalidade
data
2021-02-09 199706 971 237369 152779 4171 13 0.0209
2021-02-08 198735 611 237189 151879 4158 12 0.0209
2021-02-07 198124 664 237072 151535 4146 11 0.0209
2021-02-06 197460 918 236774 150175 4135 12 0.0209
2021-02-05 196542 1060 236216 150169 4123 13 0.0210

Selecionando linhas com o método query

Similarmente ao método eval, ao utilizarmos query, podemos criar expressões lógicas a partir de nomes das colunas do DataFrame.

Assim, podemos reescrever o código

dados_covid_PB.loc[(dados_covid_PB.obitosNovos>25) & 
                   (dados_covid_PB.casosNovos>1500)]

como

dados_covid_PB.query('obitosNovos>25 and casosNovos>1500') # note que 'and' é usado em vez de '&'
casosAcumulados casosNovos descartados recuperados obitosAcumulados obitosNovos Letalidade
data
2020-08-04 85760 1549 106500 38554 1901 31 0.0222
2020-07-31 82794 1686 96753 35971 1811 26 0.0219
2020-07-23 73104 2132 84047 28566 1618 37 0.0221
2020-07-10 59118 1504 69567 21481 1229 33 0.0208
2020-07-08 56344 1542 67549 19999 1171 26 0.0208
2020-07-07 54802 1651 64933 19373 1145 27 0.0209
2020-06-30 46957 1900 43070 14930 977 46 0.0208
2020-06-09 22452 1501 20650 4671 534 27 0.0238