web-dev-qa-db-ja.com

SQL Serverに線形回帰関数はありますか?

SQL Server 2005/2008には Oracleの線形回帰関数 と同様の線形回帰関数がありますか?

27
rao

私の知る限りではありません。ただし、1つを書くのは非常に簡単です。以下は、y =アルファ+ベータ* x +イプシロンの定数アルファと勾配ベータを示します。

-- test data (GroupIDs 1, 2 normal regressions, 3, 4 = no variance)
WITH some_table(GroupID, x, y) AS
(       SELECT 1,  1,  1    UNION SELECT 1,  2,  2    UNION SELECT 1,  3,  1.3  
  UNION SELECT 1,  4,  3.75 UNION SELECT 1,  5,  2.25 UNION SELECT 2, 95, 85    
  UNION SELECT 2, 85, 95    UNION SELECT 2, 80, 70    UNION SELECT 2, 70, 65    
  UNION SELECT 2, 60, 70    UNION SELECT 3,  1,  2    UNION SELECT 3,  1, 3
  UNION SELECT 4,  1,  2    UNION SELECT 4,  2,  2),
 -- linear regression query
/*WITH*/ mean_estimates AS
(   SELECT GroupID
          ,AVG(x * 1.)                                             AS xmean
          ,AVG(y * 1.)                                             AS ymean
    FROM some_table
    GROUP BY GroupID
),
stdev_estimates AS
(   SELECT pd.GroupID
          -- T-SQL STDEV() implementation is not numerically stable
          ,CASE      SUM(SQUARE(x - xmean)) WHEN 0 THEN 1 
           ELSE SQRT(SUM(SQUARE(x - xmean)) / (COUNT(*) - 1)) END AS xstdev
          ,     SQRT(SUM(SQUARE(y - ymean)) / (COUNT(*) - 1))     AS ystdev
    FROM some_table pd
    INNER JOIN mean_estimates  pm ON pm.GroupID = pd.GroupID
    GROUP BY pd.GroupID, pm.xmean, pm.ymean
),
standardized_data AS                   -- increases numerical stability
(   SELECT pd.GroupID
          ,(x - xmean) / xstdev                                    AS xstd
          ,CASE ystdev WHEN 0 THEN 0 ELSE (y - ymean) / ystdev END AS ystd
    FROM some_table pd
    INNER JOIN stdev_estimates ps ON ps.GroupID = pd.GroupID
    INNER JOIN mean_estimates  pm ON pm.GroupID = pd.GroupID
),
standardized_beta_estimates AS
(   SELECT GroupID
          ,CASE WHEN SUM(xstd * xstd) = 0 THEN 0
                ELSE SUM(xstd * ystd) / (COUNT(*) - 1) END         AS betastd
    FROM standardized_data pd
    GROUP BY GroupID
)
SELECT pb.GroupID
      ,ymean - xmean * betastd * ystdev / xstdev                   AS Alpha
      ,betastd * ystdev / xstdev                                   AS Beta
FROM standardized_beta_estimates pb
INNER JOIN stdev_estimates ps ON ps.GroupID = pb.GroupID
INNER JOIN mean_estimates  pm ON pm.GroupID = pb.GroupID

ここでは、GroupIDを使用して、ソースデータテーブルの値でグループ化する方法を示します。テーブル内のすべてのデータ(特定のサブグループではない)全体の統計が必要な場合は、それと結合を削除できます。わかりやすくするために、WITHステートメントを使用しました。代わりに、代わりにサブクエリを使用できます。データに対して精度が十分に高くない場合、数値の安定性がすぐに低下する可能性があるため、テーブルで使用されるデータ型の精度に注意してください。

EDIT:(コメントのR2のような追加の統計に関するピーターの質問に答えて)

同じ手法を使用して、追加の統計を簡単に計算できます。以下は、R2、相関、およびサンプルの共分散を含むバージョンです。

