3 Introdução ao Python 🚀

Python é uma linguagem multiparadigma, com uma sintaxe simples que permite ao utilizador focar no problema e deixar de lado qualquer tipo de especificidade. A linguagem vem sendo amplamente utilizada em diversas áreas, por conta das milhares de bibliotecas que possui, boa parte dessas distribuídas pela incrível comunidade da linguagem ❤️.

Nesta introdução, o foco será a aplicação da linguagem Python na manipulação e tratamento de dados, utilizando como base a biblioteca Pandas.

Você pode estar se perguntando o motivo da utilização desta biblioteca e a resposta é bem simples. Python é uma linguagem de uso geral, assim, suas funções nativas não tem como foco uma aplicação específica. Isso diferencia o Python de linguagens como R e Julia, que possuem áreas específicas de aplicação. Com isso, não podemos esperar que nativamente a linguagem tenha métodos variados para a manipulação e tretamento de dados e é nessa parte que o Pandas trabalha, facilitando toda a manipulação e tratamento de dados com funções incríveis.

Vamos começar !

3.1 Introdução ao Pandas 🐼

Para iniciar, vamos entender o que é a biblioteca Pandas e qual seu objetivo geral, para isso, vejamos a definição feita na documentação oficial do projeto.

Pandas é uma biblioteca open source, licenciada pelo BSD, que fornece estruturas de dados de alto desempenho e fáceis de usar e ferramentas de análise de dados para a linguagem de programação Python.

Veja então que, o Pandas possui quase tudo o que é necessário para a realização de um projeto de análise de dados na linguagem Python. Isso faz com que a biblioteca seja a principal ferramenta Python para a área de Data Science.

Para se ter uma ideia, o Pandas pode ser aplicado em análises envolvendo:

  • Finanças;
  • Geociências;
  • Ciência social;
  • Além de diversas outras áreas de ciências e engenharia.

Nos próximos tópicos serão abordadas as principais funções e estruturas de dados do Pandas, evidenciando o quão fácil é a utilização da ferramenta.

3.2 Estruturas de dados

O Pandas disponibiliza diversas estruturas de dados preparadas para o trabalho com grandes volumes de dados, sendo as principais:

  • Series;
  • DataFrames.

Nos subtópicos abaixo vamos ver as características de cada uma dessas estruturas, e em seguida, veremos alguns utilitários para leitura de dados com Pandas.

3.2.1 Series

As Series são estruturas unidimensionais, que contam com diversos métodos para a manipulação de dados. Pode-se entender que as Series são estruturas de dados simples, assim como as listas padrões da linguagem Python, com uma pequena diferença, os items dentro de uma Series possuem um índice.

Para entendermos como a estrutura funciona, vamos apresentar alguns exemplos de como criar e manipular as Series.

No código abaixo, inicialmente é realizada a importação do Pandas com o ‘apelido’ de pd. Em seguida criamos uma Series simples, passando como parâmetro de entrada uma lista com três valores, veja:

## 0    1
## 1    2
## 2    3
## dtype: int64

Ao fazer pd.Series, você está explicitamente informando que a estrutura de dados Series está no contexto da biblioteca Pandas.

É interessante notar que cada elemento da Series possui um índice associado. Este índice permite a recuperação rápida e simples dos dados que compõem a Series. A recuperação feita através de índices é muito parecida com a recuperação de valores em um dicionário em Python. Abaixo é apresentado um exemplo de como indexar e recuperar um elemento da Series:

## 1

Através do atributo index é possível visualizar os índices associados a estrutura:

## RangeIndex(start=0, stop=3, step=1)

O índice pode ser facilmente editado como segue:

## Index(['um', 'dois', 'tres'], dtype='object')

Também é possível já criar uma Series com um índice personalizado, para isto, basta passar como parâmetro do construtor da Series uma segunda lista com os valores dos índices.

Note que a primeira lista passada representa os dados, e a segunda o índice.

## 1    7
## 2    8
## 3    9
## dtype: int64

É possível criar uma Series através de um dicionário Python. Veja o exemplo:

## instituto    INPE
## nota           10
## dtype: object

Após a construção o uso é basicamente o mesmo do dicionário. Além do que já foi dito sobre as Series, é importante lembrar que, estas estruturas de dados possuem diversos métodos para facilitar a manipulação e entendimento dos dados, apresentados nas próximas seções.

Mesmo as Series sendo estruturas de dados poderosas, elas apresentam a limitação da unidimensionalidade. Isso impede que estruturas N-dimensionais (Como por exemplo, matrizes, tabelas e similares) não possam ser facilmente representadas através das Series. Para exemplificar este impedimento, vamos inserir uma matriz de dados em uma Series, erros não serão apresentados, porém, a representação pode não ficar como desejado.

## 0    [1, 2, 3]
## 1    [4, 5, 6]
## dtype: object

