2011-08-03 13 views
5

Digamos que usted tiene una tabla con columnas, Fecha, GroupID, X e Y.¿Hay alguna forma de calcular la correlación en TSQL usando las cláusulas OVER en lugar de las CTE?

CREATE TABLE #sample 
    (
    [Date] DATETIME, 
    GroupID INT, 
    X  FLOAT, 
    Y  FLOAT 
) 

DECLARE @date DATETIME = getdate() 

INSERT INTO #sample VALUES(@date, 1, 1,3) 
INSERT INTO #sample VALUES(DATEADD(d, 1, @date), 1, 1,1) 
INSERT INTO #sample VALUES(DATEADD(d, 2, @date), 1, 4,2) 
INSERT INTO #sample VALUES(DATEADD(d, 3, @date), 1, 3,3) 
INSERT INTO #sample VALUES(DATEADD(d, 4, @date), 1, 6,4) 
INSERT INTO #sample VALUES(DATEADD(d, 5, @date), 1, 7,5) 
INSERT INTO #sample VALUES(DATEADD(d, 6, @date), 1, 1,6) 

y desea calcular la correlación de X e Y para cada grupo. Actualmente uso CTE, que ser un poco desordenado:

;WITH DataAvgStd 
    AS (SELECT GroupID, 
       AVG(X) AS XAvg, 
       AVG(Y) AS YAvg, 
       STDEV(X) AS XStdev, 
       STDEV(Y) AS YSTDev, 
       COUNT(*) AS SampleSize 
     FROM #sample 
     GROUP BY GroupID), 
    ExpectedVal 
    AS (SELECT s.GroupID, 
       SUM((X - XAvg) * (Y - YAvg)) AS ExpectedValue 
     FROM #sample s 
       JOIN DataAvgStd das 
        ON s.GroupID = das.GroupID 
     GROUP BY s.GroupID) 
SELECT das.GroupID, 
     ev.ExpectedValue/(das.SampleSize - 1)/(das.XStdev * das.YSTDev) 
     AS 
     Correlation 
FROM DataAvgStd das 
     JOIN ExpectedVal ev 
     ON das.GroupID = ev.GroupID 

DROP TABLE #sample 

Parece que debe haber una manera de utilizar una y otra partición para hacerlo de una sola vez y sin ninguna subconsulta. Idealmente TSQL tendría una función por lo que podría escribir:

SELECT GroupID, CORR(X, Y) OVER(PARTITION BY GroupID) 
FROM #sample 
GROUP BY GroupID 
+0

Me interesaría ver si alguien presenta una solución viable, sin embargo, siempre obtengo todos mis datos en la capa empresarial y realizo correlaciones allí. También realizamos lo que llamamos "correlaciones negativas", donde saltamos valores positivos y solo incluimos valores negativos, también sería interesante ver si esto era viable en SQL. –

+0

El código que publicó no se ejecutó por varios motivos. Lo he cambiado para que realmente se ejecute, es posible que desee verificar que todavía hace lo que esperaba ... –

+0

Si X o Y son anulables, debe reemplazar "FROM #sample" con "FROM # sample WHERE X IS NO NULO E Y NO ES NULO ", de lo contrario, puede terminar con la correlación incorrecta –

Respuesta

5

Al usar esta fórmula de correlación, no puede evitar todas las consultas anidadas incluso si usa over(). El hecho es que no puede usar ambos grupos una y otra vez en la misma consulta y tampoco puede tener funciones de agregación anidadas, p. sum(x - avg(x)). Entonces, en el mejor de los casos, de acuerdo con sus datos, deberá mantener al menos el with.

Su código se vería algo así

