quinta-feira, 6 de agosto de 2020

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.

terça-feira, 4 de agosto de 2020

Encadeando métodos DataFrame Pandas

Imagem de Gerd Altmann por Pixabay

Nos tutoriais anteriores aprendemos como encadear métodos com séries. Nesse tutorial vamos aprender a encadear métodos a partir de um dataframe. A coisa mais importante quando queremos encadear métodos é saber qual vai ser a saída de cada método. Assim podemos chamar um método adequado para tratar a saída de outro método.
Nesse tutorial vamos contar todos os valores que estão faltando em cada coluna do dataframe filmes.

Como encadear métodos de um dataframe Pandas

Para o nosso exemplo vamos criar um dataframe com valores True e False. Os valores True serão utilizados para representar os valores ausentes do dataframe pai, e o valor False será usado para representar os valores presentes. Vamos fazer isso usando o método isnull(). Depois vamos encadear o método head(), que criar um dataframe com as cinco primeiras linhas do dataframe (nesse caso o retornado pelo método isnull()).

>>> import pandas as pd
>>> filmes = pd.read_csv('filmes.csv')
>>> filmes.isnull().head()
   indice  codigo_obra  ...  cnpj_requerente  data_exibicao
0   False        False  ...            False          False
1   False        False  ...            False          False
2   False        False  ...            False          False
3   False        False  ...            False          False
4   False        False  ...            False          False

[5 rows x 9 columns]

Todos os métodos da biblioteca Pandas podem ser encadeados. Isso por que cada método ou atributo retorna um dataframe ou uma série. E cada objeto possui métodos e atributos que retornam outros dataframes ou séries.

Resumindo um dataframe pandas

Photo by Brett Jordan on Unsplash

Nos exemplos dos tutoriais anteriores vimos como chamar alguns métodos das séries. Nesses exemplos trabalhamos apenas com colunas ou séries. Esses métodos eram de agregação ou redução que retornavam séries. Chamando esses mesmos métodos a partir de um dataframe as operações serão executadas em todas as colunas e não apenas em uma, reduzindo os resultados para cada coluna do dataframe. Muitos desses métodos retornam uma série com os nomes das colunas e os valores correspondentes.
Nesse tutorial, vamos ver mais alguns dos atributos e métodos que podemos utilizar com os dataframe Pandas. Vamos utilizar o dataframe filmes.csv, que já usamos nos post anteriores.

Como resumir um dataframe

Vamos abrir o dataframe filmes e chamar os atributos shape, size e ndim. E depois vamos utilizar a função interna do python len(). O atributo shape retorna uma tupla com os dois valores, o primeiro é o número de linhas do dataframe e o segundo é o número de colunas. O atributo size retorna o numero de elementos do dataframe. O ndim retorna o número de dimensões do dataframe. E a função len retorna o número de linhas do dataframe Pandas.

>>> import pandas as pd
>>> filmes = pd.read_csv('filmes.csv')
>>> filmes.shape
(9262, 9)
>>> filmes.size
83358
>>> filmes.ndim
2
>>> len(filmes)
9262

Nos dataframe é comum estar faltando alguns elementos, se você quiser saber a quantidade de, apenas, elementos presentes use o método count(). Esse método retorna uma série com os rótulos das colunas e a quantidade de elementos com valores, excluindo da contagem os elementos vazios.

>>> filmes.count()
indice                     9262
codigo_obra                9262
titulo_original            9262
titulo_brasil              9262
ano_producao               9259
diretor                    9262
razao_social_requerente    9262
cnpj_requerente            9262
data_exibicao              9262
dtype: int64

Nesse exemplo os números retornados são iguais ao número de linhas, isso porque no dataframe filmes.csv não existe elementos vazios.
Se você precisar de estatísticas básicas como o valor mínimo, máximo, média e mediana, a biblioteca Pandas disponibiliza métodos que fornecem essas estatísticas. Veja um exemplo abaixo:

>>> filmes.min()
indice                                                   0
codigo_obra                                              1
titulo_original                         A PRIMEIRA MISSA""
titulo_brasil                           A PRIMEIRA MISSA""
ano_producao                                           200
diretor                                   \t PABLO TRAPERO
razao_social_requerente    13 PRODUÇÕES E LOCAÇÕES LTDA-ME
cnpj_requerente                         00.020.648/0001-20
data_exibicao                                   01/02/2005
dtype: object

>>> filmes.max()
indice                                             9261
codigo_obra                                    20002501
titulo_original            ÚLTIMO TANGO EM BUENOS AIRES
titulo_brasil                   ÚLTIMOS DIAS NO DESERTO
ano_producao                                       2020
diretor                                      ÉRIC WARIN
razao_social_requerente       ÍRIS CINEMATOGRÁFICA LTDA
cnpj_requerente                      97.533.170/0001-73
data_exibicao                                 31/mar/17
dtype: object

# média
>>> filmes.mean()
indice          4.630500e+03
codigo_obra     1.053494e+07
ano_producao    2.009945e+03
dtype: float64

# mediana
>>> filmes.median()
indice              4630.5
codigo_obra     15004498.0
ano_producao        2011.0
dtype: float64

Você pode obter essas estatísticas chamando um único método: o describe().

>>> filmes.describe()
           indice   codigo_obra  ano_producao
count  9262.00000  9.262000e+03   9259.000000
mean   4630.50000  1.053494e+07   2009.944918
std    2673.85343  7.714715e+06     20.439854
min       0.00000  1.000000e+00    200.000000
25%    2315.25000  4.165462e+05   2006.000000
50%    4630.50000  1.500450e+07   2011.000000
75%    6945.75000  1.600608e+07   2015.000000
max    9261.00000  2.000250e+07   2020.000000

Uma coisa que afeto as estatísticas são os elementos vazios. Se você quer que esses elementos sejam ignorados utilize o parâmetro skipna passando o valor True. Se passarmos o valor False, os elementos vazios vão entrar no calculo. Veja um exemplo:

>>> filmes.min(skipna=False)
indice                                                   0
codigo_obra                                              1
titulo_original                         A PRIMEIRA MISSA""
titulo_brasil                           A PRIMEIRA MISSA""
ano_producao                                           200
diretor                                   \t PABLO TRAPERO
razao_social_requerente    13 PRODUÇÕES E LOCAÇÕES LTDA-ME
cnpj_requerente                         00.020.648/0001-20
data_exibicao                                   01/02/2005
dtype: object

Todos os métodos de estatísticas têm o parâmetro skipna. O valor padrão é True.