# Aula 10.2 - Análise e visualização de dados em Python - biblioteca pandas (parte 7)

Na última aula, nós vimos as possibilidades de juntar conjuntos de dados em um só usando a função `concat()` do pandas. Agora, e se quisermos juntar os nossos dados de uma maneira que a concatenação não consegue cobrir?

O pandas, como vimos até então, é uma espécie de 'canivete suíço' para quem quer analisar seus dados; muito por isso, a biblioteca nos oferece mais funções para que possamos 'aliviar a dor' de juntar conjuntos de dados que temos em mãos. Nesta aula, a função da vez é a `merge()`.

## Junte-se a nós... mas de que jeito?: função `merge()`

Ainda mencionando a função `concat()`, vimos que ela funciona de maneira que se consiga *empilhar* dois ou mais DataFrames, seja um em cima do outro, seja um do lado do outro. Isso funciona bem para dados que possuam colunas exatamente iguais, ou até mesmo índices... mas e se não for o seu caso?

Se você conhece um pouco de [SQL]([https://pt.wikipedia.org/wiki/SQL](https://pt.wikipedia.org/wiki/SQL)), é capaz de se lembrar de uma cláusula chamada `JOIN`. Como o próprio nome diz, é uma cláusula destinada a *juntar* dois conjutos de dados, num primeiro momento, em apenas um, podendo considerar diferentes aspectos para gerar o resultado esperado.

A função `merge()` do pandas, em bom português, possui poder semelhante ao `JOIN`; logo, você pode lançar mão dessa função para juntar DataFrames. **Mas cuidado:** assim como foi dito muitas vezes no universo de Homem-Aranha, **"com grandes poderes vêm grandes responsabilidades"**. A pergunta que fica depois disso é: quais são as "responsabilidades" do `merge()`?

### Como juntar? O parâmetro `how`

Caso voltemos nossos olhos à documentação do pandas, especialmente no que toca à função `merge()`, percebemos a presença de um grande número de parâmetros, onde você pode fazer várias modificações a fim de performar uma "sintonia fina", para chegar ao resultado que se espera. No entanto, demos foco primeiramente a um dos mais importantes: o parâmetro `how`.

Pensemos primeiro na cláusula `JOIN` do SQL: esta se desdobra em métodos específicos de mescla, como `OUTER JOIN`, `INNER JOIN`, e assim por diante. A mesma coisa pode ser vista na função `merge()`, através do argumento `how`; a diferença estará na sintaxe, basicamente. 

Observe a tabela e a imagem abaixo, onde estão as diferenças de sintaxe entre o pandas e o SQL, e quais são as possibilidades de usar cada função e cláusula. Perceba que os nomes já indicam que tipo de operação será feita; mas, ainda assim, veremos com um pouco mais de detalhe cada uma delas mais à frente.

<html>
<head>
<style>
  .join-table {
    width: 100%;
    border-collapse: collapse;
    font-family: system-ui, sans-serif;
    margin: 20px 0;
  }
  
  .join-table th {
    background-color: #f3f4f6;
    padding: 12px;
    text-align: center;
    font-weight: 600;
    border: 1px solid #e5e7eb;
  }
  
  .join-table td {
    padding: 12px;
    border: 1px solid #e5e7eb;
    text-align: center;
  }
  
  .join-table tr:nth-child(even) {
    background-color: #f9fafb;
  }
  
  .code {
    font-family: monospace;
    background-color: #f1f5f9;
    padding: 2px 4px;
    border-radius: 4px;
  }
  
  .description {
    text-align: left;
  }
</style>
</head>
<body>
  <table class="join-table">
    <thead>
      <tr>
        <th>pandas merge()</th>
        <th>SQL JOIN</th>
        <th>Operação</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td><span class="code">how='inner'</span></td>
        <td><span class="code">INNER JOIN</span></td>
        <td class="description">Retorna apenas registros que correspondem em ambas as fontes</td>
      </tr>
      <tr>
        <td><span class="code">how='left'</span></td>
        <td><span class="code">LEFT JOIN</span></td>
        <td class="description">Retorna todos os registros da fonte esquerda + correspondências da direita</td>
      </tr>
      <tr>
        <td><span class="code">how='right'</span></td>
        <td><span class="code">RIGHT JOIN</span></td>
        <td class="description">Retorna todos os registros da fonte direita + correspondências da esquerda</td>
      </tr>
      <tr>
        <td><span class="code">how='outer'</span></td>
        <td><span class="code">FULL OUTER JOIN</span></td>
        <td class="description">Retorna todos os registros de ambas as fontes</td>
      </tr>
    </tbody>
  </table>
</body>
</html>

![](https://mermaid.ink/img/pako:eNp1ksFOGzEQhl9l5EtASlrRclqpHFASGpSGiC6XZhGarmezFrv2dmwDVciVB-AR-ip9kz4JXqftbqH4NLa_-e3_12xEbiSJRBSVuc1LZAfpONMQlvVf14xNCUvUEu0qE00s3tTEa9rbz8TlDmyXVEy5U0ZDetydLg9WY3Q4ZawJJvabJ5bmEkajI7iPKvewfNdDxkFFOdPTXb5fhYd7e-SfP2pybCw0rHSuGlQ26YARlOY2gc2gosINhjBgtS5jobQmbgvjXSi2_R6jE8hN5TXu2X0IOdxQ_7rVujL6bdS66mAbfBfEpB3ZPm99Uag7sklbqbv2r8jYNfmmUjmGLLumkGbf9uG_tlPVBBFJEFN7bvfDb2_w6-ERZovF5BxOz2aLF1SMJELzyTT9P7OLK0Lns5OPr1C7CCM1vZjP4ewiffFo54i0zHSmxVCE79eoZJi3TXuVCVdSTZlIQimRr1vP28Chd-bzd52LxLGnoWDj16VICqxs2PlGoqOxwjCf9R8kDOcXY-q_EMkwSfxpN91xyLdPPtLm3w?type=png)

#### Quem está dentro, quem está fora: `inner` e `outer`

Examinemos com um pouco mais de detalhe as operações `inner` e `outer`. Para estabelecer comparações com as outras operações, estas duas primeiras consideram as duas fontes, ou tabelas, *na sua totalidade*. Isso quer dizer que, no momento da atuação do pandas, este dará o mesmo foco para quaisquer dados que apareçam para o mesmo juntar. Aqui, a diferença estará no que sairá de resultado.

A operação `outer` é, digamos, a mais simples dentre as que estamos analisando. Isso porque a única coisa que o pandas faz é juntar aquilo que aparece nas duas fontes igualmente, e incluir aquilo que aparece ou em uma, ou em outra fonte.

```python
df = pd.merge(df1, df2, how='outer')
```

A operação `inner`, por sua vez, é marcada pelo fato de haver a separação de apenas aquilo que aparece nas duas tabelas/fontes que estamos analisando; ou seja, o que é comum entre os dois conjuntos de dados. O que é único dentro de cada conjunto fica fora do resultado.

```python
df = pd.merge(df1, df2, how='inner')
```
#### Considerando um ou outro: `left` e `right`

Podemos dizer que as operações `left` e `right`, diferente das duas anteriores, estabelecem um foco especial em apenas uma das duas fontes, por vez. E isso se dá a partir da *posição* das tabelas no código que escrevemos. "Como assim?", você deve estar se perguntando. Explico:

- Assim como nas línguas portuguesa, inglesa, e tantas outras, o sentido de leitura de um código Python se dá da *esquerda* para a *direita*. Imagine uma operação matemática simples:
```python
5 + 2 + 3 + 12
```
Intuitivamente, quando usamos a calculadora, ou fazemos o cálculo à mão, ou usamos a nossa imaginação para tanto, ordenamos os números da esquerda para a direita: Pegamos primeiro o `5`, somamos com o `2`, e assim por diante, até chegarmos ao resultado.
```python
22
```
- Com as fontes que usaremos para a mescla, é basicamente a mesma coisa. Observe a seguinte linha de código:
```python
pd.merge(df1, df2, how='left', on='ID')
```
Por ser a primeira a aparecer na ordem de leitura, a fonte `df1` assumirá a posição da *esquerda*, enquanto `df2` assume a posição da *direita*.

A partir disso, a diferença entre `left` e `right` como operações do parâmetro `how` fica bem mais clara. Ao utilizarmos o primeiro, damos foco à fonte de dados da *esquerda*, adquirindo da outra fonte à direita apenas o que é semelhante a ambos. Já o `right` tem como foco a tabela da direita, operando da mesma forma. 

### Cadê a chave? O parâmetro `on`

Até aqui, foi possível entender de que forma a função `merge()` funciona no que se refere a de que jeito vamos mesclar os conjuntos de dados que temos, de acordo com o foco que o pandas dará em relação às tabelas e suas posições. Tendo esse conhecimento adquirido, já é possível lançar mão da função para fazer mesclas. 

Entretanto, em muitos casos, a indicação das posições por si só não fará com que alcancemos o resultado que estamos esperando, uma vez que, caso tenhamos uma ou mais colunas em comum para os dois conjuntos de dados, uma mescla 'pura' pode gerar duplicidades, alteração em colunas que possuem nomes iguais... em suma, isso vem gerar uma confusão que nos dará mais trabalho depois. Mas, na própria função `merge()`, há uma solução simples: apontar as *chaves* de cada tabela, a partir do parâmetro `on`.

Voltando ao SQL e adicionando um pouco de conceitos básicos de banco de dados, sabemos que, em um conjunto de dados dentro de um banco é possível ter o que chamamos de chaves; estas nada mais são do que valores únicos pelos quais podemos identificar dados dentro de uma tabela. 

Para entender melhor, imaginemos o seguinte: Temos em mãos dois DataFrames com dados envolvendo características corpóreas de certas espécies de mamíferos, conforme o bloco de código a seguir:

```python
import pandas as pd
import numpy as np

# Criando o primeiro DataFrame com características físicas principais
df1 = pd.DataFrame({
    'Especie': ['Leão', 'Tigre', 'Urso Pardo', 'Lobo Cinzento', 'Jaguar'],
    'comprimento_cm': [250, 300, 200, 150, 180],
    'tamanho_pes_cm': [18, 20, 25, 15, 16],
    'tamanho_presas_cm': [8, 7.5, 5, 6, 7]
})

# Criando o segundo DataFrame com características faciais
df2 = pd.DataFrame({
    'Especie': ['Leão', 'Tigre', 'Urso Pardo', 'Lobo Cinzento', 'Onça Pintada'],
    'tamanho_garras_cm': [6, 8, 10, 4, 7],
    'tamanho_focinho_cm': [20, 22, 35, 25, 18]
})
```

Aqui, já de início, podemos perceber uma coisa: a existência de uma coluna com nome e valores em comum. Esta coluna, `Especie`, vem a servir de identificador dos DataFrames, uma vez que possui valores que, em princípio, não se repetem na tabela. Desse modo, podemos encarar essa coluna como a *chave* dos DataFrames que foram criados.

Seguindo a ideia dessa aula, imaginemos a situação de mesclar os dois DataFrames que criamos, com o intuito de juntar todas as características corpóreas de cada espécie registrada nesses dois conjuntos de dados. É perfeitamente possível se valer da função `merge()` indicando apenas os dois `df`, conforme exemplo abaixo:

```python
df3 = pd.merge(df1, df2, how='outer')
```

Existe aí, no entanto, um porém: Ao executarmos esse código, e verificarmos seu resultado, é possível encontrar a coluna `Especie` repetida, com indicadores atrelados ao nome (algo como `Especie_x` e `Especie_y`). Isso, querendo nós ou não, atrapalha muito na hora de fazer quaisquer análises. E o que faremos com isso, então?

Nesse blog, procuramos sempre preconizar o princípio de que existirá uma solução efetiva para os problemas que encontramos na análise de dados usando Python; aqui não será diferente. Para resolvermos essa questão de uma vez por todas, basta complementarmos a linha de código anterior fazendo menção à coluna que as tabelas possuem em comum usando o parâmetro `on`, conforme exemplo:

```python
df3 = pd.merge(df1, df2, how='outer', on='Especie')
```

Adicionando valor a esse parâmetro, indicaremos ao pandas que, uma vez encontrado um valor comum de `Especie` em ambos os conjuntos de dados, a única coisa que é preciso fazer é uma complementação dos dados que já existem, adicionando novas colunas e os valores atrelados a elas. Assim teremos um DataFrame único em `df3`, sem colunas repetidas, e com todos os dados disponíveis nas duas tabelas, prontas para que possamos tomar quaisquer outras ações posteriores.

---

Vimos até aqui duas formas muito interessantes de combinar dados oriundos de fontes distintas, `concat()` e `merge()`. Com elas, sem a menor sombra de dúvida, você já estará pronto para organizar os vários fragmentos de dados que acaso poderá encontrar ao lidar com arquivos do mundo real. Ainda nos falta discutir um pouco sobre uma outra função que também serve para combinações e mesclas, o `join()`. Isso faremos em uma próxima oportunidade, de forma mais detalhada e clara.

Um grande abraço, e até o próximo post!

---

**Para ler mais:**

* [**Pandas Merging 101 - StackOverflow**](https://stackoverflow.com/questions/53645882/pandas-merging-101)
    
* [**What are the 'levels', 'keys', and names arguments for in Pandas' concat function? - Stack Overflow**](https://stackoverflow.com/questions/49620538/what-are-the-levels-keys-and-names-arguments-for-in-pandas-concat-functio/49620539#49620539)
    
* [**Python Pandas DataFrame Join, Merge, and Concatenate - Towards Data Science**](https://towardsdatascience.com/python-pandas-dataframe-join-merge-and-concatenate-84985c29ef78)
    
* [**Practical uses of merge, join and concat - Towards Data Science**](https://towardsdatascience.com/practical-uses-of-merge-join-and-concat-8f011bbac241)

* [**Combining Data in pandas With merge(), .join(), and concat() - Real Python**](https://realpython.com/pandas-merge-join-and-concat/)
