domingo, 26 de julho de 2020

Encadeando métodos do Dataframe Pandas

Já aprendemos como encadear métodos de uma séries à alguns tutoriais atrás. Nesses tutoriais aprendemos como os encadeamentos de métodos e atributos funcionam numa série. Nesse tutorial vamos aprender como encadear métodos a partir de um dataframe. O segredo para encadear os métodos é saber qual vai ser a saída de cada método e atributo. Com a biblioteca Pandas os valores retornados, quase sempre, será uma série ou um dataframe.
Nesse post vamos aprender como encadear métodos de um dataframe.

Como chamar métodos em sequência com a biblioteca Pandas

No primeiro exemplo vamos chamar o método isnull() e em seguida chamar o método head(). O método isnull() retorna um dataframe igual ao dataframe antecessor, substituindo os valores com True ou False. Se o elemento tinha um valor, o novo valor será False e True caso não tivesse. Já o método head() retorna um dataframe com cinco linhas.

>>> 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]

Nesse exemplo o método isnull() retornou um dataframe, e a partir desse dataframe chamamos o método head(). É importante notar que a cada método chamado um novo dataframe é criado com suporte a todos os métodos disponíveis, e é isso que torna o encadeamento possível. Já que cada método retorna um novo dataframe.

Métodos e atributos dos dataframes Pandas

Nos tutoriais anteriores aprendemos como chamar alguns dos métodos mais utilizados quando trabalhamos com séries da biblioteca Pandas. Os exemplos que usamos foram com apenas uma coluna ou série. Uma parte desses métodos eram de redução ou agregação que retornavam outras séries. Esses mesmo métodos podem ser chamados a partir de um dataframe assim como são chamados a partir de uma série. A diferença é que quando chamamos esses método e atributos a partir de uma série a operação é aplicada a uma coluna apenas. Quando chamamos os métodos e atributos a partir de um dataframe a operação é aplicada para cada coluna do dataframe.
Nesse tutorial vamos aprender a utilizar alguns dos métodos e atributos mais utilizados com dataframes.

Como obter informações básicas de um dataset

Primeiro vamos ler os dados de uma dataset, depois vamos ver algumas informações básicas desse dataset. As informações que estamos procurando são a forma do dataset, o tamanho, o número de dimensões e o seu tamanho. Essas informações podem ser obtidas com os atributos: shape, size, ndim e com a função len().

>>> import pandas as pd
>>> filmes = pd.read_csv('filmes.csv')
>>> filmes.shape
(9262, 9) # 9262 linhas e 9 colunas

>>> filmes.size
83358 # número de elementos do dataframe

>>> filmes.ndim
2 # número de dimensões, duas: colunas e linhas

>>> len(filmes)
9262 # número de linhas

Em um dataframe é possível, e até comum, que estejam faltando valores em alguns elementos do dataframe. E se você precisar saber quantos elementos tem valores guardados? A resposta é simples: o método count(). Esse método retorna uma série com os nomes das colunas e a quantidade de cada elemento dessa coluna que possuí um valor. O método count() é um exemplo de um método de agregação já que ele resumi os valores em um só.

>>> 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

Uma coisa comum ao trabalhar com a biblioteca Pandas é saber o valor mínimo, máximo, média e mediana de um dataframe. Para obter essas informações podemos usar os métodos min(), max(), mean() e median(). Esses métodos retornam uma séries os com valores correspondentes.

>>> 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

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

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

Se você quiser as informações do exemplo acima com um único método, podemos chamar o método describe(). Esse método vai te fornecer todas as informações do exemplo acima utilizando um único método.

>>> 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

Nos métodos acima, podemos usar o parâmetro skipna. Com esse parâmetro definimos se os elementos sem valor devem se incluídos nas estatísticas básicas.

>>> filmes.max(skipna=False)
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

Se passamos o valor False para o parâmetro skipna os elementos sem valor serão adicionados na estatística. O valor padrão desse parâmetro é True.

sábado, 25 de julho de 2020

Ordenando colunas do dataframe com Pandas

Uma coisa que vinhamos negligenciando, e uma das primeiras tarefas que devemos fazer quando importamos um dataframe, é analisar a ordem das colunas. No nosso dia-a-dia lemos da esquerda para a direita. E esse comportamento pode afetar como lemos os dados de um dataframe. Sem contar que é mais fácil encontrar um dado quando ele esta armazenado de forma organizada.
É aqui que começa nosso trabalho. Não tem um conjunto de regras que devemos seguir para organizar os dados. Uma boa prática é você mesmo criar um conjunto de regras e seguir sempre essas regras nos seus projetos. Isso se torna mais necessário quando o trabalho é em equipe e existem vários analistas gerando e trocando dados entre se.
Essas são algumas dicas para quando você importar um dataframe:

  • Classifique cada coluna seguindo um critério.
  • Agrupe as colunas mais comuns.
  • Coloque os grupos mais importantes nas primeiras colunas.

Nesse tutorial vamos ordenar as colunas fazendo uso dessas dicas, mas você pode adicionar outras dicas a essas ou não usar nenhuma.

Como ordenar colunas de um dataframe com Pandas

Para ordenar as colunas vamos utilizar o operador de índice.

>>> import pandas as pd
>>> filmes = pd.read_csv('filmes.csv')
>>> filmes.columns
Index(['indice', 'codigo_obra', 'titulo_original', 'titulo_brasil',
       'ano_producao', 'diretor', 'razao_social_requerente', 'cnpj_requerente',
       'data_exibicao'],
      dtype='object')
>>> nova_ordem = [
... 'codigo_obra',
... 'titulo_brasil',
... 'titulo_original',
... 'data_exibicao',
... 'diretor',
... 'ano_producao',
... 'indice'
... ]
>>> filmes = filmes[nova_ordem]
>>> filmes
      codigo_obra                titulo_brasil  ... ano_producao indice
0           15639               PREÇO DA PAZ O  ...       2003.0      0
1            7603                  CARTOMANTEA  ...       2002.0      1
2           26453         BLACK & WHITE VOL. 9  ...       2005.0      2
3           17284               O GURU DO SEXO  ...       2002.0      3
4            4806        O AMERICANO TRANQUILO  ...       2002.0      4
...           ...                          ...  ...          ...    ...
9257     16001794        ROCK OF AGES: O FILME  ...       2012.0   9257
9258     15000966                       MATRIX  ...       1999.0   9258
9259     19002684               MILITARY WIVES  ...       2019.0   9259
9260       609762  ROBERTO CARLOS EM JERUSALÉM  ...       2011.0   9260
9261     19005137                       O POÇO  ...       2019.0   9261

[9262 rows x 7 columns]

Você pode ordenar colunas utilizando outros métodos além de utilizar o operador do índice. Outro método que pode ser utilizado é o filter(), que já vimos o seu funcionamento no tutorial passado.
Uma dica importante para ordenaras colunas é colocar os dados fixos primeiro.

sexta-feira, 24 de julho de 2020

Selecionando colunas com métodos do Pandas

A maioria dos códigos que utilizam a biblioteca Pandas ao selecionar uma coluna fazem uso do operador de índice. Em contrapartida é interessante saber que a biblioteca disponibiliza métodos para a mesma tarefa. Os métodos mais utilizados são select_dtypes() e filter().
Esses métodos devem ser utilizados para demandas diferentes. Se você deseja selecionar colunas pelo seu tipo, o método ideal é o select_dtypes() e se precisa selecionar pelo nome o método é o filter(). Nesse tutorial vamos aprender como utilizar esses métodos.

Como selecionar colunas com métodos da biblioteca Pandas

Chamando o método select_dtypes(), passando para o parâmetro include o valor 'O', sinalizamos para o método que queremos selecionar todas as colunas do tipo object.

>>> import pandas as pd
>>> filmes = pd.read_csv('filmes.csv')
>>> filmes.select_dtypes(include='O')
                  titulo_original  ... data_exibicao
0                  PREÇO DA PAZ O  ...     06/jul/12
1                     CARTOMANTEA  ...     13/jul/04
2            BLACK & WHITE VOL. 9  ...    26/09/2007
3                        THE GURU  ...    16/12/2002
4                  QUIET AMERICAN  ...     15/jan/03
...                           ...  ...           ...
9257                 ROCK OF AGES  ...     09/jul/20
9258                   THE MATRIX  ...     25/nov/19
9259               MILITARY WIVES  ...    14/02/2020
9260  ROBERTO CARLOS EM JERUSALÉM  ...    20/12/2019
9261                       O POÇO  ...    18/10/2019

[9262 rows x 6 columns]

Se você quiser seleciona todas as colunas com números, podemos passar o valor 'number' para o parâmetro include.

>>> filmes.select_dtypes(include='number')
      indice  codigo_obra  ano_producao
0          0        15639        2003.0
1          1         7603        2002.0
2          2        26453        2005.0
3          3        17284        2002.0
4          4         4806        2002.0
...      ...          ...           ...
9257    9257     16001794        2012.0
9258    9258     15000966        1999.0
9259    9259     19002684        2019.0
9260    9260       609762        2011.0
9261    9261     19005137        2019.0

[9262 rows x 3 columns]

É possível selecionar mais de um tipo de dados de uma vez. Se quisermos selecionar colunas com números inteiros e strings, passamos uma lista para o parâmetro include:

>>> filmes.select_dtypes(include=['int64','object'])
      indice  codigo_obra  ...     cnpj_requerente data_exibicao
0          0        15639  ...  00.568.159/0001-07     06/jul/12
1          1         7603  ...  03.599.148/0001-82     13/jul/04
2          2        26453  ...  02.341.697/0001-90    26/09/2007
3          3        17284  ...  27.654.722/0001-16    16/12/2002
4          4         4806  ...  00.979.601/0001-98     15/jan/03
...      ...          ...  ...                 ...           ...
9257    9257     16001794  ...  33.015.827/0001-28     09/jul/20
9258    9258     15000966  ...  33.015.827/0001-28     25/nov/19
9259    9259     19002684  ...  02.668.665/0001-01    14/02/2020
9260    9260       609762  ...  15.372.472/0001-42    20/12/2019
9261    9261     19005137  ...      408.504.318-83    18/10/2019

[9262 rows x 8 columns]

