CREATE FUNCTION [dbo].[f_QuickMedian](@RealArray RealArray READONLY)
RETURNS REAL
AS
BEGIN
DECLARE @Median REAL;
DECLARE @QMedian REAL;
SELECT @Median = AVG(1.0 * val)
FROM
(
SELECT o.val, rn = ROW_NUMBER() OVER (ORDER BY o.val), c.c
FROM @RealArray AS o
CROSS JOIN (SELECT c = COUNT(*) FROM @RealArray) AS c
) AS x
WHERE rn IN ((c + 1)/2, (c + 2)/2);
SELECT TOP 1 @QMedian = val FROM @RealArray
ORDER BY ABS(val - @Median) ASC, val DESC
RETURN @QMedian
END
和p_TheilSen估计器:
CREATE PROCEDURE [dbo].[p_TheilSen](
@TheilSenInput TheilSenInputDataTableType READONLY
, @m Real OUTPUT
, @c Real OUTPUT
)
AS
BEGIN
DECLARE
@m_arr RealArray
, @c_arr RealArray;
INSERT INTO @m_arr
SELECT m
FROM
(
SELECT
t1.x as x1
, t1.y as y1
, t2o.x as x2
, t2o.y as y2
, t2o.y-t1.y as [y2 - y1]
, t2o.x-t1.x as [x2 - x1]
, CASE WHEN (t2o.x <> t1.x) THEN CAST((t2o.y-t1.y) AS Real)/(t2o.x-t1.x) ELSE NULL END AS [($y2-$y1)/($x2-$x1)]
, CASE WHEN t1.y = t2o.y THEN 0
ELSE
CASE WHEN t1.x = t2o.x THEN NULL
ELSE
-- push @M, ($y2-$y1)/($x2-$x1);
CAST((t2o.y-t1.y) AS Real)/(t2o.x-t1.x)
END
END as m
FROM @TheilSenInput t1
CROSS APPLY
(
SELECT t2.x, t2.y
FROM @TheilSenInput t2
WHERE t2.ID > t1.ID
) t2o
) t
WHERE m IS NOT NULL
SELECT @m = dbo.f_QuickMedian(@m_arr)
INSERT INTO @c_arr
SELECT y - (@m * x)
FROM @TheilSenInput
SELECT @c = dbo.f_QuickMedian(@c_arr)
END
例子:
DECLARE
@in TheilSenInputDataTableType
, @m Real
, @c Real
INSERT INTO @in(x,y) VALUES (10.79,118.99)
INSERT INTO @in(x,y) VALUES (10.8,120.76)
INSERT INTO @in(x,y) VALUES (10.86,122.71)
INSERT INTO @in(x,y) VALUES (10.93,125.48)
INSERT INTO @in(x,y) VALUES (10.99,127.31)
INSERT INTO @in(x,y) VALUES (10.96,130.06)
INSERT INTO @in(x,y) VALUES (10.98,132.41)
INSERT INTO @in(x,y) VALUES (11.03,135.89)
INSERT INTO @in(x,y) VALUES (11.08,139.02)
INSERT INTO @in(x,y) VALUES (11.1,140.25)
INSERT INTO @in(x,y) VALUES (11.19,145.61)
INSERT INTO @in(x,y) VALUES (11.25,153.45)
INSERT INTO @in(x,y) VALUES (11.4,158.03)
INSERT INTO @in(x,y) VALUES (11.61,162.72)
INSERT INTO @in(x,y) VALUES (11.69,167.67)
INSERT INTO @in(x,y) VALUES (11.91,172.86)
INSERT INTO @in(x,y) VALUES (12.07,177.52)
INSERT INTO @in(x,y) VALUES (12.32,182.09)
EXEC p_TheilSen @in, @m = @m OUTPUT, @c = @c OUTPUT
SELECT @m
SELECT @c
当我说我无法将其重新编码为 SQL 时,我是在撒谎。我只是太懒了。这是带有使用示例的代码。
该代码基于TheiSen perl 库,使用QuickMedian。让我们定义一个新的表类型,以便轻松地将我们的数据传递给过程。
请注意 ID 列,这在这里很重要,因为我们的解决方案使用 CROSS APPLY 语句来正确解释 TheilSen.pm 中的内部循环。
我们还需要一种新的数据类型来存储实型值数组。
这是f_QuickMedian函数,返回给定数组的中值。这归功于 Itzik Ben-Gan。
和p_TheilSen估计器:
例子:
退货:
只是为了比较,perl 版本为同一数据集返回以下值:
我使用 TheilSen 估算器来计算文件系统的 DaysToFill 指标。享受!
我还检查了一般的 T-SQL、Oracle 和服务器(太复杂,无法用纯 SQL 编写)。
但是,您可能对此感兴趣(Python 的科学/统计包)。该算法也在那里和 Python 中实现。与 Perl 不同,Python 是一种人类至少有一定机会能够理解的语言。
你的问题引起了我的兴趣,我四处寻找。有包含此算法的 C 和 C++ 库 - 它也可以在一些 R 包中使用。@srutzky 的帖子看起来也很有趣。
+1 一个有趣的问题 BTW - 欢迎来到论坛 :-)
这很可能非常适合在 SQLCLR 中执行某些操作,类似于以下问题/答案(也在 DBA.SE 上):
是否有最长公共子串问题的 SQL Server 实现?
等我以后有空的时候,我会看看这有多可行。