Feltrin, Fernando - Análise Exploratória de Dados Com Python, Pandas e Numpy - 2021
Feltrin, Fernando - Análise Exploratória de Dados Com Python, Pandas e Numpy - 2021
ANÁLISE EXPLORATÓRIA DE
DADOS COM PYTHON, PANDAS
E NUMPY
Fernando Feltrin
AVISOS
SOBRE O AUTOR
REDES SOCIAIS
https://github.com/fernandofeltrin
https://github.com/fernandofeltrin
ÍNDICE
Sumário
CAPA
AVISOS
SOBRE O AUTOR
LIVROS
CURSO
REDES SOCIAIS
ÍNDICE
INTRODUÇÃO
BIBLIOTECA NUMPY
Sobre a biblioteca Numpy
Instalação e importação das dependências
ARRAYS
Criando uma array numpy
Criando uma array gerada com números ordenados
Array gerada com números do tipo float
Array gerada com números do tipo int
Array gerada com números zero
Array gerada com números um
Array gerada com espaços vazios
Criando uma array de números aleatórios, mas com tamanho predefinido
em variável
Criando arrays de dimensões específicas
Array unidimensional
Array bidimensional
Array tridimensional
Verificando o tamanho e formato de uma array
Verificando o tamanho em bytes de um item e de toda a array
Verificando o elemento de maior valor de uma array
Consultando um elemento por meio de seu índice
Consultando elementos dentro de um intervalo
Modificando manualmente um dado/valor de um elemento por meio de
seu índice.
Criando uma array com números igualmente distribuídos
Redefinindo o formato de uma array
Usando de operadores lógicos em arrays
OPERAÇÕES MATEMÁTICAS EM ARRAYS
Criando uma matriz diagonal
Criando padrões duplicados
Somando um valor a cada elemento da array
Realizando soma de arrays
Subtração entre arrays
Multiplicação e divisão entre arrays
OPERAÇÕES LÓGICAS EM ARRAYS
Transposição de arrays
Salvando uma array no disco local
Carregando uma array do disco local
Exemplo de aplicação da biblioteca Numpy
BIBLIOTECA PANDAS
Sobre a biblioteca Pandas
Instalação das Dependências
Tipos de dados básicos Pandas
ANÁLISE EXPLORATÓRIA DE DADOS
Importando a biblioteca Pandas
SERIES
Criando uma Serie
Visualizando o cabeçalho
Definindo uma origem personalizada
Definindo um índice personalizado
Integrando uma Array Numpy em uma Serie
Qualquer estrutura de dados como elemento de uma Serie
Integrando funções com Series
Verificando o tamanho de uma Serie
Criando uma Serie a partir de um dicionário
Unindo elementos de duas Series
DATAFRAMES
Criando um DataFrame
Extraindo dados de uma coluna específica
Criando colunas manualmente
Removendo colunas manualmente
Ordenando elementos de uma coluna
Extraindo dados de uma linha específica
Extraindo dados de um elemento específico
Extraindo dados de múltiplos elementos
Buscando elementos via condicionais
OPERAÇÕES MATEMÁTICAS EM DATAFRAMES
Usando de funções embutidas do sistema
Aplicando uma operação matemática a todos elementos
Usando de funções matemáticas personalizadas
ESTRUTURAS CONDICIONAIS APLICADAS A DATAFRAMES
Aplicação de estruturas condicionais simples
Aplicação de estruturas condicionais compostas
Atualizando um DataFrame de acordo com uma condicional
INDEXAÇÃO DE DATAFRAMES
Manipulando o índice de um DataFrame
Índices multinível
ENTRADA DE DADOS
Importando dados de um arquivo para um DataFrame
TRATAMENTO DE DADOS
Tratando dados ausentes / faltantes
Agrupando dados de acordo com seu tipo
MÉTODOS APLICADOS
Alterando o formato de um DataFrame
Concatenando dados de dois DataFrames
Mesclando dados de dois DataFrames
Agregando dados de um DataFrame em outro DataFrame
Filtrando elementos únicos de uma Serie de um DataFrame
Processamento em paralelo de um Dataframe
CONSIDERAÇÕES FINAIS
INTRODUÇÃO
BIBLIOTECA NUMPY
Quando estamos usando da linguagem Python para criar nossos códigos, é
normal que usemos de uma série de recursos internos da própria linguagem,
uma vez que como diz o jargão, Python vem com baterias inclusas, o que
em outras palavras pode significar que Python mesmo em seu estado mais
básico já nos oferece uma enorme gama de funcionalidades prontas para
implementação.
No âmbito de nichos específicos como, por exemplo, computação
científica, é bastante comum o uso tanto das ferramentas nativas da
linguagem Python como de bibliotecas desenvolvidas pela comunidade para
propósitos bastante específicos.
Com uma rápida pesquisa no site https://pypi.org é possível ver que existem,
literalmente, milhares de bibliotecas, módulos e pacotes desenvolvidos pela
própria comunidade, a fim de adicionar novas funcionalidades ou aprimorar
funcionalidades já existentes no núcleo da linguagem Python.
Uma das bibliotecas mais usadas, devido a sua facilidade de implementação
e uso, é a biblioteca Numpy. Esta por sua vez oferece uma grande variedade
de funções aritméticas de fácil aplicação, de modo que para certos tipos de
operações as funções da biblioteca Numpy são muito mais intuitivas e
eficientes do que as funções nativas de mesmo propósito presentes nas
built-ins do Python.
ARRAYS
Outro meio de criar uma array numpy é gerando números inteiros dentro de
um intervalo específico definido manualmente para a função
np.random.randint( ).
Repare que o primeiro parâmetro da função, nesse caso, é o número
10, o que em outras palavras nos diz que estamos gerando números de 0 até
10, já o segundo parâmetro, size, aqui definido em 10, estipula que 10
elementos serão gerados aleatoriamente.
Mais uma vez por meio da função print( ) podemos visualizar a array
gerada, como esperado, 10 números inteiros gerados aleatoriamente.
De forma parecida com o que fizemos anteriormente por meio da
função np.random.rand( ), podemos via np.random.random( ) gerar arrays
de dados tipo float, no intervalo entre 0 e 1, aqui com mais de uma
dimensão.
Note que para isso é necessário colocar o número de linhas e
colunas dentro de um segundo par de parênteses.
Como de costume, por meio da função print( ) visualizamos os dados
gerados e atribuídos a data.
Para esse fim, inicialmente criamos uma variável de nome tamanho que
recebe como atributo o valor 10, este será o número a ser usado como
referência de tamanho para a array, em outras palavras, o número de
elementos da mesma.
Na sequência criamos nossa array data6 que recebe a função
np.random.permitation( ) por sua vez parametrizada com o valor de
tamanho. Por meio da função print( ) podemos ver o resultado desse
gerador.
O resultado, como esperado, é uma array unidimensional composta por 10
elementos aleatoriamente gerados.
Array unidimensional
Para o próximo exemplo criamos uma variável de nome data2 que recebe,
da mesma forma que o exemplo anterior, a função np.random.randint( )
atribuída para si, agora substituindo o valor de size por dois valores (3, 4),
estamos definindo manualmente que o tamanho dessa array deverá ser de 3
linhas e 4 colunas, respectivamente. Uma array bidimensional.
Como esperado, por meio da função print( ) vemos a array em si com seu
conteúdo, novamente via data2.shape vemos o formato esperado.
Array tridimensional
Seguindo com a mesma lógica, criamos a variável data3 que chama a
função np.random.randint( ), alterando novamente o número definido para o
parâmetro size, agora com 3 parâmetros, estamos definindo uma array de
formato tridimensional.
Nesse caso, importante entender que o primeiro dos três parâmetros
se refere ao número de camadas que a array terá em sua terceira dimensão,
enquanto o segundo parâmetro define o número de linhas e o terceiro
parâmetro o número de colunas.
Como esperado, 5 camadas, cada uma com 3 linhas e 4 colunas, preenchida
com números gerados aleatoriamente entre 0 e 5.
Entendidos os meios de como gerar arrays com valores aleatórios, podemos
agora entender também que é perfeitamente possível criar arrays
manualmente, usando de dados em formato de lista para a construção da
mesma. Repare que seguindo uma lógica parecida com o que já foi visto
anteriormente, aqui temos duas listas de números, consequentemente,
teremos uma array bidimensional.
Exibindo em tela o resultado via função print( ) temos nossa array com os
respectivos dados declarados manualmente. Uma última observação a se
fazer é que é imprescindível que se faça o uso dos dados declarados da
maneira correta, inclusive na sequência certa.
Uma das questões mais honestas que todo estudante de Python (não
somente Python, mas outras linguagens) possui é do por quê utilizar de
bibliotecas externas quando já temos a disposição ferramentas nativas da
linguagem.
Pois bem, toda linguagem de programação de código fonte aberto tende a
receber forte contribuição da própria comunidade de usuários, que por sua
vez, tentam implementar novos recursos ou aperfeiçoar os já existentes.
Com Python não é diferente, existe uma infinidade de bibliotecas para todo
e qualquer tipo de aplicação, umas inclusive, como no caso da Numpy,
mesclam implementar novas ferramentas assim como reestruturar
ferramentas padrão em busca de maior performance.
Uma boa prática é manter em nosso código apenas o necessário, assim
como levar em consideração o impacto de cada elemento no processamento
de nossos códigos. Não é porque você vai criar um editor de texto que você
precise compilar junto todo o sistema operacional. Inclusive se você já tem
alguma familiaridade com a programação em Python sabe muito bem que
sempre que possível importamos apenas os módulos e pacotes necessários
de uma biblioteca, descartando todo o resto.
Criada a array data com 15 elementos ordenados, por meio da função print(
) parametrizando a mesma com os dados de data veremos tais elementos.
Aproveitando o contexto, por meio da função max( ) teremos acesso ao
elemento de maior valor associado.
Como esperado, na primeira linha temos todos os elementos de data, na
segunda linha temos em destaque o número 14, lembrando que a array tem
15 elementos, mas os mesmos iniciam em 0, logo, temos números de 0 a
14, sendo 14 o maior deles.
Supondo que você realmente precise dos dados em formato float dentro de
uma array, você pode definir manualmente o tipo de dado por meio do
parâmetro dtype = ‘float’. Dessa forma, todos os elementos desta array
serão convertidos para float.
Aqui como exemplo estamos criando novamente uma array unidimensional,
com números inteiros em sua composição no momento da declaração.
Analisando o retorno de nossa função print( ) podemos ver que de fato são
números, agora com casas decimais, de tipo float64.
Criamos uma nova array de nome data, onde por meio da função
np.linspace( ) estamos gerando uma array composta de 7 elementos
igualmente distribuídos, com valores entre 0 e 1.
O retorno como esperado é uma array de números float, para que haja
divisão igual dos valores dos elementos.
Realizando a conversão de data8 para uma array do tipo numpy, por meio
da função np.array( ) parametrizada com a própria variável data8, agora é
possível realizar a operação de soma dos dados de data8 com o valor
definido 10.
Como retorno da função print( ), na primeira linha temos os valores iniciais
de data8, na segunda linha os valores após somar cada elemento ao número
10.
Algo parecido pode ser feito através da função np.eye( ), porém, nesse caso
será gerada uma matriz diagonal com valores 0 e 1, de tamanho definido
pelo parâmetro repassado para função.
Neste caso, para a variável data9 está sendo criada uma array diagonal de 4
linhas e colunas conforme a parametrização realizada em np.eye( ).
O retorno é uma matriz diagonal, com números 1 dispostos na coluna
diagonal
Aqui criada a array data10, note que atribuído para a mesma está a função
np.tile( ) parametrizada com uma array definida manualmente assim como
um último parâmetro 4, referente ao número de vezes a serem replicados os
dados/valores da array.
No retorno podemos observar os elementos declarados na array, em sua
primeira linha 9 e 4, na segunda, 3 e 7, repetidos 4 vezes.
Mesmo exemplo anterior, agora parametrizado para replicação em linhas e
colunas.
Note que, neste caso, a duplicação realizada de acordo com a
parametrização ocorre sem sobrepor a ordem original dos elementos.
Como visto anteriormente, a partir do momento que uma matriz passa a ser
uma array do tipo numpy, pode-se perfeitamente aplicar sobre a mesma
qualquer tipo de operador lógico ou aritmético. Dessa forma, é possível
realizar tais operações, desde que leve em consideração que a operação
realizada se aplicará a cada um dos elementos da array.
Para esse exemplo criamos uma nova array de nome data11, por sua vez
gerada com números ordenados. Em seguida é realizada uma simples
operação de somar 1 a própria array, e como dito anteriormente, essa soma
se aplicará a todos os elementos.
Observando os retornos das funções print( ) na primeira linha temos a
primeira array com seus respectivos elementos, na segunda, a mesma array
onde a cada elemento foi somado 1 ao seu valor original.
Transposição de arrays
Realizando outro tipo de interação com nosso vetor, podemos usar de nosso
método pesquisa_elemento( ) parametrizado com o elemento em si para
descobrir sua posição de índice no vetor.
Também é possível pesquisar diretamente o último valor atribuído para o
objeto ultimo, usando desse retorno, que será um número de índice, para
descobrirmos quantos elementos compõem nosso vetor.
Uma vez que nosso vetor esteja definido, assim como para o mesmo não
exista nenhum conflito de interação ou até mesmo de sintaxe, podemos
manipular este vetor à vontade.
Inserindo alguns elementos via método insere_elemento( ), podemos
visualizar nosso vetor instanciando e executando o método exibe_em_tela( )
sempre que necessário.
Por fim, testando nosso método exclui_elemento( ) será possível ver que
quando excluímos um determinado elemento do vetor, os elementos
subsequentes serão realocados automaticamente para que as posições de
índice se mantenham íntegras.
BIBLIOTECA PANDAS
Sobre a biblioteca Pandas
- Series
Dos tipos de dados básicos da biblioteca Pandas, o primeiro que devemos
entender é o tipo Serie. A biblioteca Pandas possui alguns tipos de dados
particulares (de funcionalidades equivalentes a outros tipos de dados como
arrays), onde uma Serie equivale a uma array unidimensional, com
mapeamento de elementos e índice próprio, capaz de aceitar em sua
composição todo e qualquer tipo de dado Python.
- DataFrames
Outro tipo de dado básico usado na biblioteca Pandas é o chamado
DataFrame, muito semelhante a uma Serie (inclusive um DataFrame é
formado por um conjunto de Series), porém equivalente a uma array
multidimensional. Dessa forma, o comportamento e o tratamento dos dados
ganham novas funcionalidades que veremos em detalhes nos próximos
capítulos.
O ponto chave aqui é que, para dados dispostos em vetores e matrizes, a
biblioteca Pandas oferece uma vasta gama de funções aplicáveis a tais
dados desde que os mesmos estejam, seja nativos ou convertidos, em
formato DataFrame.
Uma vez que não tenha ocorrido nenhum erro no processo de instalação,
para utilizarmos das funcionalidades da biblioteca Pandas é necessário
importar a mesma para nosso código.
O processo é extremamente simples, igual ao de qualquer outra importação
em Python. Sendo assim, por meio do comando import pandas todo seu
conteúdo será importado ficando assim seus recursos à disposição do
desenvolvedor.
Apenas por convenção podemos referenciar tal biblioteca por
alguma sigla, o comum entre a comunidade é instanciar a biblioteca Pandas
como simplesmente pd.
SERIES
Uma Serie pode ser criada de diversas formas, sendo a mais comum delas a
partir de seu método construtor, utilizando como base dados em forma de
lista, uma vez que se tratando de uma Serie teremos como resultado final
uma array unidimensional.
No exemplo, dada uma variável de nome base, que por sua vez possui como
atributo uma lista de elementos numéricos, podemos usar da mesma para a
geração de uma Serie.
Então é criada uma nova variável, dessa vez de nome data, que instancia e
inicializa a função pd.Series( ), repassando como parâmetro para a mesma o
conteúdo da variável base.
Exibindo em tela por meio de nossa função print( ), nos é retornado uma
estrutura com características de uma tabela indexada de apenas uma coluna
e 10 linhas.
Usando de nossa função type( ) aninhada a função print( ), por sua vez
parametrizada com data, é possível ver o tipo de dado de nossa variável
data.
Como esperado, o conteúdo de data é uma Serie criada a partir dos dados de
base, logo, nos é retornado como tipo de dado pandas.core.series.Series.
Outra possibilidade é que criemos uma Serie via construtor, repassando os
dados diretamente como parâmetro para a mesma, desde que tais dados
estejam em formato de lista.
No exemplo, a variável data chama a função pd.Series( ) parametrizando a
mesma com a lista [1,3,5,7,9,12,15,18,21,24].
Exibindo novamente o conteúdo de data via função print( ) nos é retornado
uma Serie igual as anteriores.
Visualizando o cabeçalho
Uma vez que tudo em Python é objeto, dentro das possibilidades de uso de
dados em uma Serie ou DataFrame podemos até mesmo usar de um ou mais
métodos como elementos dessa Serie ou DataFrame.
Apenas como exemplo, declarada a variável funcoes que recebe uma lista
com dois elementos, elementos estes que são palavras reservadas ao sistema
para funções de entrada e de saída, respectivamente, tais elementos podem
ser perfeitamente incorporados em uma Serie ou em um DataFrame.
Logo em seguida é criada uma variável de nome data6 que instancia e
inicializa a função pd.Series( ), usando como dados de origem o conteúdo
atrelado a variável funcoes.
Por fim, exibindo em tela o conteúdo de data6 via print( ), são retornadas as
referências aos objetos referentes aos métodos input( ) e print( ).
DATAFRAMES
Verificando o tipo de dado por meio de nossa função type( ) aninhada para
print( ), parametrizada com base, nos é retornado
pandas.core.frame.DataFrame.
Usando do método info( ) podemos obter um resumo gerado para nosso
DataFrame, uma vez que tal base de dados esteja associada a uma variável,
podendo assim aplicar o método info( ) para a mesma, retornando assim
informações como tipo de dado, número de elementos, tipo de dado dos
elementos, finalizando com tamanho alocado em memória para esse
DataFrame.
Uma vez que temos uma coluna como uma Serie, e uma Serie tem seus
dados lidos como em uma lista, podemos usar da notação de listas em
Python para fazer referência a uma coluna ou a um determinado elemento
da mesma.
Em nosso exemplo, parametrizando nossa função print( ) com data na
posição [‘c’] (*notação de lista), nos é retornado apenas os dados da coluna
c de nosso DataFrame.
Visualizando o tipo de dado de data (todo o DataFrame) via type( ) nos é
retornado pandas.core.frame.DataFrame.
Como visto anteriormente, tratando uma coluna como uma Serie podemos
extrair dados de uma determinada coluna usando da notação de extração de
dados de uma lista.
Porém, o método usual para extração de dados de uma coluna específica de
um DataFrame é simplesmente fazer referência ao nome do cabeçalho da
mesma.
Via print( ), parametrizado com data.d, estamos exibindo em tela apenas o
conteúdo da coluna de nome ‘d’ de nosso DataFrame.
Embora esse seja o método nativo, o mesmo possui como limitação a
extração de dados de apenas uma coluna por vez.
Outra forma de remover uma determinada coluna assim como todo seu
conteúdo é por meio da função do sistema del, especificando qual a variável
origem e a posição de índice ou nome do cabeçalho o qual remover. Por se
tratar de uma função do sistema, independentemente do ambiente, essa
alteração será permanente.
Exibindo em tela o conteúdo de data em nossa função print( ), é possível
notar que a coluna de nome ‘b’ foi removida como era esperado.
Para que tais conceitos façam mais sentido, vamos para a prática.
Novamente, apenas para evitar inconsistências em nossos exemplos, um
novo DataDrame é criado nos mesmos moldes dos DataFrames anteriores.
Em seguida, por meio da função print( ) exibimos em tela o conteúdo de
nosso DataFrame associado a variável data.
Na sequência, diretamente como parâmetro de nossa função print( ),
criamos uma estrutura condicional composta onde os dados de data na
posição [‘a’] forem menores que zero e (and &) os dados de data na posição
[‘b’] forem maiores que 1, um novo DataFrame é gerado, atualizando os
dados originais de data.
Observe que a expressão completa foi escrita de modo que a variável data
terá sua estrutura atualizada com base nas expressões condicionais
declaradas, porém o que foi retornado é um DataFrame vazio, pois ao
menos uma das condições impostas não foi atingida.
INDEXAÇÃO DE DATAFRAMES
Índices multinível
Avançando nossos estudos com índices, não podemos deixar de ver sobre os
chamados índices multinível, pois para algumas aplicações será necessário
mapear e indexar nossos DataFrames de acordo com suas camadas de dados
ordenados.
Lembrando que tudo o que foi visto até então também é aplicável para os
DataFrames que estaremos usando na sequência. Todo material deste livro
foi estruturado de modo a usarmos de ferramentas e recursos de forma
progressiva, de forma que a cada tópico que avançamos estamos somente a
somar mais conteúdo a nossa bagagem de conhecimento.
Em teoria, assim como em uma planilha Excel temos diferentes guias, cada
uma com suas respectivas tabelas, a biblioteca Pandas incorporou a seu rol
de ferramentas funções que adicionavam suporte a estes tipos de estruturas
de dados em nossos DataFrames.
Para criar um índice multinível, para camadas de dados em nossos
DataFrames, devemos em primeiro lugar ter em mente quantas camadas
serão mapeadas, para que assim possamos gerar índices funcionais.
Partindo para a prática, inicialmente criamos uma variável de nome indice1,
que recebe como atributo uma lista com 6 elementos em formato de string.
Da mesma forma é criada outra variável de nome indice2 que por sua vez
possui como atributo uma lista composta de 6 elementos numéricos.
Aqui já podemos deduzir que tais listas serão transformadas em camadas de
nosso índice, para isso, antes mesmo de gerar nosso DataFrame devemos
associar tais listas de modo que seus elementos sejam equiparados de
acordo com algum critério.
Para nosso exemplo inicial, estaremos trabalhando com um índice de
apenas dois níveis, porém, a lógica será a mesma independentemente do
número de camadas a serem mapeadas para índice.
Se tratando então de apenas duas camadas, podemos associar os elementos
de nossas listas criando uma lista de tuplas. O método mais fácil para gerar
uma lista de tuplas é por meio da função zip( ). É então criada uma variável
de nome indice3 que recebe como parâmetro em forma de lista via
construtor de listas list( ) a junção dos elementos de indice1 e indice2 via
zip( ).
A partir desse ponto já podemos gerar o índice em questão. Para isso,
instanciando a variável indice3, chamando a função
pd.MultiIndex.from_tuples( ), por sua vez parametrizada com os dados da
própria variável indice3, internamente tal função usará de uma série de
métricas para criar as referencias para as devidas camadas de índice.
Importante salientar que, apenas para esse exemplo, estamos criando um
índice multinível a partir de uma tupla, porém, consultando a ajuda inline
de pd.MultiIndex é possível ver diversos modos de geração de índices a
partir de diferentes estrutura de dados.
Uma vez gerado nosso índice, podemos exibir seu conteúdo através da
função print( ), pois o mesmo é uma estrutura de dados independente, que
será atrelada a nosso DataFrame.
Na sequência criamos uma variável de nome data8 que via método
construtor de DataFrames gera um novo DataFrame de 6 linhas por 2
colunas, repassando para o parâmetro nomeado index o conteúdo da
variável indice3, nosso índice multinível criado anteriormente.
Exibindo o conteúdo de nossa variável data8 via função print( ), é possível
ver que de fato temos um DataFrame com seus dados distribuídos de modo
que suas linhas e colunas respeitam uma hierarquia de índice multinível.
TRATAMENTO DE DADOS
Para esses exemplos, vamos fazer uso de um novo DataFrame, dessa vez
criado a partir de um dicionário composto de três chaves, ‘Vendedor’,
‘Item’ e ‘Valor’, com suas respectivas listas de elementos no lugar de seus
valores de dicionário.
Nos mesmos moldes dos exemplos anteriores, a partir desse dicionário
geramos um DataFrame e a partir deste ponto podemos nos focar nos
assuntos deste tópico.
Exibindo em tela via print( ) o conteúdo de data, nos é retornado um
DataFrame com seus respectivos dados organizados por categoria,
distribuídos em 3 colunas e 6 linhas.
MÉTODOS APLICADOS
Uma vez criados os DataFrames com índices únicos entre si, podemos dar
prosseguimento com o processo de concatenação.
Seguindo com nosso exemplo, é criada uma variável de nome lojas, que
usando do método pd.concat( ) parametrizado com data1 e data2 em forma
de lista, recebe os dados agrupados e remapeados internamente pela função
concat( ).
Exibindo em tela o conteúdo de lojas, é possível ver que de fato agora
temos um novo DataFrame, composto pelos dados dos DataFrames
originais, mantendo inclusive sua indexação pré definida.
Reaproveitando a linha de exercício do tópico anterior, onde falávamos
sobre o formato de um DataFrame, podemos associar tal conceito no
processo de concatenação.
Como dito anteriormente, em casos onde um DataFrame é remodelado,
métricas internas são aplicadas para que o mesmo não perca sua proporção
original.
Em nosso exemplo, usando do método pd.concat( ), definindo para o
mesmo o valor 1 em seu parâmetro nomeado axis, estamos a realizar a
concatenação dos DataFrames data1 e data2 usando como referência suas
colunas ao invés das linhas.
Importante observar que, nesse caso, tendo desproporcionalidade do
número de elementos em linhas e colunas, ao invés do método concat( )
gerar alguma exceção, ele contorna automaticamente o problema
preenchendo os espaços inválidos com NaN, mantendo assim a proporção
esperada para o novo DataFrame.
Apenas realizando um pequeno adendo, como observado em tópicos
anteriores, NaN é um marcador para um campo onde não existem
elementos (numéricos), sendo assim necessário tratar esses dados faltantes.
Mesclando dados de dois DataFrames
Uma vez que temos os dados carregados e nossa função para testes
definida, podemos finalmente partir para a parte que nos interessa neste
tópico, o tempo de processamento dos dados de nosso Dataframe.
A nível de código, em meu caso executando via Colab, inserindo o
prefixo %%time da célula será gerado um retorno referente ao tempo de
execução da mesma.
Nesta célula, neste bloco de código em particular, declaramos uma
variável de noma distance_df que instancia e inicializa o método haversine(
) por sua vez parametrizado com o conteúdo da variável df.
Executando esta célula o retorno referente ao método haversine( )
executado de forma convencional será:
The slowest run took 23.12 times longer than the fastest. This could mean that an intermediate
result is being cached.
1000 loops, best of 5: 210 µs per loop
Em tradução livre:
A execução mais lenta demorou 23,12 vezes mais que a mais rápida. Isso pode significar que
um resultado intermediário está sendo armazenado em cache.
1000 loops, melhor de 5: 210 µs por loop
CONSIDERAÇÕES FINAIS
Muito obrigado por adquirir esta obra, sucesso em sua jornada, um forte
abraço... Fernando Feltrin.