sexta-feira, 7 de agosto de 2020

Como ler e criar arquivos do Excel com pandas

Imagem de Esa Riutta por Pixabay

O arquivo CSV é muito comum e utilizado, mas se compararmos esse arquivo com o gigante da Microsoft, o arquivo do Excel, não parece ser grande coisa. Em muitas pesquisas de consultoria, os resultados apontam que o Excel é uma das ferramentas mais críticas em muitas empresas. O Excel é muito utilizado nas empresas para a tomada de muitas decisões importantes.
E por isso a biblioteca pandas fornece métodos para poder manipular esses arquivos. Nesse tutorial vamos aprender como criar e ler arquivos do Excel com a biblioteca pandas.
Antes de continuarmos vamos precisar instalar os pacotes xlwt e openpyxl. Esses pacotes são necessários para podermos exportar o nosso dataframe para um arquivo xls e xlsx. Utilize o pip para instalar os dois pacotes: pip install openpyxl xlwt.

Como ler e criar arquivos do Excel com pandas

O método que vamos utilizar, para criar um arquivo do excel, é o to_excel(). O que precisamos fazer é criar um dataframe e chamar esse método. Veja um exemplo:

>>> import pandas as pd
>>> nome = {'nome':['Fabiana', 'Diana', 'Amanda'],
... 'u_nome':['Ferreira', 'Lima', 'Saturno']}
>>> dataframe = pd.DataFrame(nome)
>>> dataframe.to_excel('dataframe.xls')
>>> dataframe.to_excel('dataframe.xlsx')

Abrindo o arquivo, que acabamos de criar, o resultado deve ser igual a este:

Para ler o arquivo que criamos, e qualquer outro feito no Excel, utilizamos o método read_excel(). Esse método precisa do complemento xlrd. Para baixo use o pip: pip install xlrd.

>>> import pandas as pd
>>> dataframe = pd.read_excel('dataframe.xls')
>>> dataframe
   Unnamed: 0     nome    u_nome
0           0  Fabiana  Ferreira
1           1    Diana      Lima
2           2   Amanda   Saturno

Como o arquivo já tem uma coluna de índice, podemos especificar isso para o método read_excel(), para evitar dados duplicados. Assim não teremos duas colunas de índice.

>>> dataframe = pd.read_excel('dataframe.xlsx', index_col=0)
>>> dataframe
      nome    u_nome
0  Fabiana  Ferreira
1    Diana      Lima
2   Amanda   Saturno

Com esses exemplos aprendemos que podemos ler e criar arquivos do Excel com a biblioteca pandas. Com a instalação de poucos pacotes já podemos manipular o tipo de arquivo mais utilizados pelas empresas.

Referência:
Método DataFrame()
Método to_excel()
Método read_excel()

quinta-feira, 6 de agosto de 2020

Lendo arquivo csv enorme com pandas

Imagem de Tania Dimas por Pixabay

A biblioteca pandas precisa que o computador tenha memoria disponível suficiente para poder funcionar corretamente. Todos os dataframes são alocados na memoria antes de podermos fazer qualquer operação com esses dados. Se você precisar ler os dados de um dataset muito grande, a biblioteca pandas fornece algumas opções. Caso o seu arquivo possa ser processado em partes, você pode ler algumas partes e depois processa-las. Se no seu dispositivo tiver memoria suficiente você pode carregar o arquivo inteiro. Podemos usar algumas dicas para diminuir o tamanho do arquivo.
O recomendado é que o seu computador tenha de três a dez vezes a quantidade de memoria do arquivo que você está trabalhando. Essa memoria extra é usada para manipular esses dados.

Como ler arquivo csv muito grande

Nesse tutorial vamos ler o conjunto o dados filmes.csv. Esse dataset cabe na memória de qualquer computador da atualidade, mas para fins didáticos vamos fazer de conta que não existe memória suficiente. E quando tentamos carregar esse dataset na memória recebemos um erro de falta de memória.
Quando lidamos com arquivos grandes que nossos computadores não tenham memória suficiente precisamos definir o total de memória que o arquivo inteiro vai ocupar. Para essa tarefa vamos usar o parâmetro nrows. Com esse parâmetro definimos a quantidade de linhas que serão carregados na memória.

