quinta-feira, 13 de agosto de 2020

Hash MD5 no Python

Imagem de TheDigitalWay por Pixabay

Os hashes criptográficos são muitos utilizados no dia-a-dia. Esse tipo de criptografia esta mais presente na sua vida do que você imagina. Por exemplo, eles são utilizados em assinaturas digitais, detecção de manipulação, códigos de autenticação de mensagens, impressões digitais, somas de verificação (verificação de integridade de mensagem ou arquivo), tabelas de hash, armazenamento de senha e muito mais. Os hashes têm mostrados a sua utilidade no envio de mensagens pela rede e para a segurança no armazenamento de senhas em servidores. Isso porque, a mensagem ou senha não fica salva diretamente no banco de dados, apenas o seu hash. Desse modo, mesmo que o banco de dados seja invadido as senhas estão salvas. Pelo menos em teoria (Nada é cem por cento seguro).
Para trabalhar com hash, python fornece várias funções no módulo hashlib. Nesse tutorial, vamos utilizar algumas dessas funções e explicar seu funcionamento do hash MD5.

MD5 Hash

A função de hash MD5 aceita sequências de bytes e retorna um valor hash de 128 bits. Esse hash é muito utilizado para verificar a integridade dos dados. Você já deve ter baixado algum arquivo, de um site, onde o hash MD5 é disponibilizado. Esse hash md5 é disponibilizado para que você possa ter certeza que o arquivo que você baixou é o mesmo que você queria. Geralmente são sites de torrents que fazem isso.
Essas são as funções que vamos utilizar para criar um hash md5:

  • encode(): Converte a string em bytes para ser aceitável pela função hash.
  • digest(): Retorna os dados codificados em formato de byte.
  • hexdigest(): Retorna os dados codificados em formato hexadecimal.

No exemplo abaixo, vamos passar um valor em bytes e vamos receber uma saída em bytes:

import hashlib 
  
# codificando a string Acervo Lima usando a função de hash md5
hash = hashlib.md5(b'Acervo Lima') 
  
# imprimir o equivalente em bytes. 
print("Equivalente em bytes ao hash: ", hash.digest())

No exemplo, chamamos a função md5() passando um valor em bytes. A função md5() faz a codificação dessa string e, depois, chamamos a função digest(). A função digest() retorna o resultado da função md5() em bytes.
Mas os hashes que vemos por ai não são disponibilizados em bytes, mas sim em hexadecimal. No exemplo abaixo, vamos retornar um hash md5 em hexadecimal.

>>> string = 'Acervo Lima'.encode()
>>> hash = hashlib.md5(string)
>>> print('O equivalente em hexadecimal ao hash: ',
... hash.hexdigest())
O equivalente em hexadecimal ao hash:  409b83920744582bf8b286e4e63e2207

Nesse exemplo, convertemos a string 'Acervo Lima' em bytes usando a função encode(). Depois, chamamos a função md5() para obter o hash md5 em bytes. E, em seguida, chamamos a função hexdigest(). A função hexdigest() converte os bytes em hexadecimal.

Referência:
Módulo hashlib

quarta-feira, 12 de agosto de 2020

concat() e append() do pandas, como usar

Imagem de Steve Buissinne por Pixabay

A biblioteca pandas possui várias facilidades para juntar séries e dataframes com outros tipos de conjuntos de dados. Nesse tutorial vamos aprender algumas dessas facilidades. Os métodos mais utilizados para concatenar uma série ou dataframe é o concat() e o append().

Concatenando Séries e DataFrames com o método concat

A função concat() é utilizada para concatenar (juntar) séries ou dataframes ao longo de um eixo. Com o método concat() podemos definir o eixo em que o método deve operar, isso no caso dos dataframes visto que as séries possuem somente um eixo. Antes de nos aprofundarmos nos parâmetros e peculiaridades desse método, vamos ver um exemplo simples de como o método opera.

>>> import pandas as pd
>>> dados1 = {
... 'A': ['A0', 'A1', 'A2', 'A3'],
... 'B': ['B0', 'B1', 'B2', 'B3'],
... 'C': ['C0', 'C1', 'C2', 'C3'],
... 'D': ['D0', 'D1', 'D2', 'D3']
... }
>>> dados2 = {
... 'A': ['A4', 'A5', 'A6', 'A7'],
... 'B': ['B4', 'B5', 'B6', 'B7'],
... 'C': ['C4', 'C5', 'C6', 'C7'],
... 'D': ['D4', 'D5', 'D6', 'D7']
... }
>>> dados3 = {
... 'A': ['A8', 'A9', 'A10', 'A11'],
... 'B': ['B8', 'B9', 'B10', 'B11'],
... 'C': ['C8', 'C9', 'C10', 'C11'],
... 'D': ['D8', 'D9', 'D10', 'D11']
... }
>>> df1 = pd.DataFrame(dados1, index=[0, 1, 2, 3])
>>> df2 = pd.DataFrame(dados1, index=[4, 5, 6, 7])
>>> df3 = pd.DataFrame(dados1, index=[8, 9, 10, 11])
>>> frames = [df1, df2, df3]
>>> resultado = pd.concat(frames)
>>> resultado
     A   B   C   D