-- test data (GroupIDs 1, 2 normal regressions, 3, 4 = no variance)
WITH some_table(GroupID, x, y) AS
(       SELECT 1,  1,  1    UNION SELECT 1,  2,  2    UNION SELECT 1,  3,  1.3  
  UNION SELECT 1,  4,  3.75 UNION SELECT 1,  5,  2.25 UNION SELECT 2, 95, 85    
  UNION SELECT 2, 85, 95    UNION SELECT 2, 80, 70    UNION SELECT 2, 70, 65    
  UNION SELECT 2, 60, 70    UNION SELECT 3,  1,  2    UNION SELECT 3,  1, 3
  UNION SELECT 4,  1,  2    UNION SELECT 4,  2,  2),
 -- linear regression query
/*WITH*/ mean_estimates AS
(   SELECT GroupID
          ,AVG(x * 1.)                                             AS xmean
          ,AVG(y * 1.)                                             AS ymean
    FROM some_table pd
    GROUP BY GroupID
),
stdev_estimates AS
(   SELECT pd.GroupID
          -- T-SQL STDEV() implementation is not numerically stable
          ,CASE      SUM(SQUARE(x - xmean)) WHEN 0 THEN 1 
           ELSE SQRT(SUM(SQUARE(x - xmean)) / (COUNT(*) - 1)) END AS xstdev
          ,     SQRT(SUM(SQUARE(y - ymean)) / (COUNT(*) - 1))     AS ystdev
    FROM some_table pd
    INNER JOIN mean_estimates  pm ON pm.GroupID = pd.GroupID
    GROUP BY pd.GroupID, pm.xmean, pm.ymean
),
standardized_data AS                   -- increases numerical stability
(   SELECT pd.GroupID
          ,(x - xmean) / xstdev                                    AS xstd
          ,CASE ystdev WHEN 0 THEN 0 ELSE (y - ymean) / ystdev END AS ystd
    FROM some_table pd
    INNER JOIN stdev_estimates ps ON ps.GroupID = pd.GroupID
    INNER JOIN mean_estimates  pm ON pm.GroupID = pd.GroupID
),
standardized_beta_estimates AS
(   SELECT GroupID
          ,CASE WHEN SUM(xstd * xstd) = 0 THEN 0
                ELSE SUM(xstd * ystd) / (COUNT(*) - 1) END         AS betastd
    FROM standardized_data
    GROUP BY GroupID
)
SELECT pb.GroupID
      ,ymean - xmean * betastd * ystdev / xstdev                   AS Alpha
      ,betastd * ystdev / xstdev                                   AS Beta
      ,CASE ystdev WHEN 0 THEN 1 ELSE betastd * betastd END        AS R2
      ,betastd                                                     AS Correl
      ,betastd * xstdev * ystdev                                   AS Covar
FROM standardized_beta_estimates pb
INNER JOIN stdev_estimates ps ON ps.GroupID = pb.GroupID
INNER JOIN mean_estimates  pm ON pm.GroupID = pb.GroupID

EDIT 2は、(中央揃えのみでなく)データを標準化し、STDEVを置き換えることで数値の安定性を向上させます 数値の安定性の問題 。私にとって、現在の実装は、安定性と複雑さの間の最良のトレードオフのようです。標準偏差を数値的に安定したオンラインアルゴリズムに置き換えることで安定性を向上させることはできますが、これにより実装が大幅に複雑になります(そして速度が低下します)。同様に、たとえば、 SUMおよびAVGに対するKahan(-Babuška-Neumaier)の補正は、限られたテストでは適度に優れているように見えますが、クエリがはるかに複雑になります。 T-SQLがSUMAVGを実装する方法がわからない限り(たとえば、すでにペアワイズ合計を使用している可能性があります)、そのような変更によって常に精度が向上することを保証できません。

40
stephan

これは、次の式を使用する T-SQLの線形回帰に関するブログ投稿 に基づく代替方法です。