Percebeu ? Não há uma matriz, e sim listas de listas associadas aos índices. A ideia é que a matriz fosse apresentada em um formato onde cada elemento está em uma posição, como na tabela abaixo.

1 2 3
4 5 6

Para estes casos é necessário o uso de uma outra estrutura de dados, o DataFrame, tratado no subtópico seguinte.

3.2.2 DataFrames

Agora que você já conhece como as Series funcionam, vamos apresentar a você o DataFrame. O DataFrame difere das Series por ser uma estrutura multidimensional, ou seja, trabalha com linhas e colunas.

Boa parte dos métodos disponíveis em uma Series também são aplicáveis em DataFrames, o que ajuda no aprendizado da utilização da API do Pandas. É importante entender que, o fato de haver mais dimensões nos dados torna a manipulação diferente, com resultados diferentes (Isto para os mesmos métodos).

Vejamos algumas características bacanas dos DataFrames:

##    0  1  2
## 0  1  2  3
## 1  4  5  6

O processo acima quando realizado nas Series, gera listas de listas. Porém com os DataFrames tem-se uma matriz com formas de recuperação por linhas e colunas. Da mesma forma que as Series, os DataFrames permitem que os índices sejam (re)nomeados, além disso, as colunas também podem ser (re)nomeadas e utilizadas para a recuperação de dados.

##       primeiro  segundo  terceiro
## zero       0.9      0.8       0.7
## um         0.4      0.5       0.7

Um DataFrame também pode ser criado através de um dicionário, veja como isso é feito:

##      nome  idade
## 0  felipe     12
## 1   maria     13

Bem, agora que você entendeu a diferença fundamental entre essas estruturas de dados, vamos aprender sobre diferentes métodos para a manipulação dessas estruturas.

3.2.3 Seleção e filtro dos dados

Uma parte muito importante é a seleção e filtragem dos dados. Vamos começar fazendo a busca utilizando os índices (index) e as colunas (No caso dos DataFrames). Para isto, utilizamos os métodos .loc, que permitem buscar uma linha com algum nome de índice específico e o .iloc que busca uma linha em uma posição específica.

A sintaxe de utilização básica para as duas estruturas de dados podem ser vistas abaixos:

DataFrame Series
.loc[Nome da linha, Nome da coluna] .loc[Nome da linha]
.iloc[posição da linha, posição da coluna] .iloc[posição da linha]

Note as diferenças de sintaxe em relação a estrutura de dados (Series ou DataFrames) adotada. Os métodos mudam pela quantidade de parâmetros que podem ser utilizados. O .loc e .iloc permitem a busca por linhas e colunas nos DataFrames e somente das linhas em uma Series.

## 'Josefa'
## nome     Josefa
## idade        21
## Name: 9, dtype: object
## 'Josefa'
## nome     Josefa
## idade        21
## Name: 9, dtype: object
## 18

Ambas estruturas também permitem a utilização de expressões booleanas para filtragem dos dados. No exemplo abaixo é instanciado um DataFrame de valores numéricos. O objetivo é selecionar todos elementos do DataFrame maiores ou iguais a 5.

##    numeros
## 4        5
## 5        6
## 6        7
## 7        8
## 8        9
## 9       10

O exemplo utilizou um DataFrame, mas as expressões booleanas também podem ser aplicadas nas Series.

Esses são os conceitos básicos para a manipulação e filtragem dos dados. Existem diversas funções e métodos que podem ser utilizados para esse fim. Inicialmente veremos somente parte deles, porém, só com o que vimos até aqui já é possível realizar grande parte das etapas de análise de dados.

3.2.4 Agrupamento de dados e agregações

Muitas vezes quereos agrupar nossos dados para obter informações, seja para facilitar a manipulação, o entendimento das relações contidas nos dados, ou mesmo para aplicar funções sobre cada um dos grupos e obter informações a partir disto. Para isso podemos utilizar diferentes formas de agrupamentos e agregações.

O termo agregações pode apresentar diferentes definições, aqui foi assumido que, “Agregações são operações aplicadas sobre os dados que resultam em um conjunto de valores”.

No Pandas, o retorno de uma agregação pode mudar de acordo com a estrutura de dados, no caso das Series é retornado apenas um valor escalar. Para os DataFrames, o retorno da agregação é um valor para cada linha ou coluna (A dimensão pode ser definida pelo usuário).

Algumas operações de agregação são:

  • sum() -> Realiza somatório;
  • min() -> Busca o valor mínimo;
  • max() -> Busca o valor máximo;
  • count() -> Realiza a contagem de elementos.
## 6
## 0    5
## 1    7
## 2    9
## dtype: int64

Por outro lado, as operações de agrupamento realizam a divisão dos dados em conjuntos que possuem alguma similaridade. No Pandas o agrupamento é realizado com o método groupby presente nas Series e DataFrames.