0   A0  B0  C0  D0
1   A1  B1  C1  D1
2   A2  B2  C2  D2
3   A3  B3  C3  D3
4   A0  B0  C0  D0
5   A1  B1  C1  D1
6   A2  B2  C2  D2
7   A3  B3  C3  D3
8   A0  B0  C0  D0
9   A1  B1  C1  D1
10  A2  B2  C2  D2
11  A3  B3  C3  D3

O método concat() recebe uma lista ou dicionário e os junta de acordo com o eixo.
Sem contesto o exemplo acima deve ficar um pouco mais complicado de entender. Vamos voltar a ele. Digamos que você queira associar um rótulo para cada dataframe que foi concatenado. Assim fica mais fácil de saber onde cada dataframe foi posicionado. Podemos fazer isso passando os rótulos para o parâmetro keys.

>>> resultado = pd.concat(frames, keys=['X', 'Y', 'Z'])
>>> resultado
       A   B   C   D
X 0   A0  B0  C0  D0
  1   A1  B1  C1  D1
  2   A2  B2  C2  D2
  3   A3  B3  C3  D3
Y 4   A0  B0  C0  D0
  5   A1  B1  C1  D1
  6   A2  B2  C2  D2
  7   A3  B3  C3  D3
Z 8   A0  B0  C0  D0
  9   A1  B1  C1  D1
  10  A2  B2  C2  D2
  11  A3  B3  C3  D3

Adicionando esses rótulos criamos um índice hierárquico. Com essa vantagem podemos selecionar cada bloco pelo seu rótulo usando o atributo loc.

>>> resultado.loc['X']
    A   B   C   D
0  A0  B0  C0  D0
1  A1  B1  C1  D1
2  A2  B2  C2  D2
3  A3  B3  C3  D3

Com esse exemplo, você já pode ver como esse atributo pode ser útil. Para saber mais sobre o atributo loc, veja nosso tutorial sobre Seleção de subconjuntos de dados.

Definindo o critério de concatenação dos eixos

Os DataFrames pandas possuem dois eixos, o eixo de índice (eixo 0) e o eixo das colunas (eixo 1). Quando juntamos dois ou mais dataframes temos que decidir o que fazer com os outros eixos (além do que estamos concatenando). As opções que temos são as seguintes:

  • join='outer': Fazer a união de todos os eixos. Esse é o valor padrão. Essa opção evita perdas de dados.
  • join='inner': Fazer uma interseção dos eixos.

Vamos ver exemplos de como funciona cada uma das opções.

>>> dados4 = {
...     'B': ['B2', 'B3', 'B6', 'B7'],
...     'D': ['D2', 'D3', 'D6', 'D7'],
...     'F': ['F2', 'F3', 'F6', 'F7']
... }
>>> df4 = pd.DataFrame(dados4, index=[2, 3, 6, 7])
>>> resultado = pd.concat([df1, df4], axis=1, join='outer')
>>> resultado
     A    B    C    D    B    D    F
0   A0   B0   C0   D0  NaN  NaN  NaN
1   A1   B1   C1   D1  NaN  NaN  NaN
2   A2   B2   C2   D2   B2   D2   F2
3   A3   B3   C3   D3   B3   D3   F3
6  NaN  NaN  NaN  NaN   B6   D6   F6
7  NaN  NaN  NaN  NaN   B7   D7   F7

Nesse exemplo, usamos dois dataframes com o número de índices e colunas diferentes. Isso para ficar claro que passando o valor ‘outer’, para o parâmetro join, o método concat() vai tentar organizar os dois dataframes de uma forma que nenhum dado seja perdido. Se você olhar atentamente vai ver que o segundo dataframe (df4) começa no índice dois e na coluna segunda coluna B. Como o primeiro dataframe (df1) possui o mesmo índice não foi preciso criar um novo. Isso não acontece com a coluna B, não da pra encaixar essas colunas. Então novas são criadas. Por isso temos elementos vazios no dataframe resultado. Se o parâmetro join for omitido, o resultado é o mesmo porque o valor padrão desse parâmetro é ‘outer’.
Agora, vamos ver um exemplo passando o valor ‘inner’:

>>> resultado = pd.concat([df1, df4], axis=1, join='inner')
>>> resultado
    A   B   C   D   B   D   F
2  A2  B2  C2  D2  B2  D2  F2
3  A3  B3  C3  D3  B3  D3  F3