>>> import pandas as pd
>>> filmes = pd.read_csv('filmes.csv', nrows=1000)
>>> filmes
     indice  codigo_obra  ...     cnpj_requerente data_exibicao
0         0      15639.0  ...  00.568.159/0001-07     06/jul/12
1         1          NaN  ...  03.599.148/0001-82     13/jul/04
2         2      26453.0  ...  02.341.697/0001-90    26/09/2007
3         3          NaN  ...  27.654.722/0001-16    16/12/2002
4         4       4806.0  ...  00.979.601/0001-98     15/jan/03
..      ...          ...  ...                 ...           ...
995     995   18001845.0  ...  00.979.601/0001-98    09/05/2018
996     996   17004586.0  ...  03.599.148/0001-82     19/jul/18
997     997   18002435.0  ...  27.654.722/0001-16     08/jun/18
998     998   17005733.0  ...  06.236.625/0001-33     05/jun/18
999     999   18002628.0  ...  08.346.733/0001-94     04/jul/18

[1000 rows x 9 columns]

Uma coisa importante é saber o quanto de memória um dataframe esta utilizando, e para isso temos o método info(). Esse método fornece algumas informações sobre o dataframe, incluindo a quantidade de memória utilizada.

>>> filmes.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1000 entries, 0 to 999
Data columns (total 9 columns):
 #   Column                   Non-Null Count  Dtype
---  ------                   --------------  -----
 0   indice                   1000 non-null   int64
 1   codigo_obra              997 non-null    float64
 2   titulo_original          1000 non-null   object
 3   titulo_brasil            1000 non-null   object
 4   ano_producao             999 non-null    float64
 5   diretor                  1000 non-null   object
 6   razao_social_requerente  1000 non-null   object
 7   cnpj_requerente          1000 non-null   object
 8   data_exibicao            1000 non-null   object
dtypes: float64(2), int64(1), object(6)
memory usage: 46.9+ KB

Além das linhas podemos definir quais colunas queremos carregar na memória com o parâmetro usecols.

>>> col = [
... 'diretor',
... 'titulo_original'
... ]
>>> filmes = pd.read_csv('filmes.csv', nrows=1000, usecols=col)
>>> filmes.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1000 entries, 0 to 999
Data columns (total 2 columns):
 #   Column           Non-Null Count  Dtype
---  ------           --------------  -----
 0   titulo_original  1000 non-null   object
 1   diretor          1000 non-null   object
dtypes: object(2)
memory usage: 7.9+ KB

Selecionando linhas e colunas, conseguimos reduzir o tamanho do dataframe. Com isso podemos reduzir o tamanho de qualquer dataframe.

Referência:
Método read_csv()
Método info()

Criando arquivo csv com pandas

Photo by Maksym Kaharlytskyi on Unsplash

Na internet podemos achar vários arquivos csv. Cada um com tipos deferentes de dados. Esses arquivos têm coisas boas e coisas ruins. A coisa boa é que eles são facilmente lidos por nos humanos, podem ser abertos com um editor de texto simples e podem ser acessados pela maioria dos editores de planilhas. O lado negativo dos arquivos csv é a falta de um padrão para o arquivo. Tem arquivos csv que utilizam vírgula como separador, outros usam ponto e vírgula. Não é possível impor o tipo de dado. Esse tipo de coisa pode tornar a criação desse arquivo complicada.
Nesse tutorial vamos aprender como criar um arquivo csv a partir de um dataframe pandas.
Os dataframes pandas têm métodos que começam com "to_". Os métodos que começam assim são métodos de exportação. Usaremos o método to_csv(). Com ele é possível descarregar um dataframe pandas num arquivo csv.

Como criar um arquivo csv com pandas

Para criar um arquivo csv primeiro precisamos de um dataframe. Nesse exemplo vamos criar um dataframe e depois salvar esse dataframe no HD como um arquivo csv.

