2011-08-03 25 views
5

比方說,你有列,日期,組ID,X和Y是否有一種方法可以使用OVER子句而不是CTE計算TSQL中的相關性?

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) 

一個表,要計算X和Y的每個組的相關性。目前我使用的CTE其變得有些混亂:

;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 

這好像應該是過度使用做這一舉,沒有任何的子查詢的方式和分區。理想的情況是TSQL將有一個功能,所以你可以寫:

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

我很想看看是否有人想出了一個可行的解決方案,但是,我總是我的所有數據拉來了業務層和執行的相關性存在。我們還進行我們所說的「負相關」 - 在這裏我們忽略正面的價值觀,只包括負值 - 這也將是有趣的,看看,如果這是在SQL可行的。 –

+0

您發佈的代碼因各種原因未能執行。所以實際上它運行,你可能要驗證它仍然沒有任何你期待...... –

+0

如果X或Y是空的,則需要更換「FROM #sample」與「FROM #sample其中X是我已經改變了它NOT NULL AND Y NOT NOT NULL「,否則最終會出現錯誤的相關性 –

回答

5

使用corellation的這個公式你無法避免所有的,即使你使用over()嵌套查詢。問題是你不能在同一個查詢中同時使用兩個組,也不能有嵌套的聚合函數,例如sum(x - avg(x))。因此,在最好的情況下,根據您的數據,您至少需要保留with

您的代碼看起來像這樣的事情

;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 

另一種方法是爲Wikipedia描述了使用equilevant公式相關。

這可以寫爲

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

您提到的第二個公式,它在維基百科文章中的位置?我不太明白。 – bpeikes

+0

@bpeikes這是「皮爾遜產品時刻係數」部分的最後一個公式。 – Athafoud

1

SQL GET有點好笑嵌套聚集或窗口功能,因此需要的CTE或派生表。

如果必須在數據庫服務器上實現,並且你正在尋找比熱膨脹係數更具可讀性你唯一的選擇是推出自己的聚合與CLR。

有一個很好的教程上建立一個類似的CLR聚集在這裏http://www.sqlservercentral.com/articles/SQLCLR/71942/

+1

是的。我不知道爲什麼有人會想在「商業」層面做到這一點。您必須從數據庫中提取大量數據以計算相關性。 我真的不明白的是,SQL服務器如何不將它作爲內置的。看起來像SQL服務器可以做更好的工作來優化數字。 – bpeikes

1

單通解這兩個Calcs(計算):

有Pearson相關係數的兩種口味,一個用於樣品,一個用於整個種羣。這些都是簡單的單傳,我相信,正確的公式兩種:

-- 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 

正如我在評論中指出,你可以嘗試用TOP 100或更少全相關(Y = X的所有實例例); TOP 200的相關性非常接近0.5; TOP 300,大約0.33;等等。有一個地方(「+ 0」)可以添加一個偏移量,如果你喜歡的話;擾流警報,它沒有效果。確保將CAST值作爲DECIMAL - 整數數學可以顯着影響這些計算。

相關問題