Nesse exemplo, podemos ver que perdemos muitos dados. Isso porque, quando passamos o valor ‘inner’ para o parâmetro join, estamos dizendo para o método que queremos apenas os dados dos índices que os dois dataframes tem em comum (no caso, 2 e 3). Você pode verificar que todos os dados dos índices 2 e 3, dos dois dataframes (df1 e df4), foram concatenados e os dados dos outro índices foram perdidos.
Nos dois últimos exemplos, concatenamos os dataframes no eixo 1 (colunas), mas podemos usar o eixo 0 (índice). Nesse caso, no primeiro exemplo o método concat() vai tentar unir as colunas. E no segundo exemplo, apenas as colunas em comum seriam concatenadas (B e D).

>>> resultado = pd.concat([df1, df4], axis=0, join='outer')
>>> resultado
     A   B    C   D    F
0   A0  B0   C0  D0  NaN
1   A1  B1   C1  D1  NaN
2   A2  B2   C2  D2  NaN
3   A3  B3   C3  D3  NaN
2  NaN  B2  NaN  D2   F2
3  NaN  B3  NaN  D3   F3
6  NaN  B6  NaN  D6   F6
7  NaN  B7  NaN  D7   F7
>>> resultado = pd.concat([df1, df4], axis=0, join='inner')
>>> resultado
    B   D
0  B0  D0
1  B1  D1
2  B2  D2
3  B3  D3
2  B2  D2
3  B3  D3
6  B6  D6
7  B7  D7

O nosso dataframe, resultante da concatenação de dois dataframes, pode ter a mesma quantidade de linhas do primeiro dataframe, e o excedente (do segundo dataframe) é perdido.

>>> resultado = pd.concat([df1, df4], axis=1).reindex(df1.index)
>>> resultado
    A   B   C   D    B    D    F
0  A0  B0  C0  D0  NaN  NaN  NaN
1  A1  B1  C1  D1  NaN  NaN  NaN
2  A2  B2  C2  D2   B2   D2   F2
3  A3  B3  C3  D3   B3   D3   F3

Usando o método append do pandas

Se você deseja concatenar dataframes somente no eixo 0 (índice), uma escolha interessante é o método append. Esse método concatena as series e os dataframes ao longo do eixo 0.

>>> resultado = df1.append(df2)
>>> resultado
    A   B   C   D
0  A0  B0  C0  D0
1  A1  B1  C1  D1
2  A2  B2  C2  D2
3  A3  B3  C3  D3
4  A0  B0  C0  D0
5  A1  B1  C1  D1
6  A2  B2  C2  D2
7  A3  B3  C3  D3

Você pode passar uma lista de objetos para serem concatenados.

>>> resultado = df1.append([df2, df3])
>>> resultado
     A   B   C   D
0   A0  B0  C0  D0
1   A1  B1  C1  D1
2   A2  B2  C2  D2
3   A3  B3  C3  D3
4   A0  B0  C0  D0
5   A1  B1  C1  D1
6   A2  B2  C2  D2
7   A3  B3  C3  D3
8   A0  B0  C0  D0
9   A1  B1  C1  D1
10  A2  B2  C2  D2
11  A3  B3  C3  D3

Ignorando índices

Você pode ignorar os índices completamente passando o valor True para o parâmetro ignore_index (Se esses dados não forem importantes). Desse modo, o método vai concatenar os dataframes de acordo com as colunas. Por exemplo, se os dois dataframes têm uma coluna chamada B, essas colunas ficaram uma embaixo da outra.

>>> resultado = pd.concat([df1, df4], ignore_index=True)
>>> resultado
     A   B    C   D    F
0   A0  B0   C0  D0  NaN
1   A1  B1   C1  D1  NaN
2   A2  B2   C2  D2  NaN
3   A3  B3   C3  D3  NaN
4  NaN  B2  NaN  D2   F2
5  NaN  B3  NaN  D3   F3
6  NaN  B6  NaN  D6   F6
7  NaN  B7  NaN  D7   F7

O mesmo parâmetro funciona no método append().

Concatenando séries com dataframes

Você pode concatenar séries com dataframes. O método concat() pode trabalhar com os dois tipos. Nesse exemplo, vamos criar uma série e concatená-la com um dataframe. Essa série será adicionada como uma coluna no final do dataframe.

>>> s1 = pd.Series(['X0', 'X1', 'X2', 'X3'], name='X')
>>> resultado = pd.concat([df1, s1], axis=1)
>>> resultado
    A   B   C   D   X
0  A0  B0  C0  D0  X0
1  A1  B1  C1  D1  X1
2  A2  B2  C2  D2  X2
3  A3  B3  C3  D3  X3

Se a série não tiver um nome, será atribuído uma sequencia de números.

>>> s2 = pd.Series(['_0', '_1', '_2', '_3'])
>>> resultado = pd.concat([df1, s2, s2, s2], axis=1)
>>> resultado
    A   B   C   D   0   1   2