Obs: 'O' e 'object' se referem a strings.
E se quiséssemos selecionar todas as colunas e deixar de lado apenas um tipo de dado? Nesse caso usaríamos o parâmetro exclude. A sua utilização é bem simples, passamos apenas o tipo de dados que não queremos. Desse modo o método retornará todas as colunas, com exceção das colunas com o tipo de dado passado para o parâmetro exclude. Esse método também aceita uma lista com os tipos de dados que não queremos selecionar.

>>> filmes.select_dtypes(exclude=['int64', 'float64'])
                  titulo_original  ... data_exibicao
0                  PREÇO DA PAZ O  ...     06/jul/12
1                     CARTOMANTEA  ...     13/jul/04
2            BLACK & WHITE VOL. 9  ...    26/09/2007
3                        THE GURU  ...    16/12/2002
4                  QUIET AMERICAN  ...     15/jan/03
...                           ...  ...           ...
9257                 ROCK OF AGES  ...     09/jul/20
9258                   THE MATRIX  ...     25/nov/19
9259               MILITARY WIVES  ...    14/02/2020
9260  ROBERTO CARLOS EM JERUSALÉM  ...    20/12/2019
9261                       O POÇO  ...    18/10/2019

[9262 rows x 6 columns]

Outro método utilizado para selecionar colunas é o método filter(). Esse método retorna colunas que os nomes contenham a string passada ao parâmetro like. Assim a string não precisa ser exata ao nome da coluna, basta que o rótulo tenha a string no começo, no meio ou no final. E se várias colunas tiverem a string, o método retornará todas elas.

>>> import pandas as pd
>>> filmes = pd.read_csv('filmes.csv')
>>> filmes.filter(like='cnpj')
         cnpj_requerente
0     00.568.159/0001-07
1     03.599.148/0001-82
2     02.341.697/0001-90
3     27.654.722/0001-16
4     00.979.601/0001-98
...                  ...
9257  33.015.827/0001-28
9258  33.015.827/0001-28
9259  02.668.665/0001-01
9260  15.372.472/0001-42
9261      408.504.318-83

[9262 rows x 1 columns]

Nesse exemplo, não foi usado o nome exato da coluna, apenas uma parte (cnpj), e mesmo assim a coluna cnpj_requerente foi selecionada. No dataframe filmes, só existe uma coluna que tem a string cnpj, por isso o método retornou apenas uma coluna. Outro exemplo é passar a string 'titulo' para o parâmetro like. Como o dataframe possui duas colunas com a string 'titulo' (titulo_brasil e titulo_oroginal), o método filter() retornará duas colunas.

>>> 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]

Outro parâmetro interessante do método filter() é o items. Com esse parâmetro podemos passar o nome das colunas que queremos. O parâmetro aceita uma lista com os valores.

>>> colunas = [
... 'diretor',
... 'indice'
... ]
>>> filmes.filter(items=colunas)
                                 diretor  indice
0     PAULO DE TARSO DE CARVALHO MORELLI       0
1         WAGNER DE ASSIS E PABLO URANGA       1
2              LENILDO MAURICIO DA SILVA       2
3                            DAISY MAYER       3
4                          PHILLIP NOYCE       4
...                                  ...     ...
9257                       ADAM SHANKMAN    9257
9258       LANA WACHOWSKI ANDY WACHOWSKI    9258
9259                      PETER CATTANEO    9259
9260    MARIO HUMBERTO MEIRELLES MOREIRA    9260
9261               ANDRE BORELLI MARTINS    9261

[9262 rows x 2 columns]

Selecionando múltiplas colunas de um dataframe com Pandas

Para selecionar uma única coluna passamos o nome da coluna como um índice do dataframe. Já vimos exemplos de como fazer isso nos tutoriais passados. Quando estamos trabalhando com um dataframe é possível que nem todos os dados que estejam ali nos interessem. Podemos resolver isso selecionando as colunas que vamos utilizar.
Nesse post vamos aprender a como selecionar duas colunas do dataframe filmes.csv.

Como selecionar duas colunas de um dataframe

Primeiro vamos importar o dataframe para uma variável. E depois criar uma lista com os nomes das colunas que pretendemos trabalhar:

>>> import pandas as pd
>>> filmes = pd.read_csv('filmes.csv')
>>> titulos = filmes[
... [
... 'titulo_original',
... 'titulo_brasil'
... ]
... ]
>>> titulos.head()
        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

Em algumas situações é preciso selecionar colunas de um dataframe. Utilizando índice o resultado pode ser uma série ou um dataframe. Quando passamos, como índice, uma lista com um único item, o resultado será um dataframe. E passando uma string o resultado será uma série.

>>> type(filmes[['titulo_original']])
<class 'pandas.core.frame.DataFrame'>

>>> type(filmes['titulo_original'])
<class 'pandas.core.series.Series'>

Uma alternativa ao índice, para selecionar colunas, é o atributo loc. Com esse atributo precisamos selecionar as linhas e passar o nome da coluna que queremos selecionar. Usaremos o seletor de dois pontos para selecionar todas as linhas. Se quisermos um dataframe passamos o nome como uma lista e para série passamos uma string:

>>> type(filmes.loc[:, ['titulo_brasil']])
<class 'pandas.core.frame.DataFrame'>

>>> type(filmes.loc[:, 'titulo_brasil'])
<class 'pandas.core.series.Series'>

Como funciona…

Utilizando o operador de índice temos bastante flexibilidade. Com ele podemos utilizar diferentes objetos. Quando passamos uma lista é retornado um dataframe com todas as colunas passadas na lista. Uma coisa interessante é que o novo dataframe respeita a ordem da lista, ou seja, se queremos mudar a posição de uma coluna podemos fazer isso trocando a ordem dos nomes na lista. Se passarmos uma string recebemos uma série.
No Segundo exemplo, vimos como podemos criar um dataframe com uma única coluna a partir de um dataframe. O mesmo acontece com a série.
No terceiro exemplo, aprendemos como criar uma série e um dataframe com o atributo loc.

Tem mais...

Um problema que utilizar o operador de índice pode causar é o de legibilidade. Isso acontece quando passamos uma lista muito longa. Para resolver esse problema podemos criar uma variável do tipo lista:

>>> colunas = [
... 'titulo_brasil',
... 'titulo_original'
... ]
>>> titulos = filmes[colunas]
>>> titulos
                    titulo_brasil              titulo_original
0                  PREÇO DA PAZ O               PREÇO DA PAZ O
1                     CARTOMANTEA                  CARTOMANTEA
2            BLACK & WHITE VOL. 9         BLACK & WHITE VOL. 9
3                  O GURU DO SEXO                     THE GURU
4           O AMERICANO TRANQUILO               QUIET AMERICAN
...                           ...                          ...
9257        ROCK OF AGES: O FILME                 ROCK OF AGES
9258                       MATRIX                   THE 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]
>>> filmes.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 9262 entries, 0 to 9261
Data columns (total 9 columns):
 #   Column                   Non-Null Count  Dtype
---  ------                   --------------  -----
 0   indice                   9262 non-null   int64
 1   codigo_obra              9262 non-null   int64
 2   titulo_original          9262 non-null   object
 3   titulo_brasil            9262 non-null   object
 4   ano_producao             9259 non-null   float64
 5   diretor                  9262 non-null   object
 6   razao_social_requerente  9262 non-null   object
 7   cnpj_requerente          9262 non-null   object
 8   data_exibicao            9262 non-null   object
dtypes: float64(1), int64(2), object(6)
memory usage: 434.2+ KB

Com o exemplo acima podemos perceber que a ordem das colunas foi alterada. A causa disso é a ordem em que os nomes das colunas foram colocados na lista.

quinta-feira, 23 de julho de 2020

Criando e deletando colunas no Pandas

Quando trabalhamos com dados em algum momento vamos precisar criar uma coluna para adicionar novos dados. Na grande maioria dos casos essa nova coluna será criada num dataframe já existente. Com a biblioteca Pandas podemos criar colunas de maneiras diferentes.
Nesse post, vamos criar novas colunas com o método assign() e deletar colunas, que não vamos precisar, com o método drop().

Como criar colunas com Pandas

Para criar uma nova coluna vamos atribuir um valor a um índice. É importante lembra que não vamos criar um dataframe novo, mas sim modificando o atual. Quando atribuir um valor a coluna, o valor passado será usado para todos os elementos da coluna. Vamos criar uma coluna indicando se já vimos o filme. Por padrão as novas colunas são adicionadas no final.

>>> import pandas as pd
>>> filmes = pd.read_csv('filmes.csv')
>>> filmes['assistido'] = 0

Mesmo o exemplo acima funcionando e sendo bastante comum, ainda podemos usar o método assign(). Usando esse método um novo dataframe será criado com uma coluna a mais. O método usa o nome do parâmetro como o novo nome da coluna, então o novo nome deve ser um nome valido para um parâmetro.

>>> filmes = filmes.drop(columns='assistido')
>>> filmes.assign(assistido = 0)
      indice  codigo_obra  ... data_exibicao assistido
0          0        15639  ...     06/jul/12         0
1          1         7603  ...     13/jul/04         0
2          2        26453  ...    26/09/2007         0
3          3        17284  ...    16/12/2002         0
4          4         4806  ...     15/jan/03         0
...      ...          ...  ...           ...       ...
9257    9257     16001794  ...     09/jul/20         0
9258    9258     15000966  ...     25/nov/19         0
9259    9259     19002684  ...    14/02/2020         0
9260    9260       609762  ...    20/12/2019         0
9261    9261     19005137  ...    18/10/2019         0

[9262 rows x 10 columns]

Para remover uma coluna vamos usar o método drop(). O método drop() recebe o nome da coluna que queremos excluir do dataframe através do parâmetro columns. Veja um exemplo:

>>> filmes.drop(columns = 'assistido')

Repare que o método drop() cria um novo dataframe, assim como o método assign().
Uma alternativa ao método drop() é com a declaração del.

    del filmes[‘diretor’]

Tem mais...

Se você quiser adicionar uma coluna entre outras já existentes, o método que você deve utilizar é o insert(). Esse método recebe três parâmetros: o primeiro deve ser um inteiro com a posição da nova coluna; o segundo, o nome da nova coluna; e o terceiro os valores da coluna.

>>> filmes.insert(
... loc=6,
... column='nova',
... value=42
... )
>>> filmes.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 9262 entries, 0 to 9261
Data columns (total 11 columns):
 #   Column                   Non-Null Count  Dtype