>>> import pandas as pd
>>> nome = ['Maria', 'João', 'Daniel', 'Paulo']
>>> idade = [36, 38, 24, 66]
>>> dados = {
... 'nome':nome,
... 'idade':idade
... }
>>> pessoas = pd.DataFrame(dados)
>>> type(pessoas)
<class 'pandas.core.frame.DataFrame'>
>>> pessoas
     nome  idade
0   Maria     36
1    João     38
2  Daniel     24
3   Paulo     66

>>> pessoas.to_csv('pessoas.csv')

Com esse código criamos um dataframe com três colunas, as duas que criamos (nome e idade) e a de índice que é adicionada pelo pandas. Depois salvamos esse dataframe no arquivo pessoas.csv.
Agora com o arquivo criado podemos abri-lo com o método read_csv().

>>> pessoas = pd.read_csv('pessoas.csv')
>>> pessoas
   Unnamed: 0    nome  idade
0           0   Maria     36
1           1    João     38
2           2  Daniel     24
3           3   Paulo     66

Podemos notar que quando salvamos o arquivo com o método to_csv() foi adicionado uma coluna de índice na primeira linha, mas sem nome. E quando abrimos esse arquivo com o método read_csv() é adicionado outra coluna de índice no inicio. Assim ficamos com duas colunas de índice, uma com nome e outra sem (unnamed). Para resolvermos esse problema usamos o parâmetro index_col do método read_csv().

>>> pessoas = pd.read_csv('pessoas.csv', index_col=0)
>>> pessoas
     nome  idade
0   Maria     36
1    João     38
2  Daniel     24
3   Paulo     66

Uma alternativa, para resolver o mesmo problema, é fazer uso do parâmetro index do método to_csv(). Se passarmos o valor False o arquivo não terá um índice.

>>> pessoas = pd.DataFrame(dados)
>>> pessoas.to_csv('pessoas.csv', index=False)
>>> pessoas = pd.read_csv('pessoas.csv')
>>> pessoas
     nome  idade
0   Maria     36
1    João     38
2  Daniel     24
3   Paulo     66

Referência:
Método DataFrame()
Método to_csv()
Método read_csv()

Criando dataframes com pandas

Photo by La-Rel Easter on Unsplash

Podemos criar um dataframe de diversas maneiras e utilizar vários programas pra isso. Nesse tutorial vamos aprender a criar um dataframe com a biblioteca pandas e utilizando a linguagem de programação python.
É comum criar um dataframe a partir de um arquivo existente, ou de um banco de dados. Nos exemplos anteriores sempre criamos o dataframe a partir do arquivo filmes.csv. Nesse tutorial vamos criar um dataframe do zero, sem arquivos pré-existentes ou bancos de dados. Vamos criar um dataframe a partir de algumas listas.

Criando um dataframe pandas

Primeiro vamos criar listas com quatro elementos cada. Essas listas vão ser convertidas em colunas do dataframe, por isso é importante que os elementos das listas sejam homogêneos.

>>> import pandas as pd
>>> nome = ['Bruna', 'Heverton', 'Gelvani', 'Milena']
>>> u_nome = ['Oliveira', 'Ferreira', 'Silva', 'Reis']
>>> nascimento = [1989, 1997, 1988, 1999]

Depois vamos criar um dicionário com os nomes das colunas do dataframe. As chaves (keys) do dicionário serão os nomes das colunas no dataframe.

>>> pessoas = {'nome':nome, 'u_nome':u_nome, 'nascimento':nascimento}

Por fim, criamos o dataframe passando o dicionário pessoa para o método DataFrame().

>>> type(m_dataframe)
<class 'pandas.core.frame.DataFrame>
>>> m_dataframe
       nome    u_nome  nascimento
0     Bruna  Oliveira        1989
1  Heverton  Ferreira        1997
2   Gelvani     Silva        1988
3    Milena      Reis        1999

Nesse exemplo, os elementos das listas foram convertidos em elementos das colunas do nosso dataframe. E as chaves do dicionário pessoas dão nome às colunas do dataframe.
O padrão, quando criamos um dataframe, é o index ser numerado começando do número zero. Podemos mudar isso com o parâmetro index.