enter image description here

ただし、ブログのSQL提案ではカーソルを使用しています。これが、私が使用した フォーラムの回答 の洗練されたバージョンです。

table
-----
X (numeric)
Y (numeric)

/**
 * m = (nSxy - SxSy) / (nSxx - SxSx)
 * b = Ay - (Ax * m)
 * N.B. S = Sum, A = Mean
 */
DECLARE @n INT
SELECT @n = COUNT(*) FROM table
SELECT (@n * SUM(X*Y) - SUM(X) * SUM(Y)) / (@n * SUM(X*X) - SUM(X) * SUM(X)) AS M,
       AVG(Y) - AVG(X) *
       (@n * SUM(X*Y) - SUM(X) * SUM(Y)) / (@n * SUM(X*X) - SUM(X) * SUM(X)) AS B
FROM table
15
icc97

実際に、グラム・シュミットの直交化を使用してSQLルーチンを作成しました。それ、および他の機械学習と予測ルーチンは sqldatamine.blogspot.com で入手できます。

Brad Larsonの提案で、ユーザーをブログに直接誘導するのではなく、ここにコードを追加しました。これは、Excelのlinest関数と同じ結果になります。私の主な情報源はHastie、Tibshirni、FriedmanによるElements of Statistical Learning(2008)です。

--Create a table of data
create table #rawdata (id int,area float, rooms float, odd float,  price float)

insert into #rawdata select 1, 2201,3,1,400
insert into #rawdata select 2, 1600,3,0,330
insert into #rawdata select 3, 2400,3,1,369
insert into #rawdata select 4, 1416,2,1,232
insert into #rawdata select 5, 3000,4,0,540

--Insert the data into x & y vectors
select id xid, 0 xn,1 xv into #x from #rawdata
union all
select id, 1,rooms  from #rawdata
union all
select id, 2,area  from #rawdata
union all
select id, 3,odd  from #rawdata

select id yid, 0 yn, price yv  into #y from #rawdata

--create a residuals table and insert the intercept (1)
create table #z (zid int, zn int, zv float)
insert into #z select id , 0 zn,1 zv from #rawdata