0  A0  B0  C0  D0  _0  _0  _0
1  A1  B1  C1  D1  _1  _1  _1
2  A2  B2  C2  D2  _2  _2  _2
3  A3  B3  C3  D3  _3  _3  _3

Podemos usar o parâmetro ignore_index e definir os rótulos das colunas com uma sequencia numérica desde a primeira coluna.

>>> resultado = pd.concat([df1, s2, s2, s2], axis=1, ignore_index=True)
>>> resultado
    0   1   2   3   4   5   6
0  A0  B0  C0  D0  _0  _0  _0
1  A1  B1  C1  D1  _1  _1  _1
2  A2  B2  C2  D2  _2  _2  _2
3  A3  B3  C3  D3  _3  _3  _3

Concatenando com um grupo de keys

O padrão é o novo dataframe herdar os nomes das colunas dos dataframes pais. Podemos mudar isso com o parâmetro keys. O uso mais comum do parâmetro keys é exatamente esse: substituir o nome das colunas.

>>> pd.concat([s3, s4, s5], axis=1)
   um  dois  tres
0   0     0     1
1   1     1     4
2   2     1     1
3   3     3     4
>>> pd.concat([s3, s4, s5], axis=1, keys=['X', 'Y', 'Z'])
   X  Y  Z
0  0  0  1
1  1  1  4
2  2  1  1
3  3  3  4

Podemos fazer a mesma coisa com um dicionário.

>>> dicio = {'X':s3, 'Y':s4, 'Z':s5}
>>> resultado = pd.concat(dicio, axis=1)
>>> resultado
   X  Y  Z
0  0  0  1
1  1  1  4
2  2  1  1
3  3  3  4

Referência:
Função concat()
Função append()

Como usar a função groupby() do pandas

Photo by Duy Pham on Unsplash

Python é uma ótima linguagem para fazer análise de dados, principalmente por causa do fantástico ecossistema de pacotes python desenvolvidos para a análise de dados. pandas é um desses pacotes. Com ele podemos importar dados de um dataset e realizar algumas analises facilmente, como agrupar dados. O método groupby é muito utilizado para esse proposito.
O que o método groupby faz é separar os dados por grupos. Com os dados agrupados é comum aplicar uma função e combinar o resultado. Por exemplo, a velocidade de um falcão-peregrino vária de 370km/h à 390km/h e um papagaio de 24km/h à 26km/h. Com esses dados podemos usar o método groupby (para agrupar os dados) e o método mean (média) para saber qual é a velocidade média de cada ave.
Com os dados de cada ave, o método groupby vai agrupar os dados por categoria, no caso a categoria é o animal. Com esses dados agrupados podemos chamar o método mean(), que fornece a média dos valores. Desse modo temos um dataframe com a velocidade média de cada animal.

>>> import pandas as pd
>>> dados = {
... 'Animal': ['Falcao', 'Papagaio', 'Falcao', 'Papagaio'],
... 'Velocidade_maxima':[390, 24, 370, 26]
... }
>>> df = pd.DataFrame(dados)
>>> df
     Animal  Velocidade_maxima
0    Falcao                390
1  Papagaio                 24
2    Falcao                370
3  Papagaio                 26

>>> df.groupby(['Animal']).mean()
          Velocidade_maxima
Animal
Falcao                  380
Papagaio                 25

Quer aprender mais sobre a biblioteca pandas? Comece com nossos tutoriais pandas.

Referência:
Função groupby()
Função DataFrame()

Como usar o método fillna() do pandas

Photo by Brett Jordan on Unsplash

A primeira linguagem de programação que vem a mente, quando o assunto é analise de dados, é a linguagem python. Isso por que python fornece um ecossistema de pacotes para a análise de dados. pandas é um desses pacotes. Com ele podemos importar dados de um dataset e realizar algumas analises facilmente.
Um problema comum, quando importamos dados de um dataset, é elementos com valores NaN (sem valor). Esse valor pode causar problemas e erros na analise de dados, por isso pode ser interessante colocar um valor para esses elementos.
Nesse tutorial, vamos aprender a substituir valores NaN dos dataframes pandas utilizando o método fillna(). O método fillna() pode preencher todos os elementos vazios com um valor definido.
Para começar, vamos precisar de um dataframe com elementos vazios.

>>> import pandas as pd
>>> import numpy as np
>>> dados = {
... 'A':[np.nan, 4, 8, np.nan],
... 'B':[9, np.nan, 6, np.nan],
... 'C':[np.nan, 1, np.nan, 9],
... 'D':[np.nan, np.nan, 8, 3]
... }
>>> df = pd.DataFrame(dados)
>>> df
     A    B    C    D
0  NaN  9.0  NaN  NaN
1  4.0  NaN  1.0  NaN
2  8.0  6.0  NaN  8.0
3  NaN  NaN  9.0  3.0