>>> m_dataframe = pd.DataFrame(pessoas, index=['a', 'b', 'c', 'd'])
>>> m_dataframe
       nome    u_nome  nascimento
a     Bruna  Oliveira        1989
b  Heverton  Ferreira        1997
c   Gelvani     Silva        1988
d    Milena      Reis        1999

Criando dataframes pandas com dicionários

Também podemos criar um dataframe a partir de uma lista de dicionários.

>>> pd.DataFrame(
... [
... {
... "nome": "Bruna",
... "u_nome": "Oliveira",
... "nascimento": 1989,
... },
... {
... "nome": "Heverton",
... "u_nome": "Ferreira",
... "nascimento": 1997,
... },
... {
... "nome": "Gelvani",
... "u_nome": "Silva",
... "nascimento": 1988,
... },
... {
... "nome": "Milena",
... "u_nome": "Reis",
... "nascimento": 1999,
... },
... ]
... )
       nome    u_nome  nascimento
0     Bruna  Oliveira        1989
1  Heverton  Ferreira        1997
2   Gelvani     Silva        1988
3    Milena      Reis        1999

Nesse exemplo, a ordem das colunas é a mesma das chaves do dicionário. Podemos mudar essa ordem com o parâmetro columns.

>>> pd.DataFrame(
... [
... {
... "nome": "Bruna",
... "u_nome": "Oliveira",
... "nascimento": 1989,
... },
... {
... "nome": "Heverton",
... "u_nome": "Ferreira",
... "nascimento": 1997,
... },
... {
... "nome": "Gelvani",
... "u_nome": "Silva",
... "nascimento": 1988,
... },
... {
... "nome": "Milena",
... "u_nome": "Reis",
... "nascimento": 1999,
... },
... ],
... columns=['nascimento', 'u_nome', 'nome']
... )
   nascimento    u_nome      nome
0        1989  Oliveira     Bruna
1        1997  Ferreira  Heverton
2        1988     Silva   Gelvani
3        1999      Reis    Milena

Mudamos a ordem das colunas passando uma lista com os nomes. A ordem das colunas segue a ordem da lista passada para o parâmetro columns.

Referência:
Método DataFrame()

Inverter a direção de uma operação

Photo by Brendan Church on Unsplash

É comum que alguns métodos dos dataframes pandas tenham o parâmetro eixo. Com esse parâmetro é possível alterar o sentido da operação. Podemos definir o sentido em que a operação deve ocorrer. Os valores passados ao parâmetro podem se 'index' (ou 0) ou 'columns' (ou 1). É muito comum ver o parâmetro de eixo recebendo valores em strings. Isso acontece porque é mais intuitivo. É mais fácil de perceber que a string 'columns' se refere a colunas do que perceber que o número um se refere à mesma coisa.
O valor padrão, da grande maioria dos parâmetros eixo, é o zero (ou 'index'). Por isso o padrão das operações é ir da primeira linha até a última. Nesse tutorial vamos aprender como mudar a direção das operações.

Como inverter a direção de uma operação no dataframe pandas

Vamos começar com um método que já vimos antes, o count(). Esse método conta os elementos não vazios. Por padrão o sentida da operação é o eixo 'index''. Para mudar o sentido da operação usamos o parâmetro axis (eixo).

>>> import pandas as pd
>>> filmes = pd.read_csv('filmes.csv')
>>> filmes.count()
indice                     9262
codigo_obra                9259
titulo_original            9262
titulo_brasil              9262
ano_producao               9259
diretor                    9262
razao_social_requerente    9262
cnpj_requerente            9262
data_exibicao              9262
dtype: int64

>>> filmes.count(axis='columns')
0       9
1       8
2       9
3       8
4       9
       ..
9257    9
9258    9
9259    9
9260    9
9261    9
Length: 9262, dtype: int64

Na primeira chamada, ao método count(), recebemos uma série com o número de elementos não vazios de cada coluna. Isso acontece porque o eixo padrão do método count() é o index. Assim o método count() vai em cada coluna e conta quantos elementos essa coluna tem. Na segunda chamada mudamos o eixo para ‘colunas’. Desse modo o método vai a cada linha e conta quantas colunas não vazias essa linha tem.

