Существуют ли функции линейной регрессии в SQL Server? - PullRequest
25 голосов
/ 29 марта 2010

Есть ли в SQL Server 2005/2008 какая-либо функция линейной регрессии, аналогичная функциям линейной регрессии в Oracle ?

Ответы [ 8 ]

40 голосов
/ 29 марта 2010

Насколько мне известно, нет ни одного. Однако написать это довольно просто. Следующее дает вам постоянную альфа и бета наклона для y = Alpha + Beta * x + epsilon:

-- 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 для ясности. В качестве альтернативы вы можете использовать подзапросы. Пожалуйста, помните о точности типов данных, используемых в ваших таблицах, поскольку числовая стабильность может быстро ухудшиться, если точность не достаточно высока относительно ваших данных.

РЕДАКТИРОВАТЬ: (в ответ на вопрос Питера для дополнительной статистики, как 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 из-за проблем числовой стабильности . Мне кажется, что текущая реализация - лучший компромисс между стабильностью и сложностью. Я мог бы улучшить стабильность, заменив стандартное отклонение численно стабильным онлайн-алгоритмом, но это существенно усложнило бы реализацию (и замедлило бы ее). Аналогично, реализации, использующие, например, Компенсации Кахана (-Babuška-Neumaier) для SUM и AVG, по-видимому, работают немного лучше в ограниченных тестах, но делают запрос намного более сложным. И до тех пор, пока я не знаю, как T-SQL реализует SUM и AVG (например, он уже может использовать парное суммирование), я не могу гарантировать, что такие модификации всегда улучшают точность.

14 голосов
/ 19 июля 2010

Это альтернативный метод, основанный на публикации в блоге о линейной регрессии в 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
3 голосов
/ 07 января 2014

Я на самом деле написал подпрограмму SQL с использованием ортоганализации Грамма-Шмидта. Он, как и другие процедуры машинного обучения и прогнозирования, доступен по адресу sqldatamine.blogspot.com

По предложению Брэда Ларсона я добавил здесь код, а не просто направлял пользователей в мой блог. Это дает те же результаты, что и функция linest в Excel. Мой основной источник - «Элементы статистического обучения» (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
2 голосов
/ 02 апреля 2014

В SQL Server нет функций линейной регрессии. Но для вычисления простой линейной регрессии (Y '= bX + A) между парами точек данных x, y - включая вычисление коэффициента корреляции, коэффициента определения (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
1 голос
/ 14 апреля 2017

Чтобы добавить к ответу @ icc97, я включил взвешенные версии для наклона и пересечения. Если все значения постоянны, наклон будет НЕДЕЙСТВИТЕЛЕН (с соответствующими настройками SET ARITHABORT OFF; SET ANSI_WARNINGS OFF;), и его необходимо заменить на 0 с помощью coalesce ().

Вот решение, написанное на 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, чтобы подтвердить результаты. Может потребоваться преобразовать данные из некоторого источника данных в число с плавающей запятой. Я включил нестабильные версии, чтобы предупредить вас от них. (Отдельное спасибо Стефану в другом ответе.)

Помните, что корреляция - это корреляция точек данных x и y, а не прогноза.

0 голосов
/ 19 сентября 2015

Надеюсь, что следующий ответ поможет понять, откуда приходят некоторые решения. Я собираюсь проиллюстрировать это на простом примере, но обобщение для многих переменных теоретически просто, если вы знаете, как использовать индексную нотацию или матрицы. Для реализации решения для чего-либо, кроме трех переменных, вам понадобится Грам-Шмидт (см. Ответ Колина Кэмпбелла выше) или другой алгоритм матричной инверсии.

Поскольку все функции, которые нам нужны - это дисперсия, ковариация, среднее, сумма и т. Д., Являются функциями агрегирования в SQL, решение можно легко реализовать. Я сделал это в HIVE, чтобы выполнить линейную калибровку баллов логистической модели - среди многих преимуществ, одно из них заключается в том, что вы можете полностью функционировать в рамках HIVE, не выходя из языка сценариев и обратно.

Модель для ваших данных (x_1, x_2, y), где ваши точки данных индексируются по i, равна

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

Модель выглядит «линейной», но это необязательно. Например, x_2 может быть любой нелинейной функцией x_1, если в ней нет свободных параметров, например, x_2 = Синь (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). Минимизация этого w.r.t. 3 параметра (установите частные производные по каждому параметру = 0) дают 3 линейных уравнения для 3 неизвестных. Эти уравнения являются ЛИНЕЙНЫМИ по параметрам (именно это делает их линейной регрессией) и могут быть решены аналитически. Делать это для простой модели (1 переменная, линейная модель, следовательно, два параметра) просто и поучительно. Обобщение к неевклидовой метрической норме на векторном пространстве ошибок является простым, специальный диагональный случай сводится к использованию «весов».

Возвращаемся к нашей модели с двумя переменными:

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)

Это два уравнения с двумя неизвестными, которые можно решить, переворачивая матрицу 2X2.

в матричной форме: ... который можно перевернуть, чтобы получить ... где

det = var (x_1) * var (x_2) - ковар (x_1, x_2) ^ 2

(о боже, какого черта "очки репутации? Дай мне немного, если хочешь увидеть уравнения.)

В любом случае, теперь, когда у вас есть m1 и m2 в закрытом виде, вы можете решить (0) для c.

Я проверил приведенное выше аналитическое решение для Солвера в Excel для квадратичного с гауссовским шумом, а остаточные ошибки соответствуют 6 значащим цифрам.

Свяжитесь со мной, если вы хотите выполнить дискретное преобразование Фурье в SQL примерно в 20 строк.

0 голосов
/ 15 января 2015

Я перевел функцию линейной регрессии, используемую в прогнозе функций в Excel, и создал функцию SQL, которая возвращает a, b и прогноз. Вы можете увидеть полное теоретическое объяснение в справке по Excel для ПРОГНОЗНОЙ функции. Во-первых, вам нужно создать таблицу данных типа XYFloatType:

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

Затем напишите следующую функцию:

    /*
-- =============================================
-- 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);
*/
0 голосов
/ 26 апреля 2013

Здесь это как функция, которая принимает табличный тип типа: table (Y float, X double), который называется XYDoubleType и предполагает, что наша линейная функция имеет форму AX + B. Она возвращает A и B столбец таблицы на тот случай, если вы хотите, чтобы он был в соединении или что-то еще

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