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

Nas últimas aulas desta série, vimos o poder do `pandas` para fazer junções de diferentes fontes de dados, facilitando assim a vida de quem tem precisado lidar com tabelas fragmentadas. Até o momento, discutimos com mais detalhes as funções `concat()` e `merge()`; falta-nos agora a apresentação da última função, `join()`.

## O nome já diz: a função `join()`

Quando você vê o nome da função, `join()`, é possível que pense outra vez na cláusula `JOIN` do SQL. E a verdade é… que é por aí mesmo. Veja a tabela abaixo; a função do pandas nada mais é que uma “tradução” das funções de `JOIN` para uma forma a ser utilizada através de uma biblioteca Python.

| pandas join() | SQL JOIN | Operação |
| --- | --- | --- |
| how='inner' | INNER JOIN | Retorna apenas registros que correspondem em ambas as fontes |
| how='left' | LEFT JOIN | Retorna todos os registros da fonte esquerda + correspondências da direita |
| how='right' | RIGHT JOIN | Retorna todos os registros da fonte direita + correspondências da esquerda |
| how='outer' | FULL OUTER JOIN | Retorna todos os registros de ambas as fontes |

Agora, se você está conosco há algum tempo e se lembra das aulas anteriores, essas informações vão parecer um tanto familiares. E são familiares mesmo! Perceba que as características batem com uma outra função do pandas, `merge()`.

### Mas é tudo igual *mesmo*? Comparando `join()` com `merge()`

É possível que você pense que a função `join()`seja uma mera redundância do `merge()`, e que a escolha de qual recurso utilizar é indiferente quando se quer fazer uma combinação de DataFrames. No entanto, as coisas não precisam ser (e não são, na verdade) assim. Dê uma olhada na tabela abaixo; aqui você verá duas coisas: apontamentos sobre as diferenças entre as funções do pandas e a equivalência com a cláusula do SQL.

| Característica | pandas join() | pandas merge() | SQL JOIN |
|----------------|---------------|----------------|-----------|
| Uso Principal | Combinar DataFrames baseado em índices | Combinar DataFrames baseado em colunas | Combinar tabelas baseado em colunas |
| Sintaxe Básica | `df1.join(df2)` | `df1.merge(df2, on='coluna')` | `SELECT * FROM t1 JOIN t2 ON t1.col = t2.col` |
| Base da Junção | Índices por padrão, pode usar colunas com `on` | Colunas específicas usando `on`, `left_on`, `right_on` | Colunas específicas usando cláusula `ON` |
| Tipos de Join | - `how='left'`<br>- `how='right'`<br>- `how='inner'`<br>- `how='outer'` | - `how='left'`<br>- `how='right'`<br>- `how='inner'`<br>- `how='outer'`<br>- `how='cross'` | - `LEFT JOIN`<br>- `RIGHT JOIN`<br>- `INNER JOIN`<br>- `FULL OUTER JOIN`<br>- `CROSS JOIN` |
| Tratamento de Colunas Duplicadas | Usa `lsuffix` e `rsuffix` | Usa `suffixes` | Requer aliases explícitos na consulta |
| Melhor Para | - Junções simples baseadas em índice<br>- Dados já indexados corretamente<br>- Operações mais diretas | - Junções complexas<br>- Múltiplas colunas de junção<br>- Maior flexibilidade | - Junções complexas<br>- Múltiplas condições<br>- Operações em banco de dados |
| Limitações | - Menos flexível para junções complexas<br>- Primariamente focado em índices | - Sintaxe mais verbosa<br>- Pode ser complexo para junções simples | - Requer conhecimento de SQL<br>- Menos integrado com análise de dados em Python |
| Performance | Melhor para junções baseadas em índice | Melhor para junções complexas de DataFrames | Otimizado para operações em banco de dados |

Desta tabela, uma coisa fica clara: enquanto `merge()` dá a possibilidade de combinar DataFrames baseados em *colunas*, a concentração de `join()` se dá nos *índices* do DataFrame. Para lembrar o que são índices para o [pandas](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.index.html):

> The index of a DataFrame is a series of labels that identify each row. The labels can be integers, strings, or any other hashable type. The index is used for label-based access and alignment, and can be accessed or modified using this attribute.

Traduzindo, é algo assim:

> O índice de um DataFrame é uma série de etiquetas (labels) que identificam cada linha. Podem ser números inteiros, strings, ou qualquer tipo de dado que possa ser utilizada como chave. É utilizado para acesso e alinhamento de dados baseando-se em etiquetas, podendo acessar e modificar linhas usando esse mesmo atributo.

Então, em vez de nos orientarmos pelos nomes das colunas, como em `merge()`, podemos nos valer de, digamos, os “nomes” das linhas usando o `join()`.

### Um exemplo prático

Comecemos o exemplo criando dois DataFrames de características envolvendo aspectos corpóreos de um grupo limitado de aves. Nesse caso, como usaremos o `join()`, daremos uma atenção especial aos índices de cada DataFrame. Cada linha das tabelas possuirá um nome científico de uma ave em específico.

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

# Criando DataFrame com dados das asas
df_asas = pd.DataFrame({
    'envergadura_cm': [240, 180, 85, 120, 300],
    'comp_dedo_medio_cm': [12, 8, 4, 6, 15],
    'comp_asa_cm': [110, 85, 40, 55, 145]
}, index=['Aquila chrysaetos',  # Águia-real
          'Buteo buteo',        # Águia-de-asa-redonda
          'Falco tinnunculus',  # Peneireiro-vulgar
          'Accipiter nisus',    # Gavião-da-europa
          'Gyps fulvus'])       # Grifo