O critério de similaridade utilizado nas operações de agrupamento são definidos pelo usuário.

Aqui vamos focar no agrupamento dos DataFrames, mas os passos são os mesmos para as Series.

No exemplo acima, os dados foram agrupados de acordo com a coluna tipo. No caso, os elementos que possuem o mesmo valor do atributo tipo são agrupados por linhas.

Certo, a operação é simples e intuitiva, mas o que devemos esperar deste método ? Intuitivamente pensamos em um conjunto de DataFrames, onde cada DataFrame representa um grupo. Esse é exatamente o retorno do Pandas, porém, há algumas particulariedades. O tipo retornado de uma operação groupby deixa de ser um DataFrame e passa a ser um DataFrameGroupBy. Isso facilita a manipulação dos grupos com métodos especializados.

Vamos iniciar o entendimento do DataFrameGroupBy pela verificação dos grupos criados. Esse processo é feito através do atributo groups.

## {'antigo': Int64Index([0], dtype='int64'), 'novo': Int64Index([1, 2], dtype='int64')}

Acessando o atributo groups é possível verificar todos os grupos gerados e seus membros. Além disto, com um objeto DataFrameGroupBy é possível aplicarmos os conceitos de agregação dentro dos grupos gerados pelo groupby.

Para entendermos o funcionamento do processo de agrupamento e agregação juntos, vejamos a definição feita por Hadley Wickham:

“O processo de dividir é o agrupamento, onde os dados são agrupados de acordo com alguma característica definida previamente, a aplicação realiza as agregações, filtros ou transformações e por fim a combinação, que representa a junção dos resultados das etapas anteriores”

Na definição o processo é dividido nas etapas Dividir-Aplicar-Combinar. Na figura abaixo é apresentado um fluxograma das diferentes etapas deste processo.

Para aprimorar o entendimento vamos criar um exemplo.

No código apresentado abaixo é realizado um agrupamento levando em consideração o atributo idade.

##        nome  dinheiro
## idade                
## 19        2         2
## 20        2         2

Veja que o resultado do agrupamento apresenta o valor do atributo idade e a quantidade de elementos associados ao grupo. Mas é possível aplicar a agregação em uma coluna específica ? Sim, é possível!

## idade
## 19    200
## 20    300
## Name: dinheiro, dtype: int64

Pronto! No exemplo acima é apresentado um exemplo de como agregar uma coluna específica no caso o atributo dinheiro. Com estes métodos de manipulação já é possível resolver diversos problemas do mundo real!

3.2.5 Funções de leitura e escrita de dados

Além das estruturas de dados poderosas, o pandas também possui funções para leitura e escrita de dados que facilitam muito a vida 🚀.

Como existem muitas funções para leitura e escrita, aqui serão apresentadas apenas algumas. Para conhecer outras funções de leitura e escrita não deixe de conferir a documentação do projeto.

Caso você esteja trabalhando com dados estruturados salvos em um arquivo .csv, existe a função read_csv para te ajudar.

O ponto importante a ser notado é que, os dados quando carregados com as funções do Pandas, sempre serão alocados em memória como uma Series ou DataFrame. Com a função type vamos verificar qual o tipo do dado carregado.

## <class 'pandas.core.frame.DataFrame'>

Observe que o dado ao ser lido é representado como uma DataFrame. Portanto, podemos utilizar todos os métodos já vistos até aqui. Existem algumas formas de visualizar os dados carregados. A seguir são apresentados exemplos das funções head e tail que respectivamente recuperam o início e o final do DataFrame.

##    PassengerId  Pclass                                          Name  ...     Fare  Cabin  Embarked
## 0          892       3                              Kelly, Mr. James  ...   7.8292    NaN         Q
## 1          893       3              Wilkes, Mrs. James (Ellen Needs)  ...   7.0000    NaN         S
## 2          894       2                     Myles, Mr. Thomas Francis  ...   9.6875    NaN         Q
## 3          895       3                              Wirz, Mr. Albert  ...   8.6625    NaN         S
## 4          896       3  Hirvonen, Mrs. Alexander (Helga E Lindqvist)  ...  12.2875    NaN         S
## 
## [5 rows x 11 columns]
##      PassengerId  Pclass                          Name     Sex  ...              Ticket      Fare  Cabin Embarked
## 413         1305       3            Spector, Mr. Woolf    male  ...           A.5. 3236    8.0500    NaN        S
## 414         1306       1  Oliva y Ocana, Dona. Fermina  female  ...            PC 17758  108.9000   C105        C
## 415         1307       3  Saether, Mr. Simon Sivertsen    male  ...  SOTON/O.Q. 3101262    7.2500    NaN        S
## 416         1308       3           Ware, Mr. Frederick    male  ...              359309    8.0500    NaN        S
## 417         1309       3      Peter, Master. Michael J    male  ...                2668   22.3583    NaN        C
## 
## [5 rows x 11 columns]

