sqlpostgresqlstatisticsaggregate

CORR no SQL: o coeficiente de correlacao de Pearson em uma consulta

Como CORR(y, x) calcula a correlacao de Pearson, o que significam sinal e magnitude, como pares NULL sao tratados e como obter a reta de tendencia com REGR_*.

3 min de leituraReferencesql · postgresql · statistics · aggregate · analytics

CORR(y, x) retorna o coeficiente de correlacao de Pearson entre duas colunas numericas como um unico agregado. O resultado fica sempre entre -1 e 1 e mostra com que forca os dois valores se movem juntos de forma linear.

O que CORR calcula e como ler

A funcao mede uma relacao linear: o quao bem a nuvem de pontos (x, y) se encaixa em uma reta. O valor e interpretado pelo sinal e pela magnitude:

  • +1 e uma relacao direta perfeita: mais x, mais y.
  • -1 e uma inversa perfeita: mais x, menos y.
  • 0 significa que nao ha vinculo linear (pode haver um nao linear).
  • Um |r| de cerca de 0.7 ou mais costuma ser chamado de forte, e 0.3-0.7 de moderado.

A ordem dos argumentos nao muda o coeficiente: CORR(a, b) = CORR(b, a). A ordem so passa a importar mais adiante, com as funcoes de regressao.

-- Correlation between order amount and the user's account age in days
SELECT CORR(
         o.amount,
         EXTRACT(EPOCH FROM (o.created_at - u.created_at)) / 86400
       ) AS amount_vs_age
FROM orders o
JOIN users u ON u.id = o.user_id
WHERE o.status = 'paid';

Correlacao nao e causalidade

A grande armadilha. Um r alto so diz que os numeros se movem juntos, nao que um cause o outro. Fontes classicas de correlacao espuria:

  • Um fator comum oculto (sazonalidade ou uma campanha puxam para cima pedidos e trafego ao mesmo tempo).
  • Efeitos de escala: clientes grandes fazem mais pedidos e tem um ticket medio maior.
  • Puro ruido em uma amostra pequena: r = 0.9 sobre 5 linhas quase nao significa nada.
-- Per-country correlation, but only where the sample is large enough to trust
SELECT u.country,
       COUNT(*)                  AS n,
       CORR(o.amount, u.id)      AS r
FROM orders o
JOIN users u ON u.id = o.user_id
GROUP BY u.country
HAVING COUNT(*) >= 30
ORDER BY r DESC;

Sempre mostre o COUNT(*) ao lado: um coeficiente sem o tamanho da amostra e facil de interpretar errado.

Tratamento de NULL e casos extremos

CORR so usa pares em que ambos os valores nao sao NULL. Se x ou y for NULL, a linha inteira e descartada silenciosamente do calculo. Esse e o comportamento padrao, nao um erro.

  • Se houver menos de dois pares validos, o resultado e NULL.
  • Se x ou y for constante (variancia zero), a divisao por zero produz NULL em vez de lancar uma excecao.
-- Salary vs manager presence: rows with NULL manager_id are dropped automatically
SELECT dept,
       COUNT(*)                       AS rows_total,
       COUNT(salary)                  AS rows_with_salary,
       CORR(salary, manager_id)       AS r
FROM employees
GROUP BY dept;

Pegadinha: COUNT(*) e o numero de pares que CORR realmente usou podem ser diferentes. Para saber o tamanho honesto da amostra, conte os pares de forma explicita, por exemplo com COUNT(*) FILTER (WHERE salary IS NOT NULL AND manager_id IS NOT NULL).

Combinando com REGR_* para uma reta de tendencia

CORR responde "quao forte e o vinculo" mas nao da a equacao da reta. A familia REGR_* da, e e barato calcula-la na mesma passada:

  • REGR_SLOPE(y, x) e a inclinacao da reta de tendencia.
  • REGR_INTERCEPT(y, x) e onde ela cruza o eixo y.
  • REGR_R2(y, x) e o coeficiente de determinacao, igual a r ao quadrado.
-- Trend line for amount as a function of account age, plus strength of fit
SELECT REGR_SLOPE(o.amount, age_days)     AS slope,
       REGR_INTERCEPT(o.amount, age_days) AS intercept,
       CORR(o.amount, age_days)           AS r,
       REGR_R2(o.amount, age_days)        AS r_squared
FROM (
  SELECT o.amount,
         EXTRACT(EPOCH FROM (o.created_at - u.created_at)) / 86400 AS age_days
  FROM orders o
  JOIN users u ON u.id = o.user_id
) o;

Previsao: predicted_amount = intercept + slope * age_days.

Diferencas entre bancos de dados

  • PostgreSQL: suporte completo a CORR, COVAR_POP, COVAR_SAMP e todo o conjunto REGR_*.
  • ClickHouse: tem corr(x, y) mas nao REGR_*; a inclinacao e derivada na mao a partir de covarPop e varPop.
  • MySQL: nao existe CORR. Voce monta a formula com AVG, STDDEV_POP e o produto medio, ou calcula na aplicacao.
-- Portable fallback that works even without a built-in CORR
SELECT (AVG(x * y) - AVG(x) * AVG(y))
       / (STDDEV_POP(x) * STDDEV_POP(y)) AS r
FROM (SELECT amount AS x, user_id AS y FROM orders) t;

Em resumo: CORR e uma forma barata e de passada unica para estimar a forca de um vinculo linear, e um REGR_* ao lado transforma essa estimativa em uma equacao de tendencia pronta para uso.

Pratique com exercícios reais

Resolva exercícios no treinador de SQL com correção instantânea e dicas.

Abrir o treinador