← Files Pesquisa MirandasTechARCHIVED FILE

scripts/analisar.py

6.18 KB · Oct 2, 2026 · 00:33 UTC

↓ Download file

#!/usr/bin/env python3
r"""Análise descritiva e comparação entre grupos a partir de um CSV — tabela em Markdown, teste com tamanho de efeito e IC, figura por script.

    python3 scripts/analisar.py dados.csv --variavel massa --grupo especie [--figura massa.png] [--markdown resultado.md]
    python3 scripts/analisar.py dados.csv --variavel massa --correlacao nadadeira
    python3 scripts/analisar.py dados.csv --categoria sexo --grupo especie          (tabela cruzada + qui-quadrado)

Regras: descritivas com n, média, DP, mediana, IQR e ausentes por grupo; 2 grupos → Welch t (com d de Cohen e IC 95 % da
diferença) e Mann-Whitney como robustez; 3+ grupos → ANOVA de Welch (η² aproximado) e Kruskal-Wallis; correlação → Pearson e
Spearman com IC; categoria × grupo → qui-quadrado e V de Cramér. Imprime tudo com 2 casas e o valor p exato (p < 0,001 quando
menor). A figura é gerada pelo script a partir dos dados (nunca desenhada à mão). Não decide nada: você interpreta.
"""
import argparse, math, sys
import numpy as np, pandas as pd
from scipy import stats

def fmt(p): return "p < 0,001" if p < 0.001 else f"p = {p:.3f}".replace(".", ",")
def br(x, d=2): return f"{x:.{d}f}".replace(".", ",")

def descritivas(df, v, g):
    linhas = ["| Grupo | n | Média | DP | Mediana | IQR | Ausentes |", "|---|---|---|---|---|---|---|"]
    grupos = [("todos", df)] + ([(k, s) for k, s in df.groupby(g)] if g else [])
    for k, s in grupos:
        x = s[v].dropna(); q1, q3 = x.quantile([0.25, 0.75]) if len(x) else (np.nan, np.nan)
        linhas.append(f"| {k} | {len(x)} | {br(x.mean())} | {br(x.std(ddof=1))} | {br(x.median())} | {br(q3 - q1)} | {s[v].isna().sum()} |")
    return "\n".join(linhas)

def comparar(df, v, g):
    grupos = {k: s[v].dropna().to_numpy() for k, s in df.groupby(g)}; grupos = {k: x for k, x in grupos.items() if len(x) >= 2}
    out = []
    if len(grupos) == 2:
        (ka, a), (kb, b) = grupos.items()
        t = stats.ttest_ind(a, b, equal_var=False); dif = a.mean() - b.mean()
        sp = math.sqrt(((len(a) - 1) * a.var(ddof=1) + (len(b) - 1) * b.var(ddof=1)) / (len(a) + len(b) - 2)); d = dif / sp
        se = math.sqrt(a.var(ddof=1) / len(a) + b.var(ddof=1) / len(b)); ic = stats.t.ppf(0.975, t.df) * se
        u = stats.mannwhitneyu(a, b, alternative="two-sided")
        out.append(f"Teste t de Welch ({ka} − {kb}): diferença de médias = {br(dif)} (IC 95 % {br(dif - ic)} a {br(dif + ic)}), t({br(t.df, 1)}) = {br(t.statistic)}, {fmt(t.pvalue)}; d de Cohen = {br(d)}.")
        out.append(f"Mann-Whitney (robustez): U = {br(u.statistic, 0)}, {fmt(u.pvalue)}.")
    elif len(grupos) > 2:
        xs = list(grupos.values()); f = stats.f_oneway(*xs)
        todos = np.concatenate(xs); ssb = sum(len(x) * (x.mean() - todos.mean()) ** 2 for x in xs); sst = ((todos - todos.mean()) ** 2).sum(); eta = ssb / sst
        k = stats.kruskal(*xs)
        out.append(f"ANOVA de um fator entre {len(xs)} grupos: F({len(xs) - 1}, {len(todos) - len(xs)}) = {br(f.statistic)}, {fmt(f.pvalue)}; η² = {br(eta)}. Diferenças par a par pedem post hoc (ex.: Tukey ou Games-Howell) com correção.")
        out.append(f"Kruskal-Wallis (robustez): H = {br(k.statistic)}, {fmt(k.pvalue)}.")
    else: out.append("menos de dois grupos com n ≥ 2: nada a comparar.")
    return "\n".join(out)