---  ------                   --------------  -----
 0   indice                   9262 non-null   int64
 1   codigo_obra              9262 non-null   int64
 2   titulo_original          9262 non-null   object
 3   titulo_brasil            9262 non-null   object
 4   ano_producao             9259 non-null   float64
 5   diretor                  9262 non-null   object
 6   nova                     9262 non-null   int64
 7   razao_social_requerente  9262 non-null   object
 8   cnpj_requerente          9262 non-null   object
 9   data_exibicao            9262 non-null   object
 10  assistido                9262 non-null   object
dtypes: float64(1), int64(3), object(7)
memory usage: 542.8+ KB

Nesse exemplo, adicionamos uma coluna depois da coluna ‘diretor’, na posição seis (lembre-se que o índice começa em zero). E adicionamos o valor quarenta e dois para todos os elementos dessa coluna. O parâmetro value aceita números inteiros, séries ou arrays.

quarta-feira, 22 de julho de 2020

Renomeando o nome de uma coluna com Pandas

Quando trabalhamos com dataframe é muito comum a renomeação das colunas. Renomear as coluna é importante, principalmente com idiomas que usam caracteres especiais, para que os nomes das colunas possam ser usados como nomes de atributos. Com isso em mente os nomes não podem começar com números e devem ser alfanuméricos com letras maiúsculas ou minúsculas, o underline também é um caractere válido. Uma boa escolha de nove descreve o conteúdo da coluna e não entra em conflito com os atributos, já existentes, dos dataframe e séries.
Nesse post vamos aprender a renomear os nomes das colunas dos dataframe e séries do Pandas. Com a renomeação dos nomes procuramos deixar o código mais organizado e de fácil compreensão. Uma escolha errada de nome para uma coluna pode dificultar o seu acesso.

Como fazer...

Nesse exemplo vamos renomear os nomes das colunas do dataframe com o método rename(). O método rename() aceita um dicionário com os nomes velhos e novos da coluna.

>>> import pandas as pd
>>> filmes = pd.read_csv('filmes.csv', encoding='latin-1')
>>> n_nomes = {
... 'Codigo da obra':'codigo_obra',
... 'Titulo Original':'titulo_original',
... 'Titulo no Brasil':'titulo_brasil',
... 'Ano de producão':'ano_producao',
... 'Diretor':'diretor',
... 'Razão Social do Requerente':'razao_social_requerente',
... 'CNPJ Requerente':'cnpj_requerente',
... 'Data de exibicão':'data_exibicao'}
>>> filmes.rename(columns=n_nomes).head()
   codigo_obra       titulo_original  ...     cnpj_requerente  data_exibicao
0        15639        PREÇO DA PAZ O  ...  00.568.159/0001-07      06/jul/12
1         7603           CARTOMANTEA  ...  03.599.148/0001-82      13/jul/04
2        26453  BLACK & WHITE VOL. 9  ...  02.341.697/0001-90     26/09/2007
3        17284              THE GURU  ...  27.654.722/0001-16     16/12/2002
4         4806        QUIET AMERICAN  ...  00.979.601/0001-98      15/jan/03

[5 rows x 8 columns]

Como funciona...

Com o método rename() podemos renomear os nomes das colunas. Os novos nomes das colunas são passados para o parâmetro colunms como um dicionário com os nomes antigos e os novos.

Encadeando métodos das séries do Pandas

Na linguagem python é possível chamar métodos usando o acesso a atributos. As variáveis apontam para objetos e os atributos e métodos retornam mais objetos. Assim podemos fazer chamadas sequenciais de métodos usando atributos. Pandas é uma biblioteca que faz bom uso dessa programação de fluxo. Muitos métodos e atributos criam séries e dataframes, que por sua vez podem criar mais séries e dataframes. E desses objetos mais métodos e atributos podem ser chamados.
Como exemplo de encadeamento de métodos, vamos usar a frase: Uma pessoa vai ao restaurante comer costelas, volta pra casa, lava o carro e depois assiste TV.
Uma forma de codificar essa frase em python:

(pessoa.ir('Restaurante')
.pede('Costela')
.come('Costela')
.ir('Casa')
.lavar('Carro')
.assistir('TV')
)

Nesse exemplo, a pessoa é uma instância de uma classe que pode chamar métodos. E cada método pode criar outra instância, desse jeito o encedeamento ocorre. No exemplo acima, o argumento passado para o método define como o método deve ser executado.
Nesse post vamos aprender como o encadeamento funciona nas séries do Pandas.

Como fazer...

Primeiro vamos extrair uma coluna do dataset e encadear métodos da série:

>>> import pandas as pd
>>> filmes = pd.read_csv('filmes.csv', encoding='latin-1')
>>> diretor = filmes.Diretor
>>> diretor.value_counts().head(5)
LENILDO MAURICIO DA SILVA    218
JULIANO COSTA BENDAZOLI       69
JOSE ANTONIO GASPAR RAMOS     39
ROGER LEMOS                   26
LENILDO MAURÍCIO DA SILVA     22
Name: Diretor, dtype: int64

Nesse exemplo, chamamos o método value_counts(). Esse método retorna uma série contendo os elementos da série progenitora com a quantidade de vezes que o valor aparece na série. E logo em seguida chamamos o método head(5), que cria uma nova série com os cinco primeiros elementos retornados pelo método value_counts(). Pode não parecer mais a cada chamada de método criamos uma série nova.
Uma consequência do encadeamento de métodos e atributos é a dificuldade na hora de fazer a depuração. Isso acontece porque nenhum dos resultados dos métodos é guardado numa variável. Num encadeamento muito longo fica difícil encontrar o local do erro, se acontecer algum (e vai.). Uma dica para minimizar esse tipo de erro é colocar o resultado da chamada do método numa variável separada e depurar o seu código linha a linha.

Operações com séries do Pandas

A linguagem python nos permite fazer operações básicas com números e objetos atraves dos operadores. Com esses operadores somos capazes de manipular objetos. Um exemplo é o operador '+', com ele podemos somar dois inteiros e receber o resultado:

>>> 3 + 7 # Soma os dois números
10

Podemos usar operadores para manipular as séries e os dataframe do Pandas. As séries e os dataframe tem suporte a muitos dos operadores da linguagem python. Quando usamos um operador com uma série ou um dataframe vamos ter como resultado uma série ou um dataframe. Nesse post vamos apresentar alguns operadores que podemos usar com séries e dataframes. Ao usar esses operadores teremos como resultado séries com valores diferentes dos seus progenitores.

Como fazer isso...

O dataset que vamos usar nos exemplos abaixo pode ser baixado aqui.
Primeiro vamos começar com um operador fácil, o operador de soma. Utilizar esse operador com uma série e um inteiro irá criar uma nova série com cada elemento da série acrescentado do número inteiro. Veja um exemplo:

>>> import pandas as pd
>>> data = pd.read_csv('data.tsv', sep='\t')
>>> classif_media = data.averageRating
>>> classif_media
0          5.7
1          6.1
2          6.5
3          6.2
4          6.1
          ...
1056047    7.2
1056048    6.6
1056049    5.9
1056050    6.9
1056051    7.3
Name: averageRating, Length: 1056052, dtype: float64
>>> classif_media + 1
0          6.7
1          7.1
2          7.5
3          7.2
4          7.1
          ...
1056047    8.2
1056048    7.6
1056049    6.9
1056050    7.9
1056051    8.3
Name: averageRating, Length: 1056052, dtype: float64

As operações de divisão, multiplicação, exponenciação e subtração seguem a mesma lógica. Veja os exemplos abaixo:

>>> classif_media / 2
0          2.85
1          3.05
2          3.25
3          3.10
4          3.05
           ...
1056047    3.60
1056048    3.30
1056049    2.95
1056050    3.45
1056051    3.65
Name: averageRating, Length: 1056052, dtype: float64
>>> classif_media * 2
0          11.4
1          12.2
2          13.0
3          12.4
4          12.2
           ...
1056047    14.4
1056048    13.2
1056049    11.8
1056050    13.8
1056051    14.6
Name: averageRating, Length: 1056052, dtype: float64
>>> classif_media ** 2
0          32.49
1          37.21
2          42.25
3          38.44
4          37.21
           ...
1056047    51.84
1056048    43.56
1056049    34.81
1056050    47.61
1056051    53.29
Name: averageRating, Length: 1056052, dtype: float64
>>> classif_media - 2
0          3.7
1          4.1
2          4.5
3          4.2
4          4.1
          ...
1056047    5.2
1056048    4.6
1056049    3.9
1056050    4.9
1056051    5.3
Name: averageRating, Length: 1056052, dtype: float64

Para fazer a divisão exata, o que vai gerar um float, use duas barras. O operador módulo também é suportado pelas séries do Pandas.

>>> classif_media // 3
0          1.0
1          2.0
2          2.0
3          2.0
4          2.0
          ...
1056047    2.0
1056048    2.0
1056049    1.0
1056050    2.0
1056051    2.0
Name: averageRating, Length: 1056052, dtype: float64
>>> classif_media % 3
0          2.7
1          0.1
2          0.5
3          0.2
4          0.1
          ...
1056047    1.2
1056048    0.6
1056049    2.9
1056050    0.9
1056051    1.3
Name: averageRating, Length: 1056052, dtype: float64

Outros operadores válidos, para serem usados com as séries do Pandas, são os de comparação (>, <, ==, >=, <=, !=). Utilizando esses operadores a saída será uma série com valores booleanos. A comparação será feita entre o valor passado e cada elemento da série. Veja um exemplo:

>>> classif_media > 6
0          False
1           True
2           True
3           True
4           True
           ...
1056047     True
1056048     True
1056049    False
1056050     True
1056051     True
Name: averageRating, Length: 1056052, dtype: bool

O valor do primeiro elemento da série é False, isso porque o primeiro elemento da série classif_media é menor ou igual a seis. O segundo é True pois o valor do segundo elemento da série classif_media é maior que seis.
Podemos usar os operadores de comparação com strings. Para esse exemplo vamos utilizar o dataset dos tutoriais passados. Veja um exemplo:

>>> filmes = pd.read_csv('filmes.csv', encoding='latin-1')
>>> diretor = filmes.Diretor
>>> diretor
0       PAULO DE TARSO DE CARVALHO MORELLI
1           WAGNER DE ASSIS E PABLO URANGA
2                LENILDO MAURICIO DA SILVA
3                              DAISY MAYER
4                            PHILLIP NOYCE
                       ...