;WITH DataAvgStd 
    AS (SELECT GroupID, 
       STDEV(X) over(partition by GroupID) AS XStdev, 
       STDEV(Y) over(partition by GroupID) AS YSTDev, 
       COUNT(*) over(partition by GroupID) AS SampleSize, 
       (X - AVG(X) over(partition by GroupID)) * (Y - AVG(Y) over(partition by GroupID)) AS ExpectedValue 
     FROM #sample s)   
SELECT distinct GroupID, 
     SUM(ExpectedValue) over(partition by GroupID)/(SampleSize - 1)/(XStdev * YSTDev) AS Correlation 
FROM DataAvgStd 

Una alternativa es utilizar una fórmula equilevant de correlación como Wikipedia describe.

Esto se puede escribir como

SELECT GroupID, 
     Correlation=(COUNT(*) * SUM(X * Y) - SUM(X) * SUM(Y))/
        (SQRT(COUNT(*) * SUM(X * X) - SUM(X) * SUM(x)) 
        * SQRT(COUNT(*) * SUM(Y* Y) - SUM(Y) * SUM(Y))) 
FROM #sample s 
GROUP BY GroupID; 
+0

La segunda fórmula que mencionas, ¿dónde está en el artículo de Wikipedia? No lo veo del todo. – bpeikes

+0

@bpeikes Es la última fórmula en la sección 'coeficiente producto-momento de Pearson'. – Athafoud

1

SQL get Es un poco gracioso de agregados de anidación o funciones de ventanas, de ahí la necesidad de que el CTE o tablas derivadas.

Si debe implementarse en el servidor de bases de datos, y está buscando algo más legible que los CTE, su única opción es rodar su propio agregado con CLR.

Hay un buen tutorial aquí http://www.sqlservercentral.com/articles/SQLCLR/71942/ sobre la construcción de un agregado CLR similar.

+1

Sí.No sé por qué alguien querría hacer esto en la capa de "negocios". Debe extraer una tonelada de datos de la base de datos para calcular las correlaciones. Lo que realmente no entiendo es cómo SQL Server no lo tiene incorporado. Parece que SQL Server podría hacer un trabajo mucho mejor al optimizar los números. – bpeikes

1

Una solución de un solo paso para Tanto Calc:

Hay dos sabores del coeficiente de correlación de Pearson, uno para una muestra y una para toda una población. Estos son de un solo paso sencillo, y creo, fórmulas correctas para ambos:

-- Methods for calculating the two Pearson correlation coefficients 
SELECT 
     -- For Population 
     (avg(x * y) - avg(x) * avg(y))/
     (sqrt(avg(x * x) - avg(x) * avg(x)) * sqrt(avg(y * y) - avg(y) * avg(y))) 
     AS correlation_coefficient_population, 
     -- For Sample 
     (count(*) * sum(x * y) - sum(x) * sum(y))/
     (sqrt(count(*) * sum(x * x) - sum(x) * sum(x)) * sqrt(count(*) * sum(y * y) - sum(y) * sum(y))) 
     AS correlation_coefficient_sample 
    FROM (
     -- The following generates a table of sample data containing two columns with a luke-warm and tweakable correlation 
     -- y = x for 0 thru 99, y = x - 100 for 100 thru 199, etc. Execute it as a stand-alone to see for yourself 
     -- x and y are CAST as DECIMAL to avoid integer math, you should definitely do the same 
     -- Try TOP 100 or less for full correlation (y = x for all cases), TOP 200 for a PCC of 0.5, TOP 300 for one near 0.33, etc. 
     -- The superfluous "+ 0" is where you could apply various offsets to see that they have no effect on the results 
     SELECT TOP 200 
       CAST(ROW_NUMBER() OVER (ORDER BY [object_id]) - 1 + 0 AS DECIMAL) AS x, 
       CAST((ROW_NUMBER() OVER (ORDER BY [object_id]) - 1) % 100 AS DECIMAL) AS y 
      FROM sys.all_objects 
    ) AS a 

Como he señalado en los comentarios, usted puede probar el ejemplo con la parte superior 100 o menos para la correlación completa (y = x para todos casos); TOP 200 produce correlaciones muy cerca de 0.5; TOP 300, alrededor de 0,33; etc. Hay un lugar ("+ 0") para agregar un desplazamiento si lo desea; alerta de spoiler, no tiene efecto. Asegúrese de ENCENDER sus valores como DECIMAL - las matemáticas enteras pueden afectar significativamente estos cálculos.

Cuestiones relacionadas