O funcionamento do método fillna() é bem simples, só precisamos passar para o método qual é o valor que vai substituir o valor NaN. Assim o método vai procurar por todo o dataframe, e onde encontrar um elemento vazio, ele vai preencher esse elemento com o valor que recebeu.

>>> df.fillna(2)
     A    B    C    D
0  2.0  9.0  2.0  2.0
1  4.0  2.0  1.0  2.0
2  8.0  6.0  2.0  8.0
3  2.0  2.0  9.0  3.0

Também podemos utilizar os valores do próprio dataframe para preencher os elementos vazios. Por exemplo, se quiséssemos preencher o elemento vazio com o último valor não vazio, passaríamos o valor 'ffill' (forward fill) para o parâmetro method. Outra meio de preencher é com o próximo valor não vazio da sequência, nesse caso passamos o valor 'bfill' (back fill) para o parâmetro method.

>>> dados = [
... [None, 2, 6],
... [1, None, None],
... [None, 3, None]
... ]
>>> df = pd.DataFrame(dados)
>>> df
     0    1    2
0  NaN  2.0  6.0
1  1.0  NaN  NaN
2  NaN  3.0  NaN

>>> df.fillna(method='ffill')
     0    1    2
0  NaN  2.0  6.0
1  1.0  2.0  6.0
2  1.0  3.0  6.0

>>> df.fillna(method='bfill')
     0    1    2
0  1.0  2.0  6.0
1  1.0  3.0  NaN
2  NaN  3.0  NaN

Também podemos definir o valor que será usado para preencher cada coluna. Por exemplo, você pode definir que os elementos vazios da coluna A serão preenchidos com o valor 'um' e da coluna D com o valor 'dois'.

>>> replace = {'A':'um', 'D':'dois'}
>>> df.fillna(value=replace)
    A    B    C     D
0   1  NaN  5.0  dois
1  um  2.0  NaN     6
2   3  NaN  8.0  dois
3  um  4.0  NaN     9

Um parâmetro, que pode ser útil, é o limit. Com ele podemos definir quantos elementos vazios serão substituídos. Por exemplo, se você quiser que apenas um elementos de cada coluna seja substituído passe o valor 1 para o parâmetro limit.

>>> df.fillna(value=replace, limit=1)
     A    B    C     D
0    1  NaN  5.0  dois
1   um  2.0  NaN     6
2    3  NaN  8.0   NaN
3  NaN  4.0  NaN     9

Repare que apenas um elemento vazio de cada coluna foi substituído.
Quer aprender mais sobre a biblioteca pandas? Comece com nossos tutoriais pandas.

Referência:
Função fillna()

terça-feira, 11 de agosto de 2020

Seleção de subconjuntos de dados

Photo by Thanos Pal on Unsplash

Introdução

As dimensões de uma série ou dataframe tem seu rotulo no objeto index. O objeto index é usado para separar as estruturas do pandas. São os indexs que rotulam cada linha e coluna, com isso podemos selecionar os dados do dataframe. A biblioteca pandas também permite selecionar dados de acordo com a posição das colunas e linhas. Podendo selecionar dados por nome ou números (posição), a biblioteca pandas fornece uma sintaxe poderosa. O que acaba torna a seleção de subconjuntos um pouco confusa.
Já vimos exemplos de seleção de dados por rótulo e posição. A lista e o dicionário do python fazem isso. Esse é um dos motivos da biblioteca pandas ser poderosa. Juntando a capacidade de acessar dados por rótulo (dicionário) e posição (lista) temos um tipo de dado muito versátil.

Seleção de dados da série com pandas

As séries e os dataframes pandas funcionam como contêiner de dados. Esses contêineres têm métodos e atributos que nos permitem selecionar os dados de várias maneiras. Os métodos e atributos fazer operações com índices para selecionar os dados. Também podemos usar os atributos iloc e loc.
Podemos selecionar dados informando a posição ou um rótulo. Usando o atributo iloc selecionamos os dados por posição, fazendo um objeto do pandas parecer com uma lista. Já usando o atributo loc, selecionamos os dados informando um rótulo, como um dicionário do python.
Esses dois atributos estão presentes nas séries e nos dataframes. Nesse tutorial vamos aprender a selecionar dados utilizando rótulos e posições. Os dois atributos aceitam valores escalares e listas.
Colocando colchetes depois de uma variável estamos fazendo uma operação de índice. Não faz diferença se é uma seleção com rótulo ou posição, tudo é operação de índice.
Você obter o dataset usado nesse tutorial aqui.

Selecionando dados utilizando rótulo e posição

Vamos carregar o dataset na memoria e selecionar uma coluna usando uma operação de index. Nesse primeiro exemplo vamos usar o rótulo da coluna.

