Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Aplica métodos estatísticos incluindo estatísticas descritivas, análise de tendências, detecção de outliers e testes de hipótese para dados do workspace. Use quando analisar distribuições de MRR/churn, testar se uma mudança de produto moveu métricas, detectar anomalias em instâncias do Licensing, calcular correlações, ou interpretar resultados estatísticos. Execução local via Python.
.claude/skills/evolution-foundation-data-statistical-analysis/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-06 | ✗→✓ | ▲ Improved | 269% | 0% |
| case-21 | ✗→✓ | ▲ Improved | 486% | 0% |
| case-07 | ✓→✓ | = Same ✓ | 252% | 0% |
| case-01 | ✓→✓ | = Same ✓ | 245% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 654% | 0% |
Estatísticas descritivas, análise de tendências, detecção de outliers, testes de hipótese, e orientação sobre quando ser cauteloso com afirmações estatísticas.
Escolher a medida correta de centro com base nos dados:
| Situação | Usar | Por quê | |---|---|---| | Distribuição simétrica, sem outliers | Média | Estimador mais eficiente | | Distribuição assimétrica | Mediana | Robusta a outliers | | Dados categóricos ou ordinais | Moda | Única opção para não-numéricos | | Altamente assimétrico com outliers (ex: MRR por cliente) | Mediana + média | Reportar ambas; a diferença mostra assimetria |
Sempre reportar média e mediana juntas para métricas de negócio. Se divergirem significativamente, os dados são assimétricos e a média isolada é enganosa.
Reportar percentis-chave para contar uma história mais rica do que a média isolada:
p1: Fundo 1% (piso / valor mínimo típico)
p5: Extremo baixo da faixa normal
p25: Primeiro quartil
p50: Mediana (cliente típico)
p75: Terceiro quartil
p90: Top 10% / clientes power user
p95: Extremo alto da faixa normal
p99: Top 1% / clientes extremosExemplo de narrativa: "O MRR mediano por cliente é R$ 420, mas os 10% melhores pagam mais de R$ 2.100/mês, puxando a média para R$ 780."
Caracterizar cada distribuição numérica analisada:
Médias móveis para suavizar ruído:
pythonimport pandas as pd # Média móvel de 7 dias (boa para dados diários com sazonalidade semanal) df['ma_7d'] = df['metrica'].rolling(window=7, min_periods=1).mean() # Média móvel de 28 dias (suaviza padrões semanais E mensais) df['ma_28d'] = df['metrica'].rolling(window=28, min_periods=1).mean()
Comparação período a período:
Taxas de crescimento:
Crescimento simples: (atual - anterior) / anterior
CAGR: (final / inicial) ^ (1 / anos) - 1
Crescimento log: ln(atual / anterior) — melhor para séries voláteisVerificar padrões periódicos:
Para analistas de negócio (não cientistas de dados), usar métodos diretos:
Sempre comunicar incerteza. Fornecer um intervalo, não uma estimativa pontual:
Quando escalar para um cientista de dados: Tendências não lineares, múltiplas sazonalidades, fatores externos (investimento em marketing, eventos como Evolution Summit), ou quando a precisão da previsão importa para alocação de recursos.
Método Z-score (para dados normalmente distribuídos):
pythonz_scores = (df['valor'] - df['valor'].mean()) / df['valor'].std() outliers = df[abs(z_scores) > 3] # Mais de 3 desvios padrão
Método IQR (robusto a distribuições não-normais):
pythonQ1 = df['valor'].quantile(0.25) Q3 = df['valor'].quantile(0.75) IQR = Q3 - Q1 limite_inferior = Q1 - 1.5 * IQR limite_superior = Q3 + 1.5 * IQR outliers = df[(df['valor'] < limite_inferior) | (df['valor'] > limite_superior)]
Método percentil (mais simples):
pythonoutliers = df[(df['valor'] < df['valor'].quantile(0.01)) | (df['valor'] > df['valor'].quantile(0.99))]
NÃO remover outliers automaticamente. Em vez disso:
Documentar o que foi feito: "Excluímos 47 registros (0,3%) com valores de transação >R$50K, que representam pedidos bulk enterprise analisados separadamente."
Para detectar valores incomuns em uma série temporal:
Exemplo prático para o Licensing:
python# Detectar queda anormal de instâncias ativas df['media_movel'] = df['instancias'].rolling(window=7).mean() df['desvio'] = df['instancias'].std() df['z_score'] = (df['instancias'] - df['media_movel']) / df['desvio'] anomalias = df[abs(df['z_score']) > 2.5] print(f"Anomalias detectadas: {len(anomalias)} dias")
Usar testes de hipótese quando precisar determinar se uma diferença observada é provavelmente real ou poderia ser devida ao acaso. Cenários comuns:
| Cenário | Teste | Quando Usar | |---|---|---| | Comparar médias de dois grupos | t-test (independente) | Dados normais, dois grupos | | Comparar proporções de dois grupos | z-test para proporções | Taxas de conversão, resultados binários | | Comparar medições pareadas | t-test pareado | Antes/depois nas mesmas entidades | | Comparar médias de 3+ grupos | ANOVA | Múltiplos segmentos ou variantes | | Dados não-normais, dois grupos | Teste U de Mann-Whitney | Métricas assimétricas, dados ordinais | | Associação entre categorias | Teste qui-quadrado | Duas variáveis categóricas |
Código Python para testes comuns:
pythonfrom scipy import stats # t-test independente (ex: MRR médio entre dois planos) grupo_a = df[df['plano'] == 'Pro']['mrr'] grupo_b = df[df['plano'] == 'Enterprise']['mrr'] t_stat, p_value = stats.ttest_ind(grupo_a, grupo_b) print(f"t={t_stat:.3f}, p={p_value:.4f}") print("Diferença estatisticamente significativa" if p_value < 0.05 else "Sem evidência de diferença") # z-test para proporções (ex: taxa de conversão entre variantes) from statsmodels.stats.proportion import proportions_ztest contagens = [conversoes_a, conversoes_b] nobs = [total_a, total_b] z_stat, p_value = proportions_ztest(contagens, nobs) # Teste qui-quadrado (ex: distribuição de planos por país) contingencia = pd.crosstab(df['pais'], df['plano']) chi2, p, dof, expected = stats.chi2_contingency(contingencia)
Significância estatística significa que a diferença provavelmente não é devida ao acaso.
Significância prática significa que a diferença é grande o suficiente para importar para decisões de negócio.
Uma diferença pode ser estatisticamente significativa mas praticamente sem sentido (comum com amostras grandes). Sempre reportar:
python# Calcular tamanho de amostra necessário from statsmodels.stats.power import TTestIndPower analise = TTestIndPower() n_necessario = analise.solve_power( effect_size=0.2, # Tamanho do efeito (Cohen's d) alpha=0.05, # Nível de significância power=0.8 # Poder desejado (80%) ) print(f"Tamanho de amostra necessário por grupo: {n_necessario:.0f}")
Ao encontrar uma correlação, explicitamente considerar:
O que pode dizer: "Clientes que usam o Evo CRM há mais de 6 meses têm 30% maior retenção" O que não pode dizer sem mais evidências: "O Evo CRM causa 30% maior retenção"
Ao testar muitas hipóteses, algumas serão "significativas" por acaso:
python# Correção de Bonferroni n_testes = 10 alfa_corrigido = 0.05 / n_testes print(f"Alfa corrigido por Bonferroni: {alfa_corrigido:.4f}")
Uma tendência em dados agregados pode se inverter quando segmentada:
Só é possível analisar entidades que "sobreviveram" para estar no dataset:
Tendências agregadas podem não se aplicar a indivíduos:
Cuidado com falsa precisão:
pythonimport pandas as pd import numpy as np from scipy import stats def analisar_mrr(df_stripe): """ Análise descritiva completa de MRR por cliente. Entrada: DataFrame com colunas [cliente_id, mrr_usd, plano, pais] """ mrr = df_stripe['mrr_usd'] print("=== ANÁLISE DE MRR ===") print(f"\nN clientes: {len(mrr):,}") print(f"\n--- TENDÊNCIA CENTRAL ---") print(f"Média: ${mrr.mean():,.2f}") print(f"Mediana: ${mrr.median():,.2f}") print(f"Moda: ${mrr.mode().iloc[0]:,.2f}") print(f"\n--- DISPERSÃO ---") print(f"Desvio padrão: ${mrr.std():,.2f}") print(f"IQR: ${mrr.quantile(0.75) - mrr.quantile(0.25):,.2f}") print(f"CV: {mrr.std()/mrr.mean()*100:.1f}%") print(f"\n--- PERCENTIS ---") for p in [1, 5, 25, 50, 75, 90, 95, 99]: print(f" p{p:>2}: ${mrr.quantile(p/100):,.2f}") print(f"\n--- FORMA DA DISTRIBUIÇÃO ---") skewness = stats.skew(mrr) kurtosis = stats.kurtosis(mrr) print(f"Assimetria: {skewness:.2f} ({'assimétrico à direita' if skewness > 0 else 'assimétrico à esquerda'})") print(f"Curtose: {kurtosis:.2f}") # Detecção de outliers via IQR Q1, Q3 = mrr.quantile(0.25), mrr.quantile(0.75) IQR = Q3 - Q1 outliers = mrr[(mrr < Q1 - 1.5*IQR) | (mrr > Q3 + 1.5*IQR)] print(f"\n--- OUTLIERS ---") print(f"Detectados: {len(outliers)} ({len(outliers)/len(mrr)*100:.1f}%)") return { 'n': len(mrr), 'media': mrr.mean(), 'mediana': mrr.median(), 'std': mrr.std(), 'iqr': IQR, 'outliers': len(outliers) }
pythondef analisar_ab_test(grupo_controle, grupo_variante, metrica='conversao'): """ Análise completa de A/B test. Entrada: dois DataFrames com coluna da métrica """ from scipy import stats a = grupo_controle[metrica] b = grupo_variante[metrica] print(f"=== A/B TEST: {metrica.upper()} ===") print(f"\nGrupo A (controle): n={len(a):,}, média={a.mean():.4f}") print(f"Grupo B (variante): n={len(b):,}, média={b.mean():.4f}") # Diferença e tamanho do efeito diff = b.mean() - a.mean() lift = diff / a.mean() * 100 cohens_d = diff / np.sqrt((a.std()**2 + b.std()**2) / 2) print(f"\n--- IMPACTO ---") print(f"Diferença absoluta: {diff:+.4f}") print(f"Lift relativo: {lift:+.1f}%") print(f"Tamanho do efeito (Cohen's d): {cohens_d:.3f}") # Teste estatístico t_stat, p_value = stats.ttest_ind(a, b) ci = stats.t.interval(0.95, len(a)+len(b)-2, loc=diff, scale=stats.sem(pd.concat([a, b]))) print(f"\n--- SIGNIFICÂNCIA ESTATÍSTICA ---") print(f"t={t_stat:.3f}, p={p_value:.4f}") print(f"IC 95%: [{ci[0]:+.4f}, {ci[1]:+.4f}]") print(f"Significativo? {'SIM' if p_value < 0.05 else 'NÃO'} (alfa=0.05)") if p_value < 0.05: print(f"\n✓ A variante B {'melhora' if diff > 0 else 'piora'} {metrica} " f"em {abs(lift):.1f}% (estatisticamente significativo)") else: print(f"\n✗ Sem evidência de diferença estatisticamente significativa")
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-07 | pass→pass | 12,561 | 11,427 | -9% | 1 | 1 | 0% | 1,917 | 6,750 | +252% | 0 | 0 | — |
case-01 | pass→pass | 11,637 | 11,883 | +2% | 1 | 1 | 0% | 1,955 | 6,744 | +245% | 0 | 0 | — |
case-02 | pass→pass | 4,888 | 6,445 | +32% | 1 | 1 | 0% | 791 | 5,962 | +654% | 0 | 0 | — |
case-12 | pass→pass | 3,611 | 6,770 | +87% | 1 | 1 | 0% | 619 | 6,098 | +885% | 0 | 0 | — |
case-03 | pass→pass | 3,415 | 6,131 | +80% | 1 | 1 | 0% | 574 | 6,048 | +954% | 0 | 0 | — |
case-04 | pass→pass | 4,872 | 6,136 | +26% | 1 | 1 | 0% | 812 | 5,894 | +626% | 0 | 0 | — |
case-05 | pass→pass | 7,541 | 7,800 | +3% | 1 | 1 | 0% | 1,325 | 6,244 | +371% | 0 | 0 | — |
case-06 | fail→pass | 10,511 | 8,100 | -23% | 1 | 1 | 0% | 1,685 | 6,221 | +269% | 0 | 0 | — |
case-08 | fail→fail | 11,078 | 11,697 | +6% | 1 | 1 | 0% | 1,935 | 7,063 | +265% | 0 | 0 | — |
case-09 | pass→pass | 12,752 | 16,002 | +25% | 1 | 1 | 0% | 2,050 | 7,495 | +266% | 0 | 0 | — |
case-10 | pass→pass | 7,218 | 8,096 | +12% | 1 | 1 | 0% | 1,348 | 6,380 | +373% | 0 | 0 | — |
case-11 | pass→pass | 5,297 | 5,299 | +0% | 1 | 1 | 0% | 962 | 5,864 | +510% | 0 | 0 | — |
case-13 | pass→pass | 3,485 | 6,204 | +78% | 1 | 1 | 0% | 597 | 6,045 | +913% | 0 | 0 | — |
case-14 | pass→pass | 9,842 | 11,296 | +15% | 1 | 1 | 0% | 1,739 | 6,837 | +293% | 0 | 0 | — |
case-15 | pass→pass | 10,381 | 13,430 | +29% | 1 | 1 | 0% | 2,174 | 7,515 | +246% | 0 | 0 | — |
case-16 | pass→pass | 10,128 | 9,329 | -8% | 1 | 1 | 0% | 1,775 | 6,583 | +271% | 0 | 0 | — |
case-17 | pass→pass | 5,885 | 9,440 | +60% | 1 | 1 | 0% | 939 | 6,392 | +581% | 0 | 0 | — |
case-18 | pass→pass | 7,397 | 6,872 | -7% | 1 | 1 | 0% | 1,035 | 5,943 | +474% | 0 | 0 | — |
case-19 | pass→pass | 8,832 | 9,937 | +13% | 1 | 1 | 0% | 1,309 | 6,453 | +393% | 0 | 0 | — |
case-20 | pass→pass | 9,848 | 11,454 | +16% | 1 | 1 | 0% | 1,604 | 6,712 | +318% | 0 | 0 | — |
case-21 | fail→pass | 6,429 | 11,979 | +86% | 1 | 1 | 0% | 1,182 | 6,923 | +486% | 0 | 0 | — |
case-22 | pass→pass | 14,168 | 13,058 | -8% | 1 | 1 | 0% | 2,330 | 6,971 | +199% | 0 | 0 | — |
case-23 | pass→pass | 3,559 | 3,656 | +3% | 1 | 1 | 0% | 627 | 5,449 | +769% | 0 | 0 | — |
case-24 | pass→pass | 8,114 | 8,887 | +10% | 1 | 1 | 0% | 1,646 | 6,588 | +300% | 0 | 0 | — |
case-25 | pass→pass | 10,907 | 8,993 | -18% | 1 | 1 | 0% | 2,502 | 6,807 | +172% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 25 cases were attempted. The headline lift of +8 percentage points is the difference between those two pass rates over the 25 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.