📊 Estatística Descritiva para Dados Biológicos

🎯 Objetivo da Lição

Aprender a calcular e interpretar estatísticas descritivas essenciais para análise de dados de pesquisa: médias, medianas, desvio padrão, quartis e muito mais.

Por que é importante: - Resumir grandes conjuntos de dados - Identificar padrões e anomalias - Tomar decisões baseadas em evidências - Comunicar resultados de forma clara


📐 Medidas de Tendência Central

Média Aritmética

Definição: Soma de todos os valores dividido pela quantidade.

import numpy as np

# Biomassas coletadas (gramas)
biomassas = [245.3, 180.7, 310.2, 198.5, 220.4, 302.1, 195.8, 275.4]

# Calcular média
media = np.mean(biomassas)
print(f"Média: {media:.2f}g")  # 241.05g

# Ou manualmente:
media_manual = sum(biomassas) / len(biomassas)

Quando usar: Dados simétricos sem outliers extremos.


Mediana

Definição: Valor central quando os dados estão ordenados.

# Calcular mediana
mediana = np.median(biomassas)
print(f"Mediana: {mediana:.2f}g")  # 233.75g

# Manualmente:
biomassas_ordenadas = sorted(biomassas)
meio = len(biomassas_ordenadas) // 2
if len(biomassas_ordenadas) % 2 == 0:
    mediana_manual = (biomassas_ordenadas[meio-1] + biomassas_ordenadas[meio]) / 2
else:
    mediana_manual = biomassas_ordenadas[meio]

Quando usar: Dados com outliers ou distribuição assimétrica.


Moda

Definição: Valor que aparece com maior frequência.

from scipy import stats

especies = ['Ulva lactuca', 'Gracilaria', 'Ulva lactuca', 'Sargassum', 
            'Ulva lactuca', 'Gracilaria', 'Ulva lactuca']

moda = stats.mode(especies, keepdims=True)
print(f"Espécie mais comum: {moda.mode[0]}")  # Ulva lactuca
print(f"Frequência: {moda.count[0]} vezes")   # 4 vezes

📏 Medidas de Dispersão

Variância e Desvio Padrão

Variância: Média dos quadrados das diferenças em relação à média.
Desvio Padrão: Raiz quadrada da variância (mesma unidade dos dados).

# Temperaturas registradas (°C)
temperaturas = [24.5, 26.1, 22.3, 21.5, 19.8, 18.2, 17.5, 18.0, 19.5, 21.0]

# Variância
variancia = np.var(temperaturas, ddof=1)  # ddof=1 para amostra
print(f"Variância: {variancia:.2f} °C²")

# Desvio padrão
desvio_padrao = np.std(temperaturas, ddof=1)
print(f"Desvio padrão: {desvio_padrao:.2f} °C")

# Interpretação
media_temp = np.mean(temperaturas)
print(f"\nTemperatura: {media_temp:.1f} ± {desvio_padrao:.1f} °C")

Interpretação: - Desvio padrão baixo → Dados concentrados perto da média - Desvio padrão alto → Dados dispersos


Coeficiente de Variação

Definição: Desvio padrão relativo à média (em %).

cv = (desvio_padrao / media_temp) * 100
print(f"Coeficiente de Variação: {cv:.2f}%")

# Interpretação:
if cv < 10:
    print("Variabilidade: BAIXA")
elif cv < 30:
    print("Variabilidade: MODERADA")
else:
    print("Variabilidade: ALTA")

Amplitude e Amplitude Interquartil

# Amplitude total (range)
amplitude = max(temperaturas) - min(temperaturas)
print(f"Amplitude: {amplitude:.1f} °C")

# Quartis
q1 = np.percentile(temperaturas, 25)  # 1º quartil (25%)
q2 = np.percentile(temperaturas, 50)  # 2º quartil (mediana)
q3 = np.percentile(temperaturas, 75)  # 3º quartil (75%)