>>> import pandas as pd
>>> animelist = pd.read_csv(‘animelist.csv’)
>>> animelist['title']
0                                Inu x Boku SS
1                             Seto no Hanayome
2                           Shugo Chara!! Doki
3                                Princess Tutu
4                          Bakuman. 3rd Season
                         ...                  
14473               Gutchonpa Omoshiro Hanashi
14474                     Geba Geba Shou Time!
14475              Godzilla: Hoshi wo Kuu Mono
14476    Nippon Mukashibanashi: Sannen Netarou
14477               Senjou no Valkyria Special
Name: title, Length: 14478, dtype: object

Podemos ter o mesmo resultado com o atributo loc.

>>> animelist.loc[:, 'title']
0                                Inu x Boku SS
1                             Seto no Hanayome
2                           Shugo Chara!! Doki
3                                Princess Tutu
4                          Bakuman. 3rd Season
                         ...                  
14473               Gutchonpa Omoshiro Hanashi
14474                     Geba Geba Shou Time!
14475              Godzilla: Hoshi wo Kuu Mono
14476    Nippon Mukashibanashi: Sannen Netarou
14477               Senjou no Valkyria Special
Name: title, Length: 14478, dtype: object

Com o atributo loc podemos selecionar linhas e colunas passando o rótulo do índice. Se apenas um valor for passado o atributo vai retornar uma linha com os dados de cada coluna. Se passamos dois valores podemos selecionar as linhas e as colunas, e ainda podemos definir o espaçamento.