9257                         ADAM SHANKMAN
9258         LANA WACHOWSKI ANDY WACHOWSKI
9259                        PETER CATTANEO
9260      MARIO HUMBERTO MEIRELLES MOREIRA
9261                 ANDRE BORELLI MARTINS
Name: Diretor, Length: 9262, dtype: object
>>> diretor == 'DAISY MAYER'
0       False
1       False
2       False
3        True
4       False
        ...
9257    False
9258    False
9259    False
9260    False
9261    False
Name: Diretor, Length: 9262, dtype: bool

Como funciona...

Nos exemplos acima os operadores são usados em cada elemento da série individualmente. Se fossemos fazer isso sem a biblioteca Pandas precisaríamos de um loop para cada elemento. Mas graças ao Pandas não precisamos.
Em cada um dos exemplos é retornada uma série com os mesmos índices mais com novos valores.

Tem mais...

Para cada operador existe um método que faz exatamente a mesma coisa. Veja um exemplo com a divisão:

>>> classif_media / 2
0          2.85
1          3.05
2          3.25
3          3.10
4          3.05
           ...
1056047    3.60
1056048    3.30
1056049    2.95
1056050    3.45
1056051    3.65
Name: averageRating, Length: 1056052, dtype: float64
>>> classif_media.div(2)
0          2.85
1          3.05
2          3.25
3          3.10
4          3.05
           ...
1056047    3.60
1056048    3.30
1056049    2.95
1056050    3.45
1056051    3.65
Name: averageRating, Length: 1056052, dtype: float64

Abaixo temos uma tabela com os operadores e seus métodos correspondentes:

Grupo de operadores Operadores Métodos
Aritmético + , - , * , / , // , % , ** .add , .sub , .mul , .div , .floordiv , .mod , .pow
Comparação < , > , <= , >= , == , != .lt , .gt , .le , .ge , .eq , .ne

terça-feira, 21 de julho de 2020

Como chamar métodos de uma série do Pandas

Quando estamos trabalhando com a biblioteca Pandas é normal ficar alterando entre séries e dataframe. Fazer uso dos métodos de uma série é uma ótima maneira de extrair o máximo da biblioteca Pandas.
Dataframe e séries possuem muitos métodos e atributos. Para descobrirmos alguns desses métodos e atributos podemos usar a função build-in do python dir. No exemplo abaixo vamos mostra a quantidade de métodos que as séries e os dataframe têm em comum. Uma coisa para se notar é que as séries e os dataframe têm um grande número de atributos e métodos com nomes iguais.

>>> import pandas as pd
>>> s_atr_metodos = set(dir(pd.Series))
>>> len(s_atr_metodos)
433 # número de métodos e atributos de uma série
>>> df_atr_metodos = set(dir(pd.DataFrame))
>>> len(df_atr_metodos)
430 # número de métodos e atributos de um dataframe
>>> len(s_atr_metodos & df_atr_metodos)
377 # Métodos e atributos em comum

Com esse exemplo percebemos que existem muitos atributos e métodos na biblioteca Pandas. Você deve estar pensando que são muitos para aprender, mas não se preocupe. Os programadores mais experientes só usam alguns desses métodos e atributos.
Nos tutoriais, que viram, vamos aprender os métodos e atributos das séries mais utilizados e poderosos. Como ficou claro no exemplo acima a maioria desses métodos e atributos tem um correspondente para os DataFrame.

Como fazer isso...

Agora vamos selecionar duas séries de tipos diferentes de um dataset. A coluna 'Diretor' é formada pelo tipo de dado object - Esse tipo de dado é utilizado, em sua maioria, para representar strings. E a coluna 'Codigo da obra'. Essa coluna é formada por números inteiros (int64). Veja o exemplo abaixo:

>>> import pandas as pd
>>> filmes = pd.read_csv('filmes.csv', encoding='latin-1')
>>> diretor = filmes['Diretor']
>>> cod = filmes['Codigo da obra']
>>> diretor.dtype
dtype('O')
>>> cod.dtype
dtype('int64')

Para saber quais são os cinco primeiros elementos de uma série usamos o método head. Se for preciso um número maior de elementos isso pode ser arranjado com parâmetros opcionais. Outro método, para visualizar dados de uma série, é o sample. Com esse método é possível pegar amostras de toda a série. Veja como utilizar esses métodos:

>>> diretor.head()
0    PAULO DE TARSO DE CARVALHO MORELLI
1        WAGNER DE ASSIS E PABLO URANGA
2             LENILDO MAURICIO DA SILVA
3                           DAISY MAYER
4                         PHILLIP NOYCE
Name: Diretor, dtype: object
>>> diretor.sample(n=6, random_state=32)
3601                          JEAN-FRANÇOIS RICHET
1663                                ETHAN MANIQUIS
4897                               DAVID BLAUSTEIN
3710    LUIZ DE FRANCA GUILHERME DE QUEIROGA FILHO
1641                                 ANTOINE FUQUA
2611                                       MAT ASS
Name: Diretor, dtype: object

O método sample() recebeu dois parâmetros. O primeiro (n) sinaliza o numero de elementos que queremos e o random_state define a semente que o método vai utilizar para gerar os números aleatórios.
Sabendo o tipo de dado de uma série, sabemos qual é a função que mais vamos utilizar. Um exemplo disso é o método value_counts(). Esse método retorna uma série contendo o nome dos elementos e quantas vezes esse valor se repete. No exemplo abaixo, vemos que o valor 'ROGER LEMOS' aparece vinte e seis vezes na série, enquanto 'DESIREE AKHAVAN' aparece uma vez apenas.

>>> diretor.value_counts()
LENILDO MAURICIO DA SILVA    218
JULIANO COSTA BENDAZOLI       69
JOSE ANTONIO GASPAR RAMOS     39
ROGER LEMOS                   26
LENILDO MAURÍCIO DA SILVA     22
                            ...
HUGO REYNALDO MATTAR           1
DESIREE AKHAVAN                1
CHARLES FERGUSON               1
JORGE PAIXÃO DA COSTA          1
JESUS GARCES LAMBERT           1
Name: Diretor, Length: 4976, dtype: int64

Embora esse método seja mais utilizado com séries com dados do tipo objeto, você também pode usa-la com séries com dados numéricos.
Se você precisar saber o tamanho de uma série a biblioteca Pandas fornece algumas formas de fazer isso, como o atributo size, shape e o método count(). E ainda temos a função interna do python len(). Veja como usar esses métodos:

>>> diretor.size
9262
>>> diretor.shape
(9262,)
>>> len(diretor)
9262
>>> diretor.count()
9262

O método count() retorna o número de elementos com valores, os elementos NaN não são contabilizados.
E ainda temos o método unique(). Esse método retorna todos os elementos únicos da série, deixando de lado os valores duplicados.

>>> diretor.unique()
array(['PAULO DE TARSO DE CARVALHO MORELLI',
       'WAGNER DE ASSIS E PABLO URANGA', 'LENILDO MAURICIO DA SILVA', ...,
       'ÁLVARO DELGADO-APARICIO L.', 'KIM HONG-SUN',
       'MARIO HUMBERTO MEIRELLES MOREIRA'], dtype=object)

As funções de estatísticas de uma série são: min(), que retorna o menor valor da série; max(), retorna o maior valor; mean(), que retorna a média entre os valores da série; e median(), que retorna a mediana.

>>> cod.min()
1
>>> cod.max()
20002501
>>> cod.mean()
10534938.192291081
>>> cod.median()
15004498.0

Se você esta com um pouco de pressa e precisa saber as informações do exemplo anterior com um método só. Você pode usar o método describe(). Com esse método você vai obter as informações básicas de estatística da série. Um exemplo:

>>> cod.describe()
count    9.262000e+03
mean     1.053494e+07
std      7.714715e+06
min      1.000000e+00
25%      4.165462e+05
50%      1.500450e+07
75%      1.600608e+07
max      2.000250e+07
Name: Codigo da obra, dtype: float64

Chamando o método quantile() obtemos o quantil de dados numéricos. Se passarmos um valor escalar como argumento, vamos receber uma saída escalar, e se passarmos uma lista vamos receber uma série.

>>> cod.quantile(0.3)
630214.2
>>> cod.quantile(
... [0.1, 0.2, 0.3, 0.4, 0.5]
... )
0.1       15196.1
0.2      278800.6
0.3      630214.2
0.4    14026379.0
0.5    15004498.0
Name: Codigo da obra, dtype: float64

Com o método count() podemos saber a quantidade de elementos de uma série que tem um valor definido. E se quiséssemos saber os valores que estão faltando? Nesse caso usaríamos o método isna(). Esse método retorna uma série com os valores True ou False para cada elemento de uma série. Por exemplo, se o primeiro elemento da série não tiver um valor o primeiro elemento da série, retornada pelo método isna(), vai ser True e False caso o elemento tenha um valor definido. E isso se repete até o último elemento das duas séries.

>>> diretor.isna()
0       False
1       False
2       False
3       False
4       False
        ...
9257    False
9258    False
9259    False
9260    False
9261    False
Name: Diretor, Length: 9262, dtype: bool

Nesse exemplo, o resultado do método isna() foi uma série com todos os valores False, isso pois a série que usamos não tem nenhum valor faltando.
Agora vamos supor que a série que usamos no exemplo anterior tivesse alguns valores faltando e quiséssemos preencher esse espaço vazio com o valor ‘Sem diretor’. Nesse caso usaríamos o método fillna(). Esse método preenche os valores vazios da série com o valor que recebe. Um exemplo de como fazer:

>>> diretor.fillna('Sem diretor')
0       PAULO DE TARSO DE CARVALHO MORELLI
1           WAGNER DE ASSIS E PABLO URANGA
2                LENILDO MAURICIO DA SILVA
3                              DAISY MAYER
4                            PHILLIP NOYCE
                       ...
9257                         ADAM SHANKMAN
9258         LANA WACHOWSKI ANDY WACHOWSKI
9259                        PETER CATTANEO
9260      MARIO HUMBERTO MEIRELLES MOREIRA
9261                 ANDRE BORELLI MARTINS
Name: Diretor, Length: 9262, dtype: object

Outra possibilidade é que você não queira adicionar um valor onde está faltando, mas sim remover esses elementos vazios. Nesse caso faríamos uso do método dropna(). Esse método remove todos os elementos vazios da série. Veja um exemplo de uso:

>>> diretor.dropna()
0       PAULO DE TARSO DE CARVALHO MORELLI
1           WAGNER DE ASSIS E PABLO URANGA
2                LENILDO MAURICIO DA SILVA
3                              DAISY MAYER
4                            PHILLIP NOYCE
                       ...
9257                         ADAM SHANKMAN
9258         LANA WACHOWSKI ANDY WACHOWSKI
9259                        PETER CATTANEO
9260      MARIO HUMBERTO MEIRELLES MOREIRA
9261                 ANDRE BORELLI MARTINS
Name: Diretor, Length: 9262, dtype: object

Nesse post aprendemos a utilizar alguns dos métodos mais utilizados na análise de dados com a biblioteca Pandas.
Os métodos que aprendemos a utilizar retornam diferentes tipos de dados.

Tem mais...

Com os exemplos que usamos, até agora nesse post, podemos saber se existem valores faltando na série observando o resultado do método count() (que retorna o número de elementos com valor) e o atributo size (que retorna o número total de elementos de uma série). Uma abordagem mais direta séria o atributo hasnans. Se tiver algum elemento sem valor, o valor do atributo será True, caso contrario será False.

segunda-feira, 20 de julho de 2020

Selecionando uma coluna com Pandas

Como já vimos nos posts anteriores selecionar uma coluna de um dataframe com Pandas retorna uma série. Uma série é um conjunto de dados organizados numa dimensão, diferente do dataframe que tem duas dimensões. Uma série é composta pelo índice e pelos dados. Com a biblioteca Pandas é possível criar uma série sem precisar de um dataframe, mais isso não é muito comum. O que geralmente acontece é criar uma série a partir de um dataframe.
Nesse tutorial vamos aprender a criar uma série de duas formas diferentes. No primeiro exemplo vamos usar o índice para criar uma série e no outro vamos criar uma série a partir de um atributo.

Como fazer isso...

Para criar uma série a partir de um dataframe, usando índices, o que precisamos fazer é passar o nome da coluna como se fosse um índice. Veja o exemplo abaixo:

>>> import pandas as pd
>>> filmes = pd.read_csv('filmes.csv', encoding='latin-1')
>>> filmes['Titulo no Brasil']
0                    PREÇO DA PAZ O
1                       CARTOMANTEA
2              BLACK & WHITE VOL. 9
3                    O GURU DO SEXO
4             O AMERICANO TRANQUILO
                       ...
9257          ROCK OF AGES: O FILME
9258                         MATRIX
9259                 MILITARY WIVES
9260    ROBERTO CARLOS EM JERUSALÉM
9261                         O POÇO
Name: Titulo no Brasil, Length: 9262, dtype: object

Criar uma série a partir de um atributo é ainda mais simples do que com índice. Veja um exemplo:

>>> import pandas as pd
>>> filmes = pd.read_csv('filmes.csv', encoding='latin-1')
>>> filmes.Diretor
0       PAULO DE TARSO DE CARVALHO MORELLI
1           WAGNER DE ASSIS E PABLO URANGA
2                LENILDO MAURICIO DA SILVA
3                              DAISY MAYER
4                            PHILLIP NOYCE
                           ...
9257                         ADAM SHANKMAN
9258         LANA WACHOWSKI ANDY WACHOWSKI
9259                        PETER CATTANEO
9260      MARIO HUMBERTO MEIRELLES MOREIRA
9261                 ANDRE BORELLI MARTINS
Name: Diretor, Length: 9262, dtype: object

Outra alternativa é utilizar os atributos loc e iloc. Com o atributo loc é possível conseguir uma série de uma coluna ou linha passando o seu nome. O atributo iloc funciona de modo semelhante ao atributo loc, a diferença é que o atributo iloc recebe o índice da coluna.
Veja um exemplo de como utilizar o atributo loc:

>>> import pandas as pd
>>> filmes = pd.read_csv('filmes.csv', encoding='latin-1')
>>> filmes.loc[:, 'Titulo Original']
0                    PREÇO DA PAZ O
1                       CARTOMANTEA
2              BLACK & WHITE VOL. 9
3                          THE GURU
4                    QUIET AMERICAN
                   ...             
9257                   ROCK OF AGES
9258                     THE MATRIX
9259                 MILITARY WIVES
9260    ROBERTO CARLOS EM JERUSALÉM
9261                         O POÇO
Name: Titulo Original, Length: 9262, dtype: object

Veja um exemplo de como utilizar o atributo iloc:

>>> import pandas as pd
>>> filmes = pd.read_csv('filmes.csv', encoding='latin-1')
>>> filmes.iloc[:, 1]
0                    PREÇO DA PAZ O
1                       CARTOMANTEA
2              BLACK & WHITE VOL. 9
3                          THE GURU
4                    QUIET AMERICAN
                   ...             
9257                   ROCK OF AGES
9258                     THE MATRIX
9259                 MILITARY WIVES
9260    ROBERTO CARLOS EM JERUSALÉM
9261                         O POÇO
Name: Titulo Original, Length: 9262, dtype: object

Nos dois exemplos acima acessamos todos os valores da coluna ‘Título Original’. No primeiro exemplo usamos o atributo loc. Com esse atributos passamos o nome da coluna que queremos acessar, no caso ‘Título Original’. Já com o atributo iloc passamos o número do índice da coluna que queremos acessar. No exemplo, logo acima, passamos o valor um assim temos acesso aos dados da segunda coluna (Lembrando que o índice começa em zero). Os dois pontos usados nos exemplos sinaliza que queremos os dados de todas as alinhas. Para selecionar um intervalo: filmes.iloc[42:100, 1].
Na saído dos dois atributos é possível notar o índice das linhas (rows), três pontos entre as linhas e, no final, o nome da coluna, o tamanho (Length) e o tipo de dados (dtype). O índice serve para saber qual é a posição do valor. Os três pontos sinalizam que alguns elementos não foram mostrados.
Um dataframe do Pandas, geralmente, tem duas ou mais colunas e cada uma dessas colunas pode ser tratada como uma série.
Nos exemplos acima ficou claro que podemos obter uma série de um dataframe de diferentes maneiras. O jeito mais fácil é como atributo. Uma coisa para prestar atenção na hora de nomear: os valores aceitos são os alfanuméricos sem espaços e sem caracteres especiais.

domingo, 19 de julho de 2020

Tipos de dados com Pandas

Os tipos de dados utilizados na biblioteca pandas podem ser classificados como contínuos ou categóricos. Os dados contínuos são usados para representar dados numéricos como a distância entre dois pontos, peso ou altura. Já os dados categóricos são usados para representar um dado de uma quantidade finita. Um exemplo de objetos finitos são as cores que um monitor pode mostrar (16,7 milhões de cores). Os dados categóricos são usados para esse tipo de dado, representa um dado entre um número finito de possibilidades.
Na biblioteca Pandas existem alguns tipos de dados específicos. A seguir veja quais são esses tipos de dados:

  • int: Tipo inteiro da biblioteca NumPy. Esse tipo de dado não tem suporte a valores ausentes.
  • int64: Numero nulo inteiros do Pandas.
  • float: Numero de ponto flutuante da biblioteca NumPy. Esse tipo de dado suporta valores ausentes.
  • object: Com esse tipo de dados você trabalhar com sequencias de caracteres. Esses caracteres podem ser números ou letras.
  • category: Tipo categórico da biblioteca Pandas.
  • bool: Tipo booleano da biblioteca NumPy. Esse tipo de dado não suporta dados ausentes.
  • boolean: Tipo booleano que suporta valor nulo.
  • datetime64: Tipos data da biblioteca NumPy. Esse tipo aceita valores ausentes.

Uma coisa muito importante de saber é o tipo de dados usados num dataframe. Esse conhecimento é importante porque é o tipo de dado que determina quais operações são possíveis. Após a criação do dataframe podemos saber quais são os tipos de dados armazenados em cada coluna.

Como verificar o tipo de dados de um dataframe

Para verificar os tipos de dados de um dataframe podemos fazer uso do atributo dtypes, do método value_counts() ou do método info().
Com o atributo dtypes você vai receber uma saída com duas colunas. A primeira coluna contem os valores do primeiro elemento de cada coluna. E a segunda coluna o tipo de dados que a coluna contém.

>>> import pandas as pd
>>> filmes = pd.read_csv('filmes.csv', encoding='latin-1')
>>> filmes.dtypes
Codigo da obra                  int64
Titulo Original                object
Titulo no Brasil               object
Ano de producão               float64
Diretor                        object
Razão Social do Requerente     object
CNPJ Requerente                object
Data de exibicão               object
dtype: object

A saída do método value_counts() são duas colunas. A primeira coluna se refere ao tipo de dados e a segunda a quantidade de colunas que contém dados desse tipo.

>>> filmes.dtypes.value_counts()
object     6
float64    1
int64      1
dtype: int64

Já o método info() nos fornece algumas informações sobre o dataframe como números de colunas, de linhas e a memória que esse dataframe está utilizando.

>>> filmes.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 9262 entries, 0 to 9261
Data columns (total 8 columns):
 #   Column                      Non-Null Count  Dtype
---  ------                      --------------  -----
 0   Codigo da obra              9262 non-null   int64
 1   Titulo Original             9262 non-null   object
 2   Titulo no Brasil            9262 non-null   object
 3   Ano de producão             9259 non-null   float64
 4   Diretor                     9262 non-null   object
 5   Razão Social do Requerente  9262 non-null   object
 6   CNPJ Requerente             9262 non-null   object
 7   Data de exibicão            9262 non-null   object
dtypes: float64(1), int64(1), object(6)
memory usage: 361.9+ KB

O tipo de dado object, normalmente, é uma string. Caso esteja faltando algum elemento de uma coluna será atribuído o valor NaN (do tipo float) para o elemento.

Atributos do dataframe com Pandas

O index, colunas e dados podem ser acessados a partir de um dataframe. Isso é útil quando você precisar acessar um valor especifica e não o dataframe inteiro. Se estamos trabalhando com uma matriz numérica deixamos esses dados numa matriz NumPy, mas se apenas algumas linhas ou colunas são numéricas guardamos esses dados num dataframe Pandas. Com dataframes podemos trabalhar com matrizes que não são numéricas mais possuem linhas ou colunas heterogêneas de dados numéricos.
No exemplo abaixo vamos criar variáveis para visualizar os dados do dataframe individualmente, como o index, as colunas e os dados.

Como fazer isso...