Referência:
Método count()
Método read_csv()

quarta-feira, 5 de agosto de 2020

Comparando valores ausentes no dataframe pandas

Photo by Ayesh Rathnayake on Unsplash

Os valores ausentes num dataframe pandas são representados com um objeto da biblioteca NumPy NaN (np.nan). Esse objeto é bastante curioso. Por exemplo, o objeto não é igual a ele mesmo. Para comparação o objeto None é igual a ele mesmo, mas não um objeto np.nan. No exemplo abaixo vamos fazer um pequeno teste e comparar os dois objetos com eles mesmos. O objeto np.nan vai retornar False e o None vai retornar True.

>>> import pandas as pd
>>> import numpy as np
>>> np.nan == np.nan
False

>>> None == None
True

Todos os operadores de comparação vão retornar False com exceção de um: o de diferente.

>>> np.nan > np.nan
False
>>> np.nan < np.nan
False
>>> np.nan != np.nan
True

Com esses exemplos percebemos que o objeto np.nan é um objeto singular. Essa propriedade é muito utilizada na biblioteca pandas.
Para verificar se dataframes e séries pandas são iguais é utilizado o operador ==. Esse operador realiza a operação em cada elemento do dataframe ou série. Como resultado dessa operação temos um objeto com as mesmas dimensões dos objetos pais. Nesse tutorial vamos utilizar o operador de igual (==). Muita gente acha que o operador igual e o método equals() fazem a mesma coisa, mas o operador faz coisa diferentes do método equals().

Como comparar valores ausentes do dataframe pandas

Vamos começar com um exemplo para termos uma ideia de como o operador igual funciona. Vamos comparar cada elemento do dataframe com um valor.

>>> import pandas as pd
>>> filmes = pd.read_csv(‘filmes.csv’)
>>> cod_obra = filmes.select_dtypes(include='number')
>>> cod_obra == 7603
      indice  codigo_obra  ano_producao
0      False        False         False
1      False         True         False
2      False        False         False
3      False        False         False
4      False        False         False
...      ...          ...           ...
9257   False        False         False
9258   False        False         False
9259   False        False         False
9260   False        False         False
9261   False        False         False

[9262 rows x 3 columns]

Nesse exemplo, apenas um elemento tem o valor igual a 7603. Por isso apenas uma operação retornou True. É assim que o operador igual funciona, realiza uma operação para cada elemento do dataframe.
Nota: para os próximos exemplos vamos precisar de um dataframe que tenha valores ausentes. Por isso abra o dataset filmes.csv, num editor de texto, e apague os primeiros valores da coluna codigo_obra (é só apagar o valor entre as vírgulas).
Para o próximo exemplo vamos comparar o dataframe com ele mesmo. O esperado é que todos os resultados sejam True, mas não é bem isso que acontece.

>>> filmes == filmes
      indice  codigo_obra  ...  cnpj_requerente  data_exibicao
0       True         True  ...             True           True
1       True        False  ...             True           True
2       True         True  ...             True           True
3       True        False  ...             True           True
4       True         True  ...             True           True
...      ...          ...  ...              ...            ...
9257    True         True  ...             True           True
9258    True         True  ...             True           True
9259    True         True  ...             True           True
9260    True         True  ...             True           True
9261    True         True  ...             True           True

[9262 rows x 9 columns]

Podemos ver que, contrariando as nossas expectativas, recebemos alguns valores False. Isso acontece por causa dos objetos np.nan da biblioteca NumPy. Como vimos acima np.nan é diferente de np.nan. Como a biblioteca pandas faz uso desse objeto, para representar objetos vazios, qualquer dataframe que tenha um elemento vazio será diferente dele mesmo. E como saber se um dataframe é igual a outro? Com o método equals().

>>> filmes.equals(filmes)
True

O método equals() retorna True se os dataframes forem iguais.
Esse é o jeito certo de verificar se dois dataframes são iguais. Utilizando o operador igual, se tiver algum elemento vazio, eles não serão iguais. Com o método equals() isso não acontece.

