#01Python - Valores ausentes (Nan | Null)
Published: 2023-08-16 15:59:48

## Tipos de Ausência de Dados

Existem tipos de padrões de ausências de dados:

1. **MCAR (Missing Completely At Random)**: MCAR significa "ausência de dados completamente aleatória". Nesse caso, a ausência dos dados é totalmente aleatória e não está relacionada a nenhuma outra variável do conjunto de dados. Isso significa que a probabilidade de um valor estar ausente é a mesma para todas as observações, não depende de valores não observados em outras variáveis. Em outras palavras, dados ausentes não introduzem nenhum viés sistemático na análise.
2. **MAR (Missing At Random)**: MAR significa "ausência de dados aleatória". A ausência dos dados pode estar relacionada a outras variáveis observadas, mas não está relacionada ao próprio valor ausente. Em outras palavras, a probabilidade de um valor estar ausente pode depender das informações disponíveis em outras variáveis, mas não depende do valor real que está faltando. Mesmo existindo uma relação entre a ausência dos dados e outras variáveis, desde que essas variáveis estejam presentes no conjunto de dados, não há viés sistemático introduzido na análise.
3. **MNAR (Missing Not At Random)**: MNAR quer dizer "ausência de dados não aleatória". Significa que, a ausência dos dados está relacionada ao próprio valor ausente, e essa relação não pode ser explicada por outras variáveis no conjunto de dados. Ou seja, a probabilidade de um valor estar ausente depende do valor real que está faltando, independentemente das outras variáveis observadas. A ausência dos dados introduz um viés sistemático na análise, devido a existir uma relação entre os dados ausentes e o resultado que se deseja observar.

É importante entender o tipo de ausência de dados ao trabalhar com o dataset, pois cada tipo de ausência requer diferentes estratégias de tratamento ou métodos de imputação de dados para lidar com os valores ausentes. Conhecer os tipos de ausência de dados também pode ajudar a interpretar corretamente os resultados e evitar conclusões falsas ou enviesadas.

Obs.: "Não há viés sistemático introduzido" significa que a ausência de dados não afeta a análise de maneira tendenciosa ou sistemática. Ou seja, a ausência dos dados não influencia os resultados de forma consistente.

---

## Identificando valores ausentes Null

Para identificar valores ausentes, utilizamos o método **`isnull()**` do pandas para verificar quais valores são nulos no dataframe. Por exemplo:

```python
df.isnull()
```

Esse comando retorna um dataframe com o mesmo formato do original, mas com valores booleanos indicando se cada elemento é nulo ou não. Pode ser usado, também, o método `**.isna()**`, descrito abaixo.

---

## Identificando valores ausentes NaN

Nan → Not a Number.

**`isna()`** é um método do pandas que retorna uma matriz booleana indicando quais elementos são valores ausentes (NaN) ou nulos. Possui a mesma funcionalidade que o método **`isnull()`** .

A função **`isna()`** pode ser aplicada a um dataframe inteiro ou a uma série específica dentro do dataframe. Quando usada em todo o dataframe, ela retorna um dataframe com a mesma forma do aplicado, onde cada elemento é substituído por **`True`** se for um valor nulo ou **`False`,** caso contrário.

Segue um exemplo de uso do método **`isna()`**:

```python
import pandas as pd

df = pd.DataFrame({'A': [1, 2, None], 'B': [3, None, 5]})
print(df.isna())
```

---

## Contagem de valores ausentes

Para ter uma visão geral dos valores ausentes em cada coluna, você pode usar o método `sum()`

em combinação com o método `isnull()`. Por exemplo:

```python
df.isnull().sum()
```

Esse comando retorna o número total de valores ausentes em cada coluna. O mesmo pode ser aplicado ao comando **`isna()`** . Por exemplo:

```python
df.isna().sum()
```

---

## Visualização de dados Ausentes [Missigno]