Nesse exemplo vamos usar os atributos do dataframe para colocar cada dado na sua própria variável e depois mostrar o conteúdo de cada uma delas.

>>> import pandas as pd
>>> filmes = pd.read_csv('filmes.csv', encoding='latin-1')
>>> index = filmes.index
>>> colunas = filmes.columns
>>> dados = filmes.to_numpy()
>>> index
RangeIndex(start=0, stop=9262, step=1)
>>> colunas
Index(['Codigo da obra', 'Titulo Original', 'Titulo no Brasil',
       'Ano de producão', 'Diretor', 'Razão Social do Requerente',
       'CNPJ Requerente', 'Data de exibicão'],
      dtype='object')
>>> dados
array([[15639, 'PREÇO DA PAZ O', 'PREÇO DA PAZ O', ...,
        'M A PRODUÇÕES ARTISTICAS E CULTURAIS LTDA ME',
        '00.568.159/0001-07', '06/jul/12'],
       [7603, 'CARTOMANTEA', 'CARTOMANTEA', ...,
        'TAG CULTURAL DISTRIBUIDORA DE FILMES LTDA',
        '03.599.148/0001-82', '13/jul/04'],
       [26453, 'BLACK & WHITE VOL. 9', 'BLACK & WHITE VOL. 9', ...,
        'FALLMS DISTRIBUIÇÃO DE FITAS LTDA', '02.341.697/0001-90',
        '26/09/2007'],
       ...,
       [19002684, 'MILITARY WIVES', 'MILITARY WIVES', ...,
        'ANTONIO FERNANDES FILMES LTDA', '02.668.665/0001-01',
        '14/02/2020'],
       [609762, 'ROBERTO CARLOS EM JERUSALÉM',
        'ROBERTO CARLOS EM JERUSALÉM', ...,
        'H2O DISTRIBUIDORA DE FILMES S/A', '15.372.472/0001-42',
        '20/12/2019'],
       [19005137, 'O POÇO', 'O POÇO', ..., 'ANDRE BORELLI MARTINS',
        '408.504.318-83', '18/10/2019']], dtype=object)

Como funciona…

As colunas e os índices são quase a mesma coisa, o que muda é o sentido de cada um (ou eixo). É comum que o index seja chamado de “eixo 0” e a coluna de “eixo 1”.
Por padrão a biblioteca Pandas usa o tipo de dado RangeIndex, mais existem vários tipos de dados que podem ser usados no seu lugar. Utilizar o tipo RangeIndex tem a vantagem de que apenas os valores necessários são carregados na memória, isso ajuda a economizar memória. Esse tipo de dado consiste no valor de inicio, o valor de parada e o incremento.

Tem mais...

O índices e as colunas são implementados como tabelas de hash. Com isso é possível fazer um alinhamento e uma seleção mais rápida. Os objetos index (índices e colunas) são parecidos com os conjuntos do Python, a diferença é que eles são ordenados e aceitam entradas duplicadas.

Introdução ao Pandas

Nesse primeiro post vamos aprender alguns conceitos importantes quando trabalhamos com pandas: estruturas de dados, séries e dataframe. Uma coisa que deve ficar bem clara é a diferença entre uma serie e um dataframe.
A biblioteca pandas é utilizada para trabalhar com dados estruturados. Arquivos CSV, planilhas do Excel e bancos de dados são alguns exemplos de dados estruturados. Dados não estruturados é todo tipo de dado que não tem uma estrutura lógica. Se estiver trabalhando com dados estruturados a biblioteca pandas será útil em algum momento e, às vezes, necessária.
Em primeiro lugar você precisa saber que um dataframa é um conjunto de dados com duas dimensões (bidimensional) e uma serie é um conjunto de dados unidimensional. Nos próximos posts vamos aprender como ler os dados de apenas uma coluna de um dataframe e retornar uma serie.
O que torna a biblioteca Pandas diferente das outras é o index. O index é apenas um número que funciona como rótulo de um dado. Nos próximos posts ficará claro como utilizar o index torna a biblioteca pandas única. Isso será quando utilizamos índices como rótulos dos elementos de uma série.

Dataframe Pandas

Dataframe pode ser encarado como uma matriz bidimensional. Essa matriz é dividida em linhas e colunas. Um dataframe tem três componentes importantes: o index, as colunas e os dados. Você deve aprender a utilizar cada um desses elementos para dominar a biblioteca Pandas.
No exemplo abaixo vamos ler os dados do dataset filmes.csv (você pode baixa o arquivo aqui) e guardar esses dados num dataframe da biblioteca Pandas. Com isso conseguimos um diagrama rotulado dos principais componentes do dataset.

>>> import pandas as pd
>>> filmes = pd.read_csv('filmes.csv', encoding='latin-1')
>>> filmes
      Codigo da obra  ... Data de exibicão
0              15639  ...        06/jul/12
1               7603  ...        13/jul/04
2              26453  ...       26/09/2007
3              17284  ...       16/12/2002
4               4806  ...        15/jan/03
...              ...  ...              ...
9257        16001794  ...        09/jul/20
9258        15000966  ...        25/nov/19
9259        19002684  ...       14/02/2020
9260          609762  ...       20/12/2019
9261        19005137  ...       18/10/2019
    
[9262 rows x 8 columns]

Como funciona...

Primeiro a biblioteca Pandas ler os dados do disco e aloca esses dados na memoria como um dataframe, utilizando o método read_csv(). Utilizamos o index para fazer referencia a uma linha e coluna para fazer referencia a uma coluna.
Com o index e a coluna podemos ler o valor de um elemento isolado. Nos próximos post veremos mais como fazer isso. Ao combinar várias séries, os indexes são alinhados antes que qualquer coisa aconteça.
É comum, nas documentações sobre dataframe, se referir ao índice como eixo 0 e as colunas como eixo 1. No exemplo acima é possível notar linhas e colunas com sequências de três pontos. Isso indica que, pelo menos, uma linha ou coluna não foram mostradas. Outro acontecimento comum é o NaN. Isso indica que o elemento está vazio.

sábado, 18 de julho de 2020

Exceções e tratamento de exceções com Delphi

Introdução

Desde a primeira versão do Delphi ele já trás o tratamento de exceções. Mesmo já fazendo muito tempo que essa funcionalidade acompanha o Delphi, isso não impediu que o tratamento de exceções fosse usado de maneira equivocada e errada até hoje.
Nesse post veremos como o tratamento de exceções deve ser feito de maneira correta. Para começar vou mostrar alguns exemplos de como NÃO usar esse recurso. Tendo esses exemplos em mente vamos discutir maneiras mais apropriadas de usar o tratamento de exceções. Uma coisa que a maioria dos programadores não percebe é que utilizar um tratamento de exceções de maneira errada pode causar mais problemas do que resolver. Utilizando esse recurso da linguagem de maneira correta o seu código fica mais fácil de ler e manter.

Tratamento de exceções estruturadas

A exceção é um recurso das linguagens de programação que permite ao programador criar um bloco de código que será executado se algum problema, não previsto acontecer. Com esse recurso o programador pode responder de maneira adequada ao erro quando ele acontece ou, até mesmo tratar esse erro.
A maioria dos programas segue uma sequencia de acontecimentos. Assim um determinado número de funções deve ser executado, corretamente, antes das funções seguintes. Um exemplo disso é uma função que excluí um registro de um banco de dados. Para que essa função seja executada corretamente é preciso que o usuário já esteja conectado ao banco de dados. Como as interfaces dos programas só disponibilizam a opção de excluir um registro depois que o usuário se conecta ao banco de dados, o programador pode deduzir que o usuário estará conectado ao banco de dados. Nessa hipótese o que acontece se o banco de dados apresentar uma falha e desconectar do usuário?
Uma solução para esse problema é verificar a conexão a cada chamada da função. Mas isso pode levar a duplicação de código, ou o programador pode simplesmente esquecer-se de fazer uma verificação.
Todos esses problemas podem ser resolvidos com o tratamento de exceções estruturadas. Uma exceção permite que a aplicação não pare de funcionar por apenas um erro. Além disso, é possível resolver o problema dentro de uma exceção.

Como não usar exceções

Uma boa parte do trabalho de um programador é em projetos existentes que estão com problemas (Isso por cauda de um projeto mal feito) e refazendo o trabalho que não foi bem feito na primeira vez. Um erro bem comum é o mau uso das exceções. Começaremos analisando os exemplos "Não faça", para ter uma noção de porque essas técnicas não são uma boa ideia.

Não esqueça as exceções

O uso mais comum e presente das exceções é o de esquecer o tratamento das exceções. Não é muito difícil encontrar códigos escritos como esse:

try
    AlgumaRotinaQueAsVezesCausaUmaViolaçãoDeAcesso
except
end;

Nesse exemplo é possível ver que o programador está evitando qualquer exceção que possa aparecer. Não é raro uma rotina no bloco try ocasionar num erro que não é encontrado com facilidade e, em vez de seguir o caminho difícil (e correto) de encontrar o erro, o programador segue o caminho mais fácil e executa uma exceção. O que alguns desenvolvedores desejam com isso é que o usuário nunca veja uma mensagem de erro. Se esse é o seu objetivo você ainda pode conseguir isso sem esconder os erros do resto do código.
Com esse exemplo todas as exceções serão suprimidas - exceções de bando de dados, exceções de falta de memória, falhas de hardware, qualquer um desses. Desse modo o seu programa pode retornar com êxito quando, na verdade, deveria ter retornado com erro. Assim fica mais difícil encontrar um erro. É melhor tratar o erro da forma correta do que ter um erro silencioso que pode resultar no desmoronamento de todo o seu sistema.
Uma exceção pode ser aceitável quando você estiver trabalhando com módulos, desse modo é possível impedir que uma exceção afete todo o seu sistema. Um exemplo disso é com DLLs, nesse cenário não deixe que nenhuma exceção escape. Com uma exceção vazia no final de uma DLL esse problema seria resolvido. Mas, se esse não for o caso, não deixe blocos de exceções vazias. E mesmo nesses casos, você deve ser informado se algum erro ocorrer ou registrar esse erro. Ficar sem saber as informações dificulta a busca pela solução do problema. É possível que apenas "pulando" esse erro o seu cliente nunca descubra que existe um problema, mas também é possível que ele descubra, e sem as informações certas você não sabe o que aconteceu e muito menos como consertar.

Não intercepte exceções de forma genérica