--create a table for the orthoganal (#c) & regression(#b) parameters
create table #c(cxn int, czn int, cv float) 
create table #b(bn int, bv float) 


--@p is the number of independent variables including the intercept (@p = 0)
declare @p int
set @p = 1


--Loop through each independent variable and estimate the orthagonal parameter (#c)
-- then estimate the residuals and insert into the residuals table (#z)
while @p <= (select max(xn) from #x)
begin   
        insert into #c
    select  xn cxn,  zn czn, sum(xv*zv)/sum(zv*zv) cv 
        from #x join  #z on  xid = zid where zn = @p-1 and xn>zn group by xn, zn

    insert into #z
    select zid, xn,xv- sum(cv*zv) 
        from #x join #z on xid = zid   join  #c  on  czn = zn and cxn = xn  where xn = @p and zn<xn  group by zid, xn,xv

    set @p = @p +1
end

--Loop through each independent variable and estimate the regression parameter by regressing the orthoganal
-- resiuduals on the dependent variable y
while @p>=0 
begin

    insert into #b
    select zn, sum(yv*zv)/ sum(zv*zv) 
        from #z  join 
            (select yid, yv-isnull(sum(bv*xv),0) yv from #x join #y on xid = yid left join #b on  xn=bn group by yid, yv) y
        on zid = yid where zn = @p  group by zn

    set @p = @p-1
end

--The regression parameters
select * from #b

--Actual vs. fit with error
select yid, yv, fit, yv-fit err from #y join 
    (select xid, sum(xv*bv) fit from #x join #b on xn = bn  group by xid) f
     on yid = xid

--R Squared
select 1-sum(power(err,2))/sum(power(yv,2)) from 
(select yid, yv, fit, yv-fit err from #y join 
    (select xid, sum(xv*bv) fit from #x join #b on xn = bn  group by xid) f
     on yid = xid) d
3
colin campbell

SQL Serverには線形回帰関数はありません。しかし、データポイントのペアx、y間の単純な線形回帰(Y '= bX + A)を計算するには、相関係数、決定係数(R ^ 2)、標準誤差推定(標準偏差)の計算を含みます。以下をせよ:

テーブルの場合regression_data数値列xおよびy付き:

declare @total_points int 
declare @intercept DECIMAL(38, 10)
declare @slope DECIMAL(38, 10)
declare @r_squared DECIMAL(38, 10)
declare @standard_estimate_error DECIMAL(38, 10)
declare @correlation_coefficient DECIMAL(38, 10)
declare @average_x  DECIMAL(38, 10)
declare @average_y  DECIMAL(38, 10)
declare @sumX DECIMAL(38, 10)
declare @sumY DECIMAL(38, 10)
declare @sumXX DECIMAL(38, 10)
declare @sumYY DECIMAL(38, 10)
declare @sumXY DECIMAL(38, 10)
declare @Sxx DECIMAL(38, 10)
declare @Syy DECIMAL(38, 10)
declare @Sxy DECIMAL(38, 10)

Select 
@total_points = count(*),
@average_x = avg(x),
@average_y = avg(y),
@sumX = sum(x),
@sumY = sum(y),
@sumXX = sum(x*x),
@sumYY = sum(y*y),
@sumXY = sum(x*y)
from regression_data

set @Sxx = @sumXX - (@sumX * @sumX) / @total_points
set @Syy = @sumYY - (@sumY * @sumY) / @total_points
set @Sxy = @sumXY - (@sumX * @sumY) / @total_points

set @correlation_coefficient = @Sxy / SQRT(@Sxx * @Syy) 
set @slope = (@total_points * @sumXY - @sumX * @sumY) / (@total_points * @sumXX - power(@sumX,2))
set @intercept = @average_y - (@total_points * @sumXY - @sumX * @sumY) / (@total_points * @sumXX - power(@sumX,2)) * @average_x
set @r_squared = (@intercept * @sumY + @slope * @sumXY - power(@sumY,2) / @total_points) / (@sumYY - power(@sumY,2) / @total_points)

-- calculate standard_estimate_error (standard deviation)
Select
@standard_estimate_error = sqrt(sum(power(y - (@slope * x + @intercept),2)) / @total_points)
From regression_data
2
SyntaxGoonoo

@ icc97の回答に追加するために、勾配と切片に加重バージョンを含めました。値がすべて一定の場合、勾配はNULLになります(適切な設定でSET ARITHABORT OFF; SET ANSI_WARNINGS OFF;)であり、coalesce()を介して0に置き換える必要があります。

SQLで書かれたソリューションは次のとおりです。

with d as (select segment,w,x,y from somedatasource)
select segment,

avg(y) - avg(x) *
((count(*) * sum(x*y)) - (sum(x)*sum(y)))/
((count(*) * sum(x*x)) - (Sum(x)*Sum(x)))   as intercept,

((count(*) * sum(x*y)) - (sum(x)*sum(y)))/
((count(*) * sum(x*x)) - (sum(x)*sum(x))) AS slope,

avg(y) - ((avg(x*y) - avg(x)*avg(y))/var_samp(X)) * avg(x) as interceptUnstable,
(avg(x*y) - avg(x)*avg(y))/var_samp(X) as slopeUnstable,
(Avg(x * y) - Avg(x) * Avg(y)) / (stddev_pop(x) * stddev_pop(y)) as correlationUnstable,

(sum(y*w)/sum(w)) - (sum(w*x)/sum(w)) *
((sum(w)*sum(x*y*w)) - (sum(x*w)*sum(y*w)))/
  ((sum(w)*sum(x*x*w)) - (sum(x*w)*sum(x*w)))   as wIntercept,

((sum(w)*sum(x*y*w)) - (sum(x*w)*sum(y*w)))/
  ((sum(w)*sum(x*x*w)) - (sum(x*w)*sum(x*w))) as wSlope,

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

count(*) as n

from d where x is not null and y is not null group by segment

ここで、wは重量です。結果を確認するために、Rに対してこれをダブルチェックしました。データをsomedatasourceから浮動小数点にキャストする必要があるかもしれません。それらに対して警告するために不安定なバージョンを含めました。 (別の答えでステファンに感謝します。)

相関はデータポイントxとyの相関であり、予測ではないことに注意してください。

1
Chris

Excelの関数予測で使用される線形回帰関数を翻訳し、a、b、および予測を返すSQL関数を作成しました。詳細な説明は、ExcelのFORECAST関数のヘルプで確認できます。はじめに、テーブルデータタイプXYFloatTypeを作成する必要があります。

 CREATE TYPE [dbo].[XYFloatType] 
AS TABLE(
[X] FLOAT,
[Y] FLOAT)

次に、follow関数を記述します。

    /*
-- =============================================
-- Author:      Me      :)
-- Create date: Today   :)
-- Description: (Copied Excel help): 
--Calculates, or predicts, a future value by using existing values. 
The predicted value is a y-value for a given x-value. 
The known values are existing x-values and y-values, and the new value is predicted by using linear regression. 
You can use this function to predict future sales, inventory requirements, or consumer trends.
-- =============================================
*/

CREATE FUNCTION dbo.FN_GetLinearRegressionForcast

(@PtXYData as XYFloatType READONLY ,@PnFuturePointint)
RETURNS @ABDData TABLE( a FLOAT, b FLOAT, Forecast FLOAT)
AS

BEGIN 
    DECLARE  @LnAvX Float
            ,@LnAvY Float
            ,@LnB Float
            ,@LnA Float
            ,@LnForeCast Float
    Select   @LnAvX = AVG([X])
            ,@LnAvY = AVG([Y])
    FROM @PtXYData;
    SELECT @LnB =  SUM ( ([X]-@LnAvX)*([Y]-@LnAvY) )  /  SUM (POWER([X]-@LnAvX,2))
    FROM @PtXYData;
    SET @LnA = @LnAvY - @LnB * @LnAvX;
    SET @LnForeCast = @LnA + @LnB * @PnFuturePoint;
    INSERT INTO @ABDData ([A],[B],[Forecast]) VALUES (@LnA,@LnB,@LnForeCast)
    RETURN 
END

/*
your tests: 

 (I used the same values that are in the Excel help)
DECLARE @t XYFloatType 
INSERT @t VALUES(20,6),(28,7),(31,9),(38,15),(40,21)        -- x and y values
SELECT *, A+B*30 [Prueba]FROM dbo.FN_GetLinearRegressionForcast@t,30);
*/

これは、次のタイプのテーブルタイプを取る関数です:table(Y float、X double)はXYDoubleTypeと呼ばれ、線形関数がAX + Bの形式であると想定しています。念のため、AとBをTable列として返します。あなたはそれを結合または何かにしたいです

CREATE FUNCTION FN_GetABForData(
 @XYData as XYDoubleType READONLY
 ) RETURNS  @ABData TABLE(
            A  FLOAT,
            B FLOAT, 
            Rsquare FLOAT )
 AS
 BEGIN
    DECLARE @sx FLOAT, @sy FLOAT
    DECLARE @sxx FLOAT,@syy FLOAT, @sxy FLOAT,@sxsy FLOAT, @sxsx FLOAT, @sysy FLOAT
    DECLARE @n FLOAT, @A FLOAT, @B FLOAT, @Rsq FLOAT

    SELECT @sx =SUM(D.X) ,@sy =SUM(D.Y), @sxx=SUM(D.X*D.X),@syy=SUM(D.Y*D.Y),
        @sxy =SUM(D.X*D.Y),@n =COUNT(*)
    From @XYData D
    SET @sxsx =@sx*@sx
    SET @sxsy =@sx*@sy
    SET @sysy = @sy*@sy

    SET @A = (@n*@sxy -@sxsy)/(@n*@sxx -@sxsx)
    SET @B = @sy/@n  - @A*@sx/@n
    SET @Rsq = POWER((@n*@sxy -@sxsy),2)/((@n*@sxx-@sxsx)*(@n*@syy -@sysy))

    INSERT INTO @ABData (A,B,Rsquare) VALUES(@A,@B,@Rsq)

    RETURN 
 END
0
Painless Coding

次の回答が、いくつかの解決策の出所を理解する助けになることを願っています。簡単な例で説明しますが、インデックス表記または行列の使用方法を知っている限り、多くの変数の一般化は理論的に簡単です。 3つの変数以外のソリューションを実装するには、グラムシュミット(上記のコリンキャンベルの回答を参照)または別の行列反転アルゴリズムを使用します。

必要なすべての関数は分散、共分散、平均、合計などなので、SQLの集計関数なので、簡単にソリューションを実装できます。私は、ロジスティックモデルのスコアの線形キャリブレーションを行うためにHiveでこれを行いました-多くの利点の中で、1つは、スクリプト言語から出入りすることなく、Hive内で完全に機能できることです。

データポイントがiによってインデックス化されるデータ(x_1、x_2、y)のモデルは、

y(x_1、x_2)= m_1 * x_1 + m_2 * x_2 + c

モデルは「線形」であるように見えますが、そうである必要はありません。たとえば、x_2には、x_1の任意の非線形関数を使用できます。 x_2 = Sinh(3 *(x_1)^ 2 + 42)。 x_2が「ちょうど」x_2でモデルが線形である場合でも、回帰問題はそうではありません。問題がパラメーターm_1、m_2、cを見つけてL2エラーを最小化することであると判断した場合のみ、線形回帰問題が発生します。

L2エラーはsum_i((y [i]-f(x_1 [i]、x_2 [i]))^ 2)です。これを最小限に抑える3つのパラメーター(偏微分w.r.tを設定します。各パラメーター= 0)は、3つの未知数に対して3つの線形方程式を生成します。これらの方程式は、パラメータが線形であり(これが線形回帰の原因です)、分析的に解くことができます。単純なモデル(1つの変数、線形モデル、したがって2つのパラメーター)に対してこれを行うと、簡単で有益です。エラーベクトル空間での非ユークリッドメトリックノルムへの一般化は簡単です。対角線の特殊な場合は、「重み」を使用することになります。

2つの変数でモデルに戻ります。

y = m_1 * x_1 + m_2 * x_2 + c

期待値を取る=>

= m_1 * + m_2 * + c(0)

次に、共分散w.r.tを取ります。 x_1とx_2、およびcov(x、x)= var(x)を使用:

cov(y、x_1)= m_1 * var(x_1)+ m_2 * covar(x_2、x_1)(1)

cov(y、x_2)= m_1 * covar(x_1、x_2)+ m_2 * var(x_2)(2)

これらは2つの未知数の2つの方程式であり、2X2行列を反転することで解くことができます。

マトリックス形式:...これは、反転させて...を生成できます。

det = var(x_1)* var(x_2)-covar(x_1、x_2)^ 2

(ああバーフ、一体何が「評判ポイント?方程式を見たい場合はギミ。」

いずれにせよ、m1とm2が閉じた形になったので、cについて(0)を解くことができます。

上記の解析解をExcelのソルバーでチェックして、ガウスノイズのある2次曲線を探しました。残差は6桁の有効数字に一致しています。

SQLで離散フーリエ変換を約20行で実行する場合は、私に連絡してください。

0
climbert8