Operações com dataframes pandas

Imagem de Steve Buissinne por Pixabay

Quando usamos um operador de comparação ou aritmético num dataframe pandas, a operação é feita com todos os elementos. A grande maioria das operações sobre os dataframe envolvem colunas numéricas, mas é comum encontramos com strings. Um cuidado que deve ser tomado é quanto ao tipo dos dados, se os dados não forem homogêneos a operação vai falhar. No exemplo abaixo vamos ver esse erro com o dataset filmes. Nesse dataset os dados não são homogêneos, existem dados do tipo int64, float64 e object. Por isso quando tentamos adicionar o valor inteiro 5, geramos um erro. Porque um número inteiro não pode ser adicionado a uma string, sem antes converter o número para uma string.

>>> import pandas as pd
>>> filmes = pd.read_csv('filmes.csv')
>>> filmes + 5
Traceback (most recent call last):
  ...
TypeError: can only concatenate str (not "int") to str

Uma condição para utilizar com êxito um operador com um dataframe é selecionar dados homogêneos. Para esse tutorial vamos selecionar as colunas do tipo inteiro usando o método filter().

>>> import pandas as pd
>>> filmes = pd.read_csv('filmes.csv')
>>> filmes_int = filmes.filter(items=['indice', 'codigo_obra'])
>>> filmes_int.head()
   indice  codigo_obra
0       0        15639
1       1         7603
2       2        26453
3       3        17284
4       4         4806

O parâmetro like, do método filter(), é muito utilizado para selecionar coluna, isso porque não precisamos passar o nome exato da coluna para o parâmetro. Utilizando esse parâmetro podemos selecionar várias colunas com uma sequencia de strings. Por exemplo, nesse dataset existem duas colunas que tem a string 'titulo' no seu nome. Para selecionar essas duas colunas é só passar a string 'titulo' para o parâmetro like. Veja um exemplo:

>>> filmes.filter(like='titulo')
                  titulo_original                titulo_brasil
0                  PREÇO DA PAZ O               PREÇO DA PAZ O
1                     CARTOMANTEA                  CARTOMANTEA
2            BLACK & WHITE VOL. 9         BLACK & WHITE VOL. 9
3                        THE GURU               O GURU DO SEXO
4                  QUIET AMERICAN        O AMERICANO TRANQUILO
...                           ...                          ...
9257                 ROCK OF AGES        ROCK OF AGES: O FILME
9258                   THE MATRIX                       MATRIX
9259               MILITARY WIVES               MILITARY WIVES
9260  ROBERTO CARLOS EM JERUSALÉM  ROBERTO CARLOS EM JERUSALÉM
9261                       O POÇO                       O POÇO

[9262 rows x 2 columns]

Como fazer operações com dataframes pandas

Agora que já selecionamos os dados de forma homogênea, realizar operações é fácil como somar um mais um. Veja o exemplo:

>>> filmes_int * 2
      indice  codigo_obra
0          0        31278
1          2        15206
2          4        52906
3          6        34568
4          8         9612
...      ...          ...
9257   18514     32003588
9258   18516     30001932
9259   18518     38005368
9260   18520      1219524
9261   18522     38010274

Nesse exemplo cada elemento do dataframe filmes_int foi multiplicado por dois. Podemos realizar todas as principais operações com um dataframe pandas, como soma, divisão, etc. No exemplo acima todos os elementos foram multiplicados por dois, mas se quiséssemos que cada elemento fosse somado com um valor diferente? Utilizaríamos outro dataframe.

>>> filmes_int + filmes.filter(items=['indice', 'codigo_obra'])
      indice  codigo_obra
0          0        31278
1          2        15206
2          4        52906
3          6        34568
4          8         9612
...      ...          ...
9257   18514     32003588
9258   18516     30001932
9259   18518     38005368
9260   18520      1219524
9261   18522     38010274

[9262 rows x 2 columns]

Nesse exemplo cada elemento do dataframe filmes_int é somado com o elemento correspondente no dataframe gerado pelo método filter(). Assim linha é somada com linha e coluna com coluna.