# selecionando uma linha
# passamos um número inteiro porque o índice é de inteiros e não strings
>>> animelist.loc[12]
anime_id                                                      11123
title                                          Sekaiichi Hatsukoi 2
title_english     Sekai Ichi Hatsukoi - World's Greatest Fi...
title_japanese                                              世界一初恋 2
title_synonyms    Sekai-ichi Hatsukoi 2, Sekai'ichi Hatsukoi 2
image_url         https://myanimelist.cdn-dena.com/images/anime/...
type                                                             TV
source                                                        Manga
episodes                                                         12
status                                              Finished Airing
airing                                                        False
aired_string                            Oct 8, 2011 to Dec 24, 2011
aired                    {'from': '2011-10-08', 'to': '2011-12-24'}
duration                                            24 min. per ep.
rating                                    PG-13 - Teens 13 or older
score                                                          8.23
scored_by                                                     53285
rank                                                            288
popularity                                                      945
members                                                       89543
favorites                                                       726
background        An event to show ep.1 of season 2 (along with ...
premiered                                                 Fall 2011
broadcast                                                   Unknown
related           {'Adaptation': [{'mal_id': 10309, 'type': 'man...
producer                   Lantis, Kadokawa Shoten, AT-X, KlockWorx
licensor                                                 Funimation
studio                                                  Studio Deen
genre                            Comedy, Drama, Romance, Shounen Ai
opening_theme     ['"Sekai no Hate ni Kimi ga Itemo" (世界の果てに君がいて...
ending_theme             ['"Aikotoba" (アイコトバ) by Sakura Merry-Men']
Name: 12, dtype: object

>>> animelist.loc[12:32, 'title':'rank']
                     title  ...    rank
12    Sekaiichi Hatsukoi 2  ...   288.0
13  Tonari no Kaibutsu-kun  ...  1093.0
14                  Bleach  ...   693.0
15                 Chobits  ...  1546.0
16          Kimi ni Todoke  ...   400.0
17      Naruto: Shippuuden  ...   337.0
18                 Ranma ½  ...   802.0
19               Toradora!  ...   174.0
20     Bakuman. 2nd Season  ...   108.0
21            Gakuen Alice  ...   941.0
22              Skip Beat!  ...   282.0
23           Chihayafuru 2  ...   116.0
24        Shounen Onmyouji  ...  1158.0
25             Chihayafuru  ...   246.0
26         Itazura na Kiss  ...  1108.0
27               Beelzebub  ...   528.0
28   Hanasakeru Seishounen  ...   753.0
29              SKET Dance  ...   213.0
30            B-gata H-kei  ...  3193.0
31        Crayon Shin-chan  ...  1032.0
32           Fruits Basket  ...   939.0

[21 rows x 17 columns]

No ultimo exemplo selecionamos linhas e colunas. Selecionamos os dados da linha numero doze até a linha trinta e dois. As colunas, selecionamos da coluna title até a coluna rank. O atributo iloc funciona do mesmo modo, a diferença é que esse atributo não aceita rótulos. O atributo iloc só aceita posições.

>>> animelist.iloc[12:32, 1:17]
                     title  ... scored_by
12    Sekaiichi Hatsukoi 2  ...     53285
13  Tonari no Kaibutsu-kun  ...    281448
14                  Bleach  ...    433097
15                 Chobits  ...    175388
16          Kimi ni Todoke  ...    212710
17      Naruto: Shippuuden  ...    385179
18                 Ranma ½  ...     59911
19               Toradora!  ...    557898
20     Bakuman. 2nd Season  ...    122090
21            Gakuen Alice  ...     33244
22              Skip Beat!  ...     90699
23           Chihayafuru 2  ...     62720
24        Shounen Onmyouji  ...     14619
25             Chihayafuru  ...     89081
26         Itazura na Kiss  ...     91187
27               Beelzebub  ...    136105
28   Hanasakeru Seishounen  ...     16772
29              SKET Dance  ...     44132
30            B-gata H-kei  ...    142827
31        Crayon Shin-chan  ...     17683

[20 rows x 16 columns]

Para selecionar, do primeiro elemento até um determinado, é só omitir a posição ou rótulo de inicio. Por exemplo: animelist.iloc[:32, 1:17]. Se você quiser definir o inicio e selecionar até o ultimo elemento é só omitir a posição ou rotulo do último elemento: animelist.loc[12:, ‘rank’:].

Referência:
Atributo loc
Atributo iloc

Agrupando e ordenando com pandas

Photo by UX Indonesia on Unsplash

O agrupamento e a classificação de dados é uma das tarefas mais básicas durante a análise de dados. É comum selecionar linhas que tenham o maior valor dentro de uma coluna e classificar esses valores em grupos. Por exemplo, se você precisar saber qual foi o filme de 2016 com maior audiência ou qual é a classificação de filme que arrecada mais com bilheteria, isso seria feito classificando os grupos. Primeiro é preciso separar os filmes por ano e depois selecionar os filmes com maior bilheteria.
Nesse tutorial, vamos descobrir qual é o anime com o maior rank entre os animes com os scores mais altos.

Como agrupar e ordenar com pandas

Primeiro vamos carregar o dataset e selecionar as colunas que nos interessa: title, rank e score. O dataset que vamos utilizar pode ser baixado aqui.

>>> import pandas as pd
>>> animelist = pd.read_csv('animelist.csv')
>>> animelist = animelist[['title', 'score', 'rank']]
>>> animelist
                                       title  score     rank
0                              Inu x Boku SS   7.63   1274.0
1                           Seto no Hanayome   7.89    727.0
2                         Shugo Chara!! Doki   7.55   1508.0
3                              Princess Tutu   8.21    307.0
4                        Bakuman. 3rd Season   8.67     50.0
...                                      ...    ...      ...
14473             Gutchonpa Omoshiro Hanashi   5.50  10337.0
14474                   Geba Geba Shou Time!   4.60  10236.0
14475            Godzilla: Hoshi wo Kuu Mono   0.00      NaN
14476  Nippon Mukashibanashi: Sannen Netarou   6.00  11557.0
14477             Senjou no Valkyria Special   5.15  12056.0

[14478 rows x 3 columns]

Vamos utilizar o método sort_values() para ordenar o dataframe por score. Esse método vai ordenar todo o dataframe começando do menor para o maior. Assim as primeiras linhas serão os animes com os menores score. Para inverter isso passamos o valor False para o parâmetro ascending.

>>> animelist.sort_values(by='score', ascending=False)
                                                   title  score     rank
14407                                         Ittekimasu  10.00  10657.0
14317  Kuunyan no Koutsuu Anzen: Tadashii Jitensha no...  10.00  11071.0
548    Manichi ga Tsurai Kimochi Wakarimasu ka: Yurus...  10.00  11178.0
14467               Kyouiku Eiga-sai Jushou Anime Series  10.00  11091.0
14403                            Dokidoki Little Ooyasan   9.52      NaN
...                                                  ...    ...      ...
6557                                 Asagao to Kase-san.   0.00      NaN
6614                              Boogiepop wa Warawanai   0.00      NaN
14067                             BanG Dream! 2nd Season   0.00      0.0
12964                       Katsugeki/Touken Ranbu Movie   0.00      NaN
12039                           Moshimoshi, Terumi Desu.   0.00      NaN

[14478 rows x 3 columns]

No exemplo acima, os dados foram ordenados de acordo com o score, do maior para o menor. Para ordenar várias colunas, vamos usar uma lista com os nomes das colunas que queremos ordenar.

>>> animelist.sort_values(by=['score', 'rank'], ascending=False)
    title  score     rank
548    Manichi ga Tsurai Kimochi Wakarimasu ka: Yurus...  10.00  11178.0
14467               Kyouiku Eiga-sai Jushou Anime Series  10.00  11091.0
14317  Kuunyan no Koutsuu Anzen: Tadashii Jitensha no...  10.00  11071.0
14407                                         Ittekimasu  10.00  10657.0
14403                            Dokidoki Little Ooyasan   9.52      NaN
...                                                  ...    ...      ...
14414                             Shinya! Tensai Bakabon   0.00      NaN
14416                          Chikyuugai Shounen Shoujo   0.00      NaN
14417                           Uchuu no Hou: Reimei-hen   0.00      NaN
14463                                          Space Bug   0.00      NaN
14475                        Godzilla: Hoshi wo Kuu Mono   0.00      NaN

[14478 rows x 3 columns]

Note que as duas colunas foram ordenadas do maior para o menor. A biblioteca pandas seleciona o score mais alto e depois seleciona os ranks mais altos com o mesmo score. Desse modo, agrupamos os animes de acordo com o score e o rank. Assim obtemos a informação de que o anime com maior rank, entre os animes que tem o maior score, é o Manichi ga Tsurai. Podemos obter um par score/rank dos maiores valores de cada score diferente chamando o método drop_duplicates().

>>> (
... animelist.sort_values(by=['score', 'rank'], ascending=False)
... .drop_duplicates(subset='score')
... )
                                                   title  score     rank
548    Manichi ga Tsurai Kimochi Wakarimasu ka: Yurus...  10.00  11178.0
14403                            Dokidoki Little Ooyasan   9.52      NaN
14454           Tat-chan - Momo-chan no Fushigina Taiken   9.50  12430.0
2555                    Fullmetal Alchemist: Brotherhood   9.25      1.0
3129                                   Okaachan Gomen ne   9.20  11595.0
...                                                  ...    ...      ...
12844                                         Dragon Egg   1.50  10028.0
14420      Cofun Gal no Coffy (TV): Christmas Daisakusen   1.33   9959.0
14393                          Atashi Tenshi Anata Akuma   1.25   9712.0
13955                                         Woo to Wah   1.00  12678.0
14425                        Wo Shi Jiang Xiaobai (2018)   0.00  12719.0

[630 rows x 3 columns]

Por padrão, o método drop_duplicates() pega o primeiro valor. Podemos mudar esse comportamento passando o valor 'last' para o parâmetro keep.

>>> (
... animelist.sort_values(by=['score', 'rank'], ascending=False)
... .drop_duplicates(subset='score', keep='last')
... )
                                               title  score     rank
14407                                     Ittekimasu  10.00  10657.0
14403                        Dokidoki Little Ooyasan   9.52      NaN
14208                                   Hashire John   9.50  10357.0
2555                Fullmetal Alchemist: Brotherhood   9.25      1.0
3129                               Okaachan Gomen ne   9.20  11595.0
...                                              ...    ...      ...
12844                                     Dragon Egg   1.50  10028.0
14420  Cofun Gal no Coffy (TV): Christmas Daisakusen   1.33   9959.0
14393                      Atashi Tenshi Anata Akuma   1.25   9712.0
14187                               Gomen ne Chitchi   1.00  10256.0
14475                    Godzilla: Hoshi wo Kuu Mono   0.00      NaN

[630 rows x 3 columns]

Referência:
Método sort_values()

segunda-feira, 10 de agosto de 2020

Selecionando o menor dos maiores

Photo by Jon Tang on Unsplash

O que vamos fazer nesse tutorial é parecido com as manchetes de noticias cativantes, como das cem melhores universidades, estas cinco tem a menor nota de corte ou dos cinquenta melhores países para morar, estes três são os mais fáceis de conseguir visto.
Uma coisa comum na analise de dados é agrupar dados e depois fazer uma busca nesse grupo. Imagine que você precisar dos n-maiores valores de uma colunas e depois selecionar os n-menores valores desse grupo que você acabou de selecionar.
Nesse tutorial vamos selecionar um grupo de dados e depois selecionar outro grupo de dados a partir dos dados selecionados na primeira seleção. Esse tipo de seleção é utilizado para refinar os resultados. Como analisar gigabytes de dados não é uma tarefa fácil devemos diminuir as variáveis no processo de analise de dados.

Como selecionar o menor dos maiores com pandas

Nesse exemplo vamos obter os cinco animes com o menor rank entre os cem animes com os maiores score. O dataset que vamos usar pode ser obtido aqui.

>>> import pandas as pd
>>> anime = pd.read_csv('animelist.csv')
>>> anime = anime[['title', 'score', 'rank']]
>>> anime.nlargest(100, 'score').nsmallest(5, 'rank')
                                 title  score  rank
2555  Fullmetal Alchemist: Brotherhood   9.25   1.0
1028                    Kimi no Na wa.   9.19   2.0
1252                          Gintama°   9.15   3.0
6177                     Steins;Gate 0   9.15   4.0
2999                       Steins;Gate   9.14   5.0

Como funciona...

Primeiro selecionamos as colunas que nos interessa: title, score e rank. Depois selecionamos as cem linhas com o score mais alto, em seguida selecionamos as cinco linhas com o rank mais baixo. Desse jeito selecionamos os cinco animes com menor rank entre os cem animes com o maior score.

Referência:
Método nlargest()
Método nnsmallest()