A biblioteca **`missingno`** é uma ferramenta útil para visualizar padrões de dados ausentes em um dataframe. Ela gera gráficos que ajudam a identificar os padrões de valores ausentes e a entender a distribuição desses valores em um dataframe. Alguns dos seus principais gráficos são:

1. **Matriz de ausência (Matrix Plot)**: Mostra a presença ou ausência de valores em cada célula do dataframe. Cada linha representa uma amostra ou registro, e cada coluna representa uma variável, as células vazias/em branco indicam valores ausentes. Tornando visível padrões e correlações entre os valores ausentes.
2. **Gráfico de barras de valores ausentes (Bar Chart)**: Exibe a contagem de valores ausentes para cada variável. Ele mostra a proporção de valores ausentes em relação ao número total de observações disponíveis para cada variável. É possível identificar quais variáveis têm um número significativo de valores ausentes.
3. **Gráfico de calor (Heatmap)**: Utiliza cores para representar a presença ou ausência de valores em um dataframe. É útil quando se trabalha com grandes conjuntos de dados, pois permite visualizar a distribuição de valores ausentes em várias variáveis.

A biblioteca **`missingno`** também fornece outras visualizações, como dendrogramas de correlação e gráficos de linha para rastrear valores ausentes ao longo do tempo.

Segue um exemplo de utilização da biblioteca **`missingno`**:

```python
import missingno as msno
import matplotlib.pyplot as plt

# Matriz de ausência
msno.matrix(df)
plt.show()

# Gráfico de barras de valores ausentes
msno.bar(df)
plt.show()

# Gráfico de calor
msno.heatmap(df)
plt.show()
```

---

## Definindo limite para valores ausentes por coluna

Não existe um padrão ou regra para determinar o limite de valores ausentes por coluna, pois isso pode variar dependendo do problema, da natureza dos dados e dos requisitos da análise. 

No entanto, existem algumas abordagens para definir esse limite:

1. **Limite percentual**: Pode-se estabelecer um limite percentual, como por exemplo, permitir uma coluna ter até 5% (ou qualquer outro valor) de valores ausentes. 
Caso a proporção de valores ausentes em uma coluna exceder esse limite, podem ser tomadas medidas como imputação de valores ou exclusão da coluna.
2. **Análise do domínio**: Certas colunas podem ser mais críticas e requerer menos valores ausentes, enquanto outras, podem ter uma liberdade maior para a ausência de dados. 
Por exemplo, em um dataframe de dados médicos, variáveis como idade ou sexo podem ser consideradas essenciais, já outras colunas, com informações mais específicas, podem possuir mais valores ausentes.
3. **Impacto nos resultados**: Deve-se considerar o impacto dos valores ausentes nos resultados finais. Se uma coluna contiver informações críticas para o problema em questão ou for necessária para construção do resultado, é recomendável ter um limite mais baixo de valores ausentes.

É importante documentar o processo de tratamento de dados ausentes, principalmente a decisão sobre o limite de valores ausentes.

---

## Lidando com valores ausentes

Existem várias estratégias para lidar com valores ausentes. Aqui estão algumas opções comuns:

### Removendo linhas ou colunas

Se os valores ausentes estiverem em um número pequeno de linhas ou colunas, você pode optar por removê-las. Use o método **`dropna()`** para fazer isso. Por exemplo, para remover todas as linhas que contenham pelo menos um valor nulo:

```python
# Dropando linhas com valores ausentes:
df = df.dropna()
```

Para remover colunas com pelo menos um valor nulo, especifique o parâmetro **`axis=1`**:

```python
# Dropando colunas com valores ausentes:
df = df.dropna(axis=1)
```

Segue abaixo, um exemplo de exclusão de linhas de valores ausentes abaixo de um limite de 5% em um dataframe e, análise se há colunas com valores ausentes acima do limite definido:

```python
# Definindo threshold (limite):
threshold = len(df)*0.05

# Colunas para dropar linhas de valores ausentes (limite < 5%):
cols_to_drop_na = df.columns[df.isna().sum() <= threshold]

# Dropando valores ausentes de colunas abaixo do limite aceitável:
df.dropna(subset=cols_to_drop_na, inplace=True)

# Verificando se ainda há colunas com valores ausentes:
print(df.isna().sum)

# Se há, definimos essas colunas para futura tratativa:
cols_with_missing_values = df.columns[df.isna().sum() > 0]
print(cols_with_missing_values)
```

### Substituindo valores ausentes por valores estatísticos

Se você preferir manter todas as linhas e colunas, pode preencher os valores ausentes com um valor específico. Use o método **`fillna()`** para preencher os valores nulos. 
No exemplo abaixo, se preenche todos os valores nulos com ‘0’:

```python
df = df.fillna(0)
```

Você também pode preencher com outros valores, como estatísticas da coluna:

```python
# Preencher valores ausentes com a média da coluna
df_filled_mean = df.fillna(df.mean())

# Preencher valores ausentes com a mediana da coluna
df_filled_median = df.fillna(df.median())

# Preencher valores ausentes com o desvio padrão da coluna
df_filled_std = df.fillna(df.std())

# Preencher valores ausentes com a moda da coluna (valor mais frequente)
df_filled_mode = df.apply(lambda col: col.fillna(col.mode()[0]) if col.isna().any() else col)
```

Ou, estatísticas avançadas como a mediana ponderada:

```python
# Definir pesos para a mediana ponderada
weights = [1, 2, 1, 3, 1]

# Função para calcular a mediana ponderada
def weighted_median(values, weights):
    sorted_indices = np.argsort(values)
    sorted_values = np.array(values)[sorted_indices]
    sorted_weights = np.array(weights)[sorted_indices]
    cumsum_weights = np.cumsum(sorted_weights)
    total_weight = cumsum_weights[-1]
    median_idx = np.argmax(cumsum_weights >= total_weight / 2)
    return sorted_values[median_idx]

# Preencher valores ausentes com a mediana ponderada da coluna
df_filled_weighted_median = df.apply(lambda col: col.fillna(weighted_median(col.dropna(), weights)) if col.isna().any() else col)
```

Mas esteja ciente que, preencher valores ausentes com outros valores, pode trazer gráficos de análise com perspectivas falsas, dependendo da quantidade de valores preenchidos.

### Medidas estatísticas por subgrupos

Preencher valores ausentes com medidas estatísticas segmentadas por subgrupos dentro do dataframe é uma técnica útil quando você deseja imputar valores ausentes com base em características específicas de subconjuntos dos seus dados. 
Isso permite levar em consideração a heterogeneidade dos dados e evitar distorções ao preencher os valores ausentes com medidas estatísticas gerais.

Segue um exemplo de como preencher valores ausentes com média de um grupo:

```python
# Calcular a média por grupo
group_means = df.groupby('Grupo')['Valor'].mean()

# Preencher valores ausentes com a média por grupo
df_filled_segmented_mean = df.groupby('Grupo')['Valor'].apply(lambda x: x.fillna(x.mean()))

print("DataFrame preenchido com a média segmentada por grupo:\n", df_filled_segmented_mean)
```

Esse código pode ser adaptado para outras medidas estatísticas, como a mediana, desvio padrão, moda, entre outras. Basta substituir a função estatística apropriada dentro da função **`apply()`**.

### Interpolar valores ausentes

Outra opção é usar a interpolação para preencher os valores ausentes com base nos valores existentes nas colunas. O método **`interpolate()`** faz isso automaticamente. Por exemplo:

```python
df = df.interpolate()
```

---

## Verificar novamente valores ausentes após transformações

Depois de realizar as etapas de tratamento de valores ausentes, verifique novamente se restam valores nulos no dataframe, para garantir que os valores ausentes tenham sido tratados corretamente.

```python
# Para valores nulos:
df.isnull().sum()

# Ou:
df.isna().sum()
```