# Amplitude interquartil (IQR)
iqr = q3 - q1
print(f"\nQ1 (25%): {q1:.1f} °C")
print(f"Q2 (50%): {q2:.1f} °C")
print(f"Q3 (75%): {q3:.1f} °C")
print(f"IQR: {iqr:.1f} °C")

IQR: Contém os 50% centrais dos dados (menos sensível a outliers).


📊 Exemplo Completo: Análise de Coletas

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy import stats

# Dataset de coletas
dados = {
    'praia': ['Ingleses', 'Ingleses', 'Barra', 'Barra', 'Armação', 
              'Armação', 'Garopaba', 'Garopaba', 'Laguna', 'Laguna'],
    'especie': ['Ulva', 'Gracilaria', 'Ulva', 'Sargassum', 'Ulva', 
                'Gracilaria', 'Ulva', 'Laminaria', 'Ulva', 'Gracilaria'],
    'biomassa_g': [245.3, 180.7, 302.1, 310.2, 275.4, 195.8, 310.8, 425.8, 295.7, 205.6],
    'temperatura_c': [24.5, 24.5, 22.3, 23.8, 21.5, 21.5, 19.8, 21.0, 18.0, 18.0],
    'profundidade_m': [3.2, 5.1, 3.5, 4.5, 3.8, 5.0, 3.6, 10.5, 3.7, 5.4]
}

df = pd.DataFrame(dados)

# ====================
# ESTATÍSTICAS GERAIS
# ====================

print("="*60)
print("📊 ESTATÍSTICAS DESCRITIVAS - BIOMASSA")
print("="*60)

biomassas = df['biomassa_g']

print(f"\n📏 Tendência Central:")
print(f"   Média: {biomassas.mean():.2f}g")
print(f"   Mediana: {biomassas.median():.2f}g")

print(f"\n📐 Dispersão:")
print(f"   Desvio Padrão: {biomassas.std():.2f}g")
print(f"   Variância: {biomassas.var():.2f}g²")
print(f"   CV: {(biomassas.std() / biomassas.mean() * 100):.2f}%")

print(f"\n📊 Valores Extremos:")
print(f"   Mínimo: {biomassas.min():.2f}g")
print(f"   Máximo: {biomassas.max():.2f}g")
print(f"   Amplitude: {biomassas.max() - biomassas.min():.2f}g")

print(f"\n🎯 Quartis:")
print(f"   Q1 (25%): {biomassas.quantile(0.25):.2f}g")
print(f"   Q2 (50%): {biomassas.quantile(0.50):.2f}g")
print(f"   Q3 (75%): {biomassas.quantile(0.75):.2f}g")
print(f"   IQR: {biomassas.quantile(0.75) - biomassas.quantile(0.25):.2f}g")

# ====================
# POR ESPÉCIE
# ====================

print("\n" + "="*60)
print("🌿 ESTATÍSTICAS POR ESPÉCIE")
print("="*60)

for especie in df['especie'].unique():
    dados_especie = df[df['especie'] == especie]['biomassa_g']

    print(f"\n📌 {especie}")
    print(f"   n = {len(dados_especie)}")
    print(f"   Média: {dados_especie.mean():.2f}g ± {dados_especie.std():.2f}g")
    print(f"   Mediana: {dados_especie.median():.2f}g")
    print(f"   Range: [{dados_especie.min():.2f}, {dados_especie.max():.2f}]g")

# ====================
# VISUALIZAÇÕES
# ====================

fig, axes = plt.subplots(2, 2, figsize=(12, 10))
fig.suptitle('📊 Análise Estatística de Coletas', fontsize=16, fontweight='bold')

# 1. Histograma
ax1 = axes[0, 0]
ax1.hist(biomassas, bins=6, edgecolor='black', alpha=0.7, color='skyblue')
ax1.axvline(biomassas.mean(), color='red', linestyle='--', linewidth=2, label=f'Média: {biomassas.mean():.1f}g')
ax1.axvline(biomassas.median(), color='green', linestyle='--', linewidth=2, label=f'Mediana: {biomassas.median():.1f}g')
ax1.set_xlabel('Biomassa (g)')
ax1.set_ylabel('Frequência')
ax1.set_title('Histograma de Biomassa')
ax1.legend()
ax1.grid(True, alpha=0.3)