def correlacao(df, v, w):
    s = df[[v, w]].dropna(); r = stats.pearsonr(s[v], s[w]); rho = stats.spearmanr(s[v], s[w]); ic = r.confidence_interval()
    return f"Pearson r = {br(r.statistic)} (IC 95 % {br(ic.low)} a {br(ic.high)}), n = {len(s)}, {fmt(r.pvalue)}; Spearman ρ = {br(rho.statistic)}, {fmt(rho.pvalue)}."

def cruzada(df, c, g):
    t = pd.crosstab(df[c], df[g]); chi = stats.chi2_contingency(t); n = t.to_numpy().sum(); v = math.sqrt(chi[0] / (n * (min(t.shape) - 1)))
    md = "| " + c + " \\ " + g + " | " + " | ".join(map(str, t.columns)) + " |\n|---|" + "---|" * len(t.columns) + "\n" + "\n".join("| " + str(i) + " | " + " | ".join(str(x) for x in r) + " |" for i, r in zip(t.index, t.to_numpy()))
    return md + f"\n\nQui-quadrado: χ²({chi[2]}) = {br(chi[0])}, {fmt(chi[1])}; V de Cramér = {br(v)}; n = {n}." + ("  Aviso: há células com esperado < 5 — considere o teste exato de Fisher." if (chi[3] < 5).any() else "")

def figura(df, v, g, caminho):
    import matplotlib; matplotlib.use("Agg"); import matplotlib.pyplot as plt
    fig, ax = plt.subplots(figsize=(7, 4.2), dpi=150)
    if g:
        grupos = [(k, s[v].dropna()) for k, s in df.groupby(g)]
        ax.boxplot([x for _, x in grupos], tick_labels=[f"{k}\n(n={len(x)})" for k, x in grupos], showmeans=True)
        for i, (_, x) in enumerate(grupos, start=1): ax.scatter(np.random.default_rng(1).normal(i, 0.04, len(x)), x, s=8, alpha=0.35)
        ax.set_xlabel(g)
    else: ax.hist(df[v].dropna(), bins=20)
    ax.set_ylabel(v); ax.set_title(f"{v} por {g}" if g else v); fig.tight_layout(); fig.savefig(caminho); return caminho

def main():
    ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
    ap.add_argument("arquivo"); ap.add_argument("--variavel"); ap.add_argument("--grupo"); ap.add_argument("--correlacao"); ap.add_argument("--categoria")
    ap.add_argument("--figura"); ap.add_argument("--markdown"); ap.add_argument("--sep", default=None)
    a = ap.parse_args(); df = pd.read_csv(a.arquivo, sep=a.sep, engine="python")
    partes = [f"# Análise de `{a.arquivo}` ({len(df)} linhas)"]
    if a.categoria and a.grupo: partes.append(cruzada(df, a.categoria, a.grupo))
    elif a.variavel:
        df[a.variavel] = pd.to_numeric(df[a.variavel], errors="coerce"); partes.append(descritivas(df, a.variavel, a.grupo))
        if a.grupo: partes.append(comparar(df, a.variavel, a.grupo))
        if a.correlacao: df[a.correlacao] = pd.to_numeric(df[a.correlacao], errors="coerce"); partes.append(correlacao(df, a.variavel, a.correlacao))
        if a.figura: partes.append(f"figura: {figura(df, a.variavel, a.grupo, a.figura)}")
    else: sys.exit("informe --variavel (com --grupo e/ou --correlacao) ou --categoria com --grupo")
    texto = "\n\n".join(partes); print(texto)
    if a.markdown: open(a.markdown, "w", encoding="utf-8").write(texto + "\n"); print(f"\ngravado: {a.markdown}")

if __name__ == "__main__": main()

SHA-256: 1e5a19d26b80ea8d46678db69d840e91932646b40162669f343c5e83fe60bfa3