Não é muito difícil encontrar códigos escritos dessa maneira:

try
    AlgoQueTalvezCauseProblemas
except
   on E: Exception do
   begin
      MessageDlg(E.Message, mtWarning, [mbOK], 0);
   end;
end;

O que esse código faz é relatar um problema que de um jeito ou de outro seria relatado. Mas ele faz mais do isso, ele interrompe a exceção. A exceção será tratada no escopo local, dessa maneira ela não vai escapar do escopo atual. E ainda vai capturar todas as exceções, inclusive até aquelas que você pode não querer.
Esse código é um pouco melhor do que "pular" as exceções (só um pouco). Você pode considerar usar esse código quando chamar uma rotina que não vai lidar com nenhuma exceção ou quando ela for lidar com uma exceção especifica. Um exemplo disso é o evento OnReconcileError do TClientDataset que passa uma exceção. Quando um processo em lote do Clientdataset lança uma exceção isso pode interromper o loop. Desse modo é possível que você queira capturar todas as exceções de forma genérica.

Não procure exceções

Em termos de processamento as exceções são caras para criar e manipular, sendo assim você não deve criá-las sem um proposito. E não criá-las com o proposito, apenas, de verificar a ocorrência de um erro.
Você pode estar tentando fazer algo parecido com esse exemplo:

function StringIsInteger(str: string): Boolean;
var
    Tmp: integer;
begin
   Result := True;
   try
     Tmp := StrToInt(str);
   except
      Result := False;
   end;
end;

Com esse código você consegue o que quer, mas é provável que gere muitas exceções. E isso pode trazer problemas para o desempenho da aplicação, especialmente quando a probabilidade do resultado for False for alto.
Deixando de lado o desempenho, esse exemplo faz mau uso do tratamento de exceções. Claramente a função foi projetada para aceitar parâmetros não inteiros. Por isso uma exceção não deve ser usada nesse caso. Uma maneira mais inteligente de resolver esse problema seria usar TryStrToInt do SysUtils.

Não use o tratamento de exceções como um sistema de sinalização genérico

type
TExceçãoDeComportamentoNormal = class(Exception);
    ...
begin
   CoisasNormaisESemErros;
   raise TExceçãoDeComportamentoNormal.Create('Algo perfeitamente normal' + 'e esperado aconteceu');
end;

Muitos desenvolvedores fazem uso do exemplo acima para sinalizar que ocorreu tudo bem na rotina. Você também pode se sentir tentado a fazer a mesma coisa, mas lembre-se que as exceções devem ser usadas para controlar o fluxo do programa em casos específicos e passar informações. Utilizar esse recurso para enviar uma mensagem de "que tudo ocorreu bem" pode ter consequências inesperadas.

Como usar exceções de maneira apropriada

Agora que você sabe algumas maneiras de como não usar exceções, aqui estão algumas dicas para usar corretamente as exceções do Delphi.

Use exceções para que seu código seja executado sem a Interrupção de tratamento de erros

Um dos objetivos das exceções é separar o código do seu programa do código de tratamento de algum erro eventual que possa acontecer na execução do seu programa. Desse modo é possível escrever o seu código como se nada fosse dar errado e, depois, separar o código com try e except para tratar algum problema desse código. Assim o seu código é executado com eficiência porque ele não está a todo o momento procurando por parâmetros errados para garantir que está tudo certo, para só então fazer alguma coisa com esses parâmetros.
Uma boa pratica é separar o código das exceções com TApplication. Esse objeto possui um evento chamado OnException que serve perfeitamente para esse tipo de situação. Com esse evento você pode lidar com todas as exceções que não forem tratadas pelo seu programa. OnException permite você registrar as exceções ou aplicar um tratamento para cada tipo especifico de exceção.

Os desenvolvedores devem capturar as exceções

Como veremos abaixo, as bibliotecas e os componentes devem ser a principal fonte de exceções. Esses são os locais onde a maioria das exceções é criada. Escrevendo aplicativos a necessidade de criar exceções é baixa. Os desenvolvedores devem lidar com as exceções geradas pelos componentes e pelas bibliotecas.

Capture apenas exceções especificas

Como já falamos acima, nunca "pule" uma exceção. A coisa mais produtiva a se fazer é capturar apenas exceções especificas do seu código. Por exemplo, se você esta fazendo muitas conversões capture EConvertError. Ou EMathError, se você estiver utilizando muita matemática.
Como mencionado acima, existem programadores (ou gerente de projeto) que não querem que o usuário veja uma mensagem de erro. Isso não é um problema em se, o que causa o problema é "pular" exceções. Uma maneira de não deixar o usuário ver uma mensagem de erro e não "pular" essa exceção é capturar a exceção especifica que o usuário está vendo. Veja um exemplo:

try
   CódigoQueVaiLançarEConvertError;
except
   on E: EConvertError do
   begin
      // Trate o erro EConvertError aqui
   end;
end;

Com esse código apenas uma exceção especifica será capturada e não todas. É uma coisa simples mais é melhor do que pular todas as exceções.
A maioria das exceções tem um código de erro, você pode capturar apenas erros com o código que lhe interessa e permitir que outros erros apareçam. Veja um exemplo de como isso pode ser feito:

try
   LançaOErroEConvertError;
except
   on E: EIBError do
   begin
      if E.ErrorCode = ErroQueQueroPegar then
         begin
            // Trate desse erro específico
         end else
         begin
            raise; // Lança uma exceção se não for a única
         end;
   end;
end;

Um motivo para capturar as exceções no nível hierárquico mais baixo é que pode haver futuras exceções que descendem da classe Exception. Veja um exemplo:

try
   AlgumDataset.Open
except
   on E: EDatabaseError do
   begin
      // Trate a exceção
   end;
end;

e deposi ceclarar:

type
   ENxEstranhoDatabaseError = class(EDatabaseError)

Essa exceção estranha será capturada pelo código, talvez isso não seja oque você queira. Certamente não é possível impedir que isso aconteça em cada acontecimento. Mas a frequência que acontece pode diminuir capturando exceções na parte mais inferior do código.
Conclusão: intercepte as exceções o mais longe possível na hierarquia de classes e intercepte apenas as exceções que você planeja manipular.

Componentes e bibliotecas lançam exceções

As exceções não aparecem do nada. A sua grande maioria é criada dentro do código. (Algumas poucas podem ser criadas fora do código Delphi) Mesmo assim é bom que você faça as suas próprias exceções.
Uma boa prática é criar exceções para cada erro dentro das suas bibliotecas e componentes. Assim outros desenvolvedores podem capturar essas exceções especificas.
Você deve escrever os seus códigos para que eles retornem apenas dois resultados: o resultado esperado ou uma exceção.

Deixe que os usuários vejam mensagens de exceção

Quando você esconde todas as exceções do usuário o que pode acontecer é o programa avançar sem ter concluído as tarefas e deixando dados corrompidos para trás. E o pior é que o usuário acha que tudo ocorreu bem.
Muitos especialistas em interface observaram que os usuários têm medo das caixas de diálogo. Um dos motivos é que elas não fornecem algo útil para o usuário fazer, o que a maioria faz é tipo isso: mostrar uma mensagem tipo "F@#$%... Você acaba de perder o trabalho de 2 horas" (Com certeza as mensagens não chegam nem perto disso, mas é o quê os usuários entendem. E estão errados!?) e um botão de OK. Uma coisa a se fazer é passar informações realmente uteis, para que o erro possa ser corrigido, isso de uma maneira amigável.

Boas mensagens para as exceções

Quando criar uma mensagem de erro sinta-se livre para detalhar o tipo de erro. Use mensagens curtas e diretas para informar o que acabou de ocorrer.
Não faça algo do tipo:

type
   EAlgumaExcecao = class(Exception);

procedure CausaUmaExcecao;
begin
   raise EAlgumaExcecao.Create('Mensagem qualquer');
end;

Quando você pode fazer algo parecido com isso:

type
   EAlgumaExcecao = class(Exception);
procedure CausaUmaExcecao;
begin
   raise EAlgumaExcecao.Create('Um erro ocorreu, e foi isso que aconteceu....');
end;

Ao escrever uma mensagem de erro descreva o que aconteceu de forma descritiva e completa. Nessa mensagem talvez você queira informar o nome do procedimento ou do objeto.

Conclusão

É muito mais fácil usar o tratamento de exceções de maneira incorreta. Com algumas linhas de código fazer um erro "desaparecer" é muito tentador. Mas, infelizmente, usar as exceções desse jeito pode causar mais problemas ao invés de fazer os existentes desaparecer. O uso correto do tratamento de exceções facilita a leitura do código e a sua manutenção. Use as exceções corretamente para criar um código limpo e organizado.

Introdução a gráficos 3D com Matplotlib

Antes de começarmos a criar gráficos 3D com a biblioteca Matplotlib precisamos habilitar o módulo tollkit da biblioteca. Para habilitar esse módulo precisamos importar a biblioteca mplot3d. Não se preocupe se você não instalou essa biblioteca, por padrão ela já vem com a biblioteca matplotlib. Apenas uma ressalva quanto a isso: verifique se a versão do Matplotlib é igual ou superior à versão 1.0.
Quando o módulo já tiver sido importado, é fácil criar um gráfico 3D apenas passando o valor '3d' para o parâmetro projection de qualquer método para a criação de eixos do matplotlib. Veja um exemplo abaixo:

Exemplo Python

from mpl_toolkits import mplot3d

import numpy as np
import matplotlib.pyplot as plt
    
fig = plt.figure()
eixos = plt.axes(projection="3d")
    
plt.show()

Com os nossos eixos criados já podemos começar a fazer desenhos nesses eixos. Os métodos para criar gráficos em três dimensões são muito similares aos métodos para criar gráficos em 2D, a diferença é a terminação do método que termina com 3d. Outra diferença é o número de argumentos, ao invés de passar apenas dois argumentos passaremos três, um para cada eixo. Os outros parâmetros permanecem os mesmo, como os parâmetros para alterar a cor e a linha dos gráficos. Veja um exemplo abaixo:

Exemplo Python

from mpl_toolkits import mplot3d
import numpy as np
import matplotlib.pyplot as plt
    
fig = plt.figure()
eixos = plt.axes(projection="3d")
    
z_line = np.linspace(0, 15, 1000)
x_line = np.cos(z_line)
y_line = np.sin(z_line)
eixos.plot3D(x_line, y_line, z_line, 'gray')
    