# Criando DataFrame com dados das pernas
df_pernas = pd.DataFrame({
    'comp_tarso_cm': [9.5, 7.2, 4.5, 5.8, 10.2],
    'comp_total_perna_cm': [35, 28, 18, 22, 40],
    'circunf_tarso_cm': [8, 6, 3, 4, 9]
}, index=['Aquila chrysaetos',    # Águia-real
          'Buteo buteo',          # Águia-de-asa-redonda
          'Falco tinnunculus',    # Peneireiro-vulgar
          'Strix aluco',          # Coruja-do-mato
          'Gyps fulvus'])         # Grifo
```

Perceba já de início que existem alguns índices que possuem nomes iguais (que é o intuito dessa atividade); esta parte é muito importante para usar o `join()`. A partir daqui, então, já podemos “traduzir” as cláusulas do SQL para argumentos da função, assim como fizemos com o `merge()` anteriormente, como no bloco de código abaixo:

```python
# Exemplo 1: Inner join (apenas espécies que aparecem em ambos os DataFrames)
df_inner = df_asas.join(df_pernas, how='inner')

# Exemplo 2: Left join (todas as espécies do DataFrame de asas)
df_left = df_asas.join(df_pernas, how='left')

# Exemplo 3: Right join (todas as espécies do DataFrame de pernas)
df_right = df_asas.join(df_pernas, how='right')

# Exemplo 4: Outer join (todas as espécies de ambos os DataFrames)
df_outer = df_asas.join(df_pernas, how='outer')
```

Como resultado, teremos para cada variável criada um DataFrame com os dados de asas e pernas de aves conforme o tipo de `JOIN` selecionado. Note que existem espécies que ocorrem em apenas uma das tabelas. Isso não vem a atrapalhar a mescla como um todo, mas é importante salientar que, no caso das colunas referentes ao outro DataFrame (onde a espécie não está descrita), os dados virão nulos.

## O resumo da ópera

Chegando ao final das discussões sobre as diferentes formas de mesclar DataFrames usando pandas, podemos resumir tudo em uma tabela como esta abaixo, estabelecendo também comparações com a cláusula JOIN do SQL:

# Comparação entre métodos de junção pandas e SQL

# Comparação entre métodos de junção pandas e SQL

| Característica | pandas join() | pandas merge() | pandas concat() | SQL JOIN |
|----------------|---------------|----------------|-----------------|----------|
| Uso Principal | Combinar DataFrames baseado em índices | Combinar DataFrames baseado em colunas | Empilhar DataFrames (vertical ou horizontal) | Combinar tabelas baseado em colunas |
| Sintaxe Básica | `df1.join(df2)` | `df1.merge(df2, on='coluna')` | `pd.concat([df1, df2])` | `SELECT * FROM t1 JOIN t2 ON t1.col = t2.col` |
| Base da Junção | Índices por padrão, pode usar colunas com `on` | Colunas específicas usando `on`, `left_on`, `right_on` | Posição (eixo) dos DataFrames | Colunas específicas usando cláusula `ON` |
| Tipos de Operação | - `how='left'`<br>- `how='right'`<br>- `how='inner'`<br>- `how='outer'` | - `how='left'`<br>- `how='right'`<br>- `how='inner'`<br>- `how='outer'`<br>- `how='cross'` | - `axis=0` (vertical)<br>- `axis=1` (horizontal)<br>- `join='outer'`<br>- `join='inner'` | - `LEFT JOIN`<br>- `RIGHT JOIN`<br>- `INNER JOIN`<br>- `FULL OUTER JOIN`<br>- `CROSS JOIN` |
| Tratamento de Duplicatas | Usa `lsuffix` e `rsuffix` | Usa `suffixes` | Usa `ignore_index` e `keys` | Requer aliases explícitos na consulta |
| Melhor Para | - Junções simples baseadas em índice<br>- Dados já indexados corretamente<br>- Operações mais diretas | - Junções complexas<br>- Múltiplas colunas de junção<br>- Maior flexibilidade | - Combinar DataFrames sequencialmente<br>- Empilhar dados vertical/horizontalmente<br>- Manter índices originais | - Junções complexas<br>- Múltiplas condições<br>- Operações em banco de dados |
| Limitações | - Menos flexível para junções complexas<br>- Primariamente focado em índices | - Sintaxe mais verbosa<br>- Pode ser complexo para junções simples | - Não faz junção baseada em valores<br>- Limitado a operações de empilhamento | - Requer conhecimento de SQL<br>- Menos integrado com análise em Python |
| Performance | Melhor para junções baseadas em índice | Melhor para junções complexas de DataFrames | Eficiente para empilhamento simples | Otimizado para operações em banco de dados |
| Exemplo de Uso Comum | Combinar dados financeiros usando datas como índice | Combinar dados de vendas usando ID do cliente | Combinar relatórios mensais sequencialmente | Combinar dados de diferentes tabelas relacionais |

---

Esta é, portanto, a última aula que explora as três funções que podemos usar para juntar tabelas usando o pandas: `concat()`, `join()`, e `merge()`. Continuaremos a explorar outros atributos do pandas e do Python para analisar dados, especialmente os dados ecológicos, nas próximas aulas a serem publicadas em breve.

Aproveite o conteúdo para seguir estudando! Um abraço e até a próxima!

---

**Para ler mais:**

* [Join no Pandas - Asimov Academy](https://hub.asimov.academy/tutorial/join-no-pandas-como-utilizar-o-metodo-join-de-dataframes/)
    
* [What is the difference between join and merge in Pandas? - Stack Overflow](https://stackoverflow.com/questions/22676081/what-is-the-difference-between-join-and-merge-in-pandas)