# 2. Boxplot
ax2 = axes[0, 1]
ax2.boxplot(biomassas, vert=True)
ax2.set_ylabel('Biomassa (g)')
ax2.set_title('Boxplot de Biomassa')
ax2.grid(True, alpha=0.3)

# 3. Boxplot por espécie
ax3 = axes[1, 0]
especies_ordenadas = df.groupby('especie')['biomassa_g'].median().sort_values().index
df_plot = df.set_index('especie').loc[especies_ordenadas]
df_plot.boxplot(column='biomassa_g', by='especie', ax=ax3)
ax3.set_xlabel('Espécie')
ax3.set_ylabel('Biomassa (g)')
ax3.set_title('Biomassa por Espécie')
plt.sca(ax3)
plt.xticks(rotation=45, ha='right')

# 4. Scatter com tendência
ax4 = axes[1, 1]
ax4.scatter(df['temperatura_c'], df['biomassa_g'], s=100, alpha=0.6, edgecolors='black')
ax4.set_xlabel('Temperatura (°C)')
ax4.set_ylabel('Biomassa (g)')
ax4.set_title('Biomassa vs Temperatura')
ax4.grid(True, alpha=0.3)

# Linha de tendência
z = np.polyfit(df['temperatura_c'], df['biomassa_g'], 1)
p = np.poly1d(z)
ax4.plot(df['temperatura_c'], p(df['temperatura_c']), 
         "r--", alpha=0.8, linewidth=2, label='Tendência')
ax4.legend()

plt.tight_layout()
plt.savefig('estatisticas_descritivas.png', dpi=300)
print("\n✅ Gráfico salvo: estatisticas_descritivas.png")
plt.show()

print("\n" + "="*60)
print("✅ ANÁLISE CONCLUÍDA!")
print("="*60)

📈 Resumo dos 5 Números

Five-number summary: Resumo completo da distribuição.

def resumo_cinco_numeros(dados):
    """Calcula e exibe o resumo de 5 números"""
    minimo = np.min(dados)
    q1 = np.percentile(dados, 25)
    mediana = np.median(dados)
    q3 = np.percentile(dados, 75)
    maximo = np.max(dados)

    print("\n📊 RESUMO DOS 5 NÚMEROS:")
    print(f"   Mínimo:   {minimo:.2f}")
    print(f"   Q1 (25%): {q1:.2f}")
    print(f"   Mediana:  {mediana:.2f}")
    print(f"   Q3 (75%): {q3:.2f}")
    print(f"   Máximo:   {maximo:.2f}")

    return minimo, q1, mediana, q3, maximo

resumo_cinco_numeros(biomassas)

🔍 Identificação de Outliers

Método IQR: Valores fora de 1.5 × IQR são considerados outliers.

def identificar_outliers(dados):
    """Identifica outliers usando método IQR"""
    q1 = np.percentile(dados, 25)
    q3 = np.percentile(dados, 75)
    iqr = q3 - q1

    # Limites
    limite_inferior = q1 - 1.5 * iqr
    limite_superior = q3 + 1.5 * iqr

    # Identificar outliers
    outliers = [x for x in dados if x < limite_inferior or x > limite_superior]

    print(f"\n🔍 DETECÇÃO DE OUTLIERS:")
    print(f"   Limite inferior: {limite_inferior:.2f}")
    print(f"   Limite superior: {limite_superior:.2f}")

    if outliers:
        print(f"   ⚠️ {len(outliers)} outlier(s) detectado(s): {outliers}")
    else:
        print(f"   ✅ Nenhum outlier detectado")

    return outliers

identificar_outliers(df['biomassa_g'])

🎓 Checklist desta Lição


➡️ Próxima Lição


Você domina estatística descritiva! 📊✨