pontos_z = 15 * np.random.random(100)
pontos_x = np.cos(pontos_z) + 0.1 * np.random.randn(100)
pontos_y = np.sin(pontos_z) + 0.1 * np.random.randn(100)
eixos.scatter3D(pontos_x, pontos_y, pontos_z, c=pontos_z, cmap='viridis');
    
plt.show()

Assim como os gráficos 2D, podemos mover o gráfico 3D, dar zoom ou mudar o ângulo de visão.

Gráficos de superfície

Os gráficos de superfície são usados para visualizar um conjunto de três variáveis num cenário 3D. Com esse tipo de gráfico é possível visualizar como o valor de um eixo em relação aos outros dois. Veja um exemplo abaixo:

Exemplo Python

from mpl_toolkits import mplot3d
import numpy as np
import matplotlib.pyplot as plt
    
fig = plt.figure()
ax = plt.axes(projection="3d")
    
ax = plt.axes(projection='3d')
    
def funcao_z(x, y):
    return np.sin(np.sqrt(x ** 2 + y ** 2))
    
x = np.linspace(-6, 6, 30)
y = np.linspace(-6, 6, 30)
    
X, Y = np.meshgrid(x, y)
Z = funcao_z(X, Y)
    
ax.plot_wireframe(X, Y, Z, color='green')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
    
ax.plot_surface(X, Y, Z, rstride=1, cstride=1,
                    cmap='viridis', edgecolor='none')
ax.set_title('Gráfico de superfícies');
    
plt.show()

Gráficos de fluxo com Matplotlib

Um gráfico de fluxo é um tipo de gráfico 2D usado para mostrar fluxo de fluido e gradientes de campo 2D.
O método básico para criar um gráfico de fluxo no Matplotlib é:
ax.streamplot(x_grid,y_grid,x_vec,y_vec, density=espacamento)
Onde x_grid e y_grid são matrizes dos pontos x, y. As matrizes x_vec e y_vec denotam a velocidade do fluxo em cada ponto da grade. O argumento do parâmetro density=espacamento especifica a proximidade das linhas de fluxo.

Um gráfico de fluxo simples

Vamos começar com um gráfico de fluxo que contém linhas de fluxo em uma grade 10 x 10. Todas as linhas de fluxo no gráfico são paralelas e apontam para a direita.
A seção de código a seguir cria um gráfico de fluxo que contém linhas paralelas horizontais apontando para a direita.

Exemplo Python

import numpy as np
import matplotlib.pyplot as plt
# Se estiver usando o jupyter notebook, use:
# %matplotlib inline
    
x = np.arange(0,10)
y = np.arange(0,10)
    
X, Y = np.meshgrid(x,y)
u = np.ones((10,10)) # x-component to the right
v = np.zeros((10,10)) # y-component zero
    
fig, ax = plt.subplots()
    
ax.streamplot(X,Y,u,v, density = 0.5)
ax.set_title('Gráfico de fluxo com linhas pararelas')
plt.show()

O gráfico contém linhas de fluxo paralelas, todas apontando para a direita.

Gráfico de fluxo de um campo

Podemos construir um gráfico de fluxo que mostra linhas de campo com base em um campo vetorial 2D definido.

Exemplo Python

import numpy as np
import matplotlib.pyplot as plt
# Se estiver usando o jupyter notebook, use:
# %matplotlib inline
    
x = np.arange(0,2.2,0.1)
y = np.arange(0,2.2,0.1)
    
X, Y = np.meshgrid(x, y)
u = np.cos(X)*Y
v = np.sin(y)*Y
    
fig, ax = plt.subplots()
    
ax.streamplot(X,Y,u,v, density = 1)
ax.axis([0.5,2.1,0,2])
ax.xaxis.set_ticks([])
ax.yaxis.set_ticks([])
ax.set_title('Gráfico de fluxo com linhas de campo')
    
plt.show()

Gráfico de setas com Matplotlib

Gráfico de aljavas

Um gráfico de aljavas é um tipo de gráfico 2D que mostra linhas vetoriais como setas. Os gráficos com setas são úteis na Engenharia Elétrica para visualizar o potencial elétrico e útil na Engenharia Mecânica para mostrar gradientes de tensão.

» Gráfico de aljavas com uma seta

Primeiro, criaremos um gráfico de aljavas simples que contém uma seta para demonstrar como o método quiver() da biblioteca Matplotlib funciona. O método quiver() usa quatro argumentos posicionais:

Exemplo Python

ax.quiver(x_pos, y_pos, x_direct, y_direct)

x_pos e y_pos são as posições iniciais da seta e x_direct, y_direct são as direções da seta (pra onde a seta aponta).
Nosso primeiro gráfico contém uma seta no ponto inicial x_pos = 0, y_pos = 0. A direção da flecha está apontando para cima e para a direita x_direct = 1, y_direct = 1.
O exemplo de código a baixo cria um gráfico de aljavas com apenas uma seta.

Exemplo Python

import numpy as np
import matplotlib.pyplot as plt
# se estiver usando o Jypyter notebook, use:
# %matplotlib inline
    
fig, ax = plt.subplots()
    
x_pos = 0
y_pos = 0
x_direct = 1
y_direct = 1
    
ax.quiver(x_pos, y_pos, x_direct, y_direct)
ax.set_title('Gráfico de aljavas com uma seta')
    
plt.show()

Executando o código acima o seu gráfico deve esta assim:

Nesse exemplo o gráfico de aljava contém uma seta. A seta começa no ponto 0, 0 e termina no ponto 1, 1.

» Gráfico de aljavas com duas setas

Agora vamos adicionar uma segunda seta ao gráfico de aljava passando em dois pontos de partida e duas direções de seta.
Manteremos nossa posição inicial da seta original na origem 0,0 e apontando para cima e para a direita (na direção 1,1). Definiremos uma segunda seta com uma posição inicial de -0,5,0,5, que aponta diretamente para baixo (na direção 0, -1).
Um argumento de palavra-chave adicional para adicionar o método quiver() é scale=5. Incluir o parâmetro scale=5 dimensiona o comprimento das setas, para que as setas pareçam mais longas e apareçam melhor no gráfico de aljava.
Para ver o início e o fim de ambas as setas, definiremos os limites do eixo entre -1,5 e 1,5 usando o método ax.axis() e passaremos uma lista dos limites do eixo no formato [xmin, xmax, ymin, ymax] .
Executando o exemplo abaixo, podemos ver duas setas. Uma seta aponta para o canto superior direito e a outra seta aponta para baixo.

Exemplo Python

import numpy as np
import matplotlib.pyplot as plt
# se estiver usando o Jypyter notebook, use:
# %matplotlib inline
    
fig, ax = plt.subplots()
    
x_pos = [0, 0]
y_pos = [0, 0]
x_direct = [1, 0]
y_direct = [1, -1]
    
ax.quiver(x_pos, y_pos, x_direct, y_direct, scale=5)
ax.axis([-1.5, 1.5, -1.5, 1.5])
    
plt.show()

Nesse exemplo podemos ver um gráfico com duas setas. Ambas as setas começam no ponto 0,0. Uma seta aponta para o canto superior direito, a outra seta aponta para baixo.

» Gráfico de aljavas usando uma grade de malha

Um gráfico de aljava com duas setas é um bom começo, mas é entediante e repetitivo adicionar as flechas de aljava uma a uma. Para criar uma superfície 2D completa de setas, utilizaremos a função meshgrid() da biblioteca NumPy.
Primeiro, precisamos criar um conjunto de matrizes que denotem as posições iniciais x e y de cada seta no gráfico. As matrizes da posição inicial da seta serão denominadas X e Y.
Podemos usar as posições iniciais da seta x, y para definir os componentes x e y de cada direção da seta. Chamaremos a direção da seta u e v. Para esse gráfico, definiremos a direção da seta com base no ponto inicial da seta da aljava usando as equações abaixo.

Xdireção = COS(Xposição_inicial)
Ydireção = SIN(Yposição_inicial)

A seção de código abaixo cria as matrizes das posições X e Y usando a função np.meshgrid() da biblioteca NumPy.

Exemplo Python

import numpy as np
import matplotlib.pyplot as plt
# se estiver usando o Jypyter notebook, use:
# %matplotlib inline
    
x = np.arange(0,2.2,0.2)
y = np.arange(0,2.2,0.2)
    
X, Y = np.meshgrid(x, y)
u = np.cos(X) * Y
v = np.sin(y) * Y

Em seguida, podemos construir o gráfico de aljava usando o método quiver() do Matplotlib. Lembre-se de que o método quiver() aceita quatro argumentos posicionais:

ax.quiver(x_pos, y_pos, x_direct, y_direct)

Nesse gráfico de aljava, x_pos e y_pos são matrizes 2D que contêm as posições iniciais das setas e x_direct, y_direct são matrizes 2D que contêm as direções das setas.
Os comandos ax.xaxis.set_ticks([]) e ax.yaxis.set_ticks([]) remove as marcas de seleção do eixo e ax.set_aspect('equal') define a proporção da plotagem para 1:1.

Exemplo Python

fig, ax = plt.subplots(figsize=(7,7))
ax.quiver(X,Y,u,v)
    
ax.xaxis.set_ticks([])
ax.yaxis.set_ticks([])
ax.axis([-0.2, 2.3, -0.2, 2.3])
ax.set_aspect('equal')
    
plt.show()

Agora vamos construir outro gráfico de aljava em que os componentes Î e ĵ (a direção) das setas de força, ⃗F dependem do ponto inicial da seta x, y de acordo com a função:

Novamente, usaremos a função meshgrid() do NumPy para criar as matrizes da posição inicial da seta e aplicar nossa função ⃗F às matrizes do ponto inicial da seta X e Y.

Exemplo Python

import numpy as np
import matplotlib.pyplot as plt
# se estiver usando o Jupyter notebook, use:
# %matplotlib inline
    
x = np.arange(-1,1,0.1)
y = np.arange(-1,1,0.1)
    
X, Y = np.meshgrid(x, y)
u = np.cos(X)*Y
v = np.sin(Y)*Y
    
X,Y = np.meshgrid(x,y)
    
u = X/5
v = -Y/5
    
fig, ax = plt.subplots(figsize=(9,9))
    
ax.quiver(X,Y,u,v)
    
ax.xaxis.set_ticks([])
ax.yaxis.set_ticks([])
ax.set_aspect('equal')
    
plt.show()