Caso os dados que você está trabalhando sejam oriundos de algum serviço web, o Pandas também pode te ajudar! Com o método read_json você pode passar não só o nome do arquivo json em sua máquina, mas também a URL do serviço web que distribui tais dados. Como exemplo vamos recuperar dados de uma API Rest de exemplo.

##     status                                               data                                      message
## 0  success  {'id': 1, 'employee_name': 'Tiger Nixon', 'emp...  Successfully! All records has been fetched.
## 1  success  {'id': 2, 'employee_name': 'Garrett Winters', ...  Successfully! All records has been fetched.
## 2  success  {'id': 3, 'employee_name': 'Ashton Cox', 'empl...  Successfully! All records has been fetched.
## 3  success  {'id': 4, 'employee_name': 'Cedric Kelly', 'em...  Successfully! All records has been fetched.
## 4  success  {'id': 5, 'employee_name': 'Airi Satou', 'empl...  Successfully! All records has been fetched.
## Index(['status', 'data', 'message'], dtype='object')

Da mesma forma que a leitura, fazer escrita de dados com pandas é muito simples. Tendo os dados sobre uma Series ou DataFrame é possível salvar os dados em diferentes formatos. Por exemplo, o método to_csv pode ser utilizado para salvar uma Series ou um DataFrame em um arquivo .csv.

Mas caso você queira salvar em um arquivo json, basta trocar o método to_csv para to_json e pronto! Seus dados estarão disponibilizados no sistema de arquivos nos formatos indicados.

Viu ? É tudo muito simples e direto, o que te permite focar em sua análise e deixar de lado problemas com sintaxe 🗽. Para fechar esta seção de análise de dados com Python e Pandas, será apresentado um exemplo de análise que pode ser utilizada em aplicaçẽos reais.

3.3 Exemplos

Para o exemplo foi utilizado o conjunto de dados Meteorite Landings retirados da plataforma Kaggle. O conjunto de dados consistem em mais de 45 mil registros de meteoros que atingiram a Terra.

Para iniciar a análise inicialmente importamos a biblioteca Pandas.

Depois de importar a biblioteca, faça a importação dos dados

Fazer análise de dados é responder perguntas, matar a curiosidade, então, na análise feita, algumas perguntas serão feitas para que a análise tenha algum objetivo.

Para começar, vamos verificar o tipo de dado que foi devolvido da função read_csv utilizada para carregar os dados.

## <class 'pandas.core.frame.DataFrame'>

Beleza, é um DataFrame, isso indica que vamos conseguir aplicar os métodos que vimos anteriormente sem problemas. Agora vamos verificar as dimensões que a tabela representa no DataFrame possui. Para tal, o atributo shape será utilizado.

## (45716, 10)

Interessante! Os valores (45716, 10), indicam respectivamente que, o conjunto de dados possui 45716 linhas e 10 colunas. Por falar nas colunas, vamos verificar quais são as colunas disponíveis neste conjunto de dados.

## Index(['name', 'id', 'nametype', 'recclass', 'mass', 'fall', 'year', 'reclat',
##        'reclong', 'GeoLocation'],
##       dtype='object')

A descrição de cada coluna está disponível na página dos dados, não deixe de conferir para o entendimento completo sobre os métodos de análise e os dados.

Vamos explorar a coluna nametype, que indica o tipo de meteorito, havendo dois tipos possíveis:

  • Valid: Tipo comum de meteorito;
  • Relict: Meteorito degradado pelo clima da Terra.

Para verificar qual tipo possui mais dados vamos dividir o conjunto de dados entre as observações do conjunto do tipo Valid e as observações do tipo Relict.

Vamos olhar nas dimensões de cada conjunto de dados gerados.

## (45641, 10)
## (75, 10)

Olha que bacana! Já é possível perceber que o número de meteoritos do tipo Valid é muito maior que os do tipo Relict. Agora, vamos entender a quantidade de massa média que cada um desses grupos apresenta.

Vamos olhar o resultado do agrupamento.

## nametype
## Relict        0.121269
## Valid     13285.656127
## Name: mass, dtype: float64

A massa média (apresentada em gramas) do grupo Valid é muito maior, o que pode ser explicado pela quantidade no conjunto de dados.

Para finalizar, vamos fazer a contagem dos tipos de meteoritos.

Por fim, façamos o filtro do conjunto de dados pela quantidade de massa .

## (7036, 10)

É isso, o exemplo mostrou que conhecendo bem a estrutura e semântica dos dados e com os métodos apresentados no curso é possível extrair várias informações de um conjunto de dados.

3.4 Para saber mais

Não deixe de buscar mais informações! Abaixo alguns links que podem ser úteis.