Qualcuno ha una funzione di regressione di Theil-Sen scritta in T-SQL?
Ne ho trovato uno scritto in Perl , ma non sono in grado di ricodificarlo in SQL.
Qualcuno ha una funzione di regressione di Theil-Sen scritta in T-SQL?
Ne ho trovato uno scritto in Perl , ma non sono in grado di ricodificarlo in SQL.
Risposte:
Stavo mentendo quando ho detto che non sono in grado di ricodificarlo in SQL. Ero troppo pigro. Ecco il codice con un esempio di utilizzo.
Il Codice si basa su un Theisen libreria Perl, utilizzando QuickMedian . Definiamo un nuovo tipo di tabella per passare facilmente i nostri dati alla procedura.
CREATE TYPE dbo.TheilSenInputDataTableType AS TABLE
(
ID INT IDENTITY(1,1),
x REAL,
y REAL
)
Nota la colonna ID, che è importante qui poiché la nostra soluzione utilizza l'istruzione CROSS APPLY per ottenere una corretta interpretazione del ciclo interno trovato in TheilSen.pm.
my ($x1,$x2,$y1,$y2);
foreach my $i(0 .. $n-2){
$y1 = $y->[$i];
$x1 = $x->[$i];
foreach my $j($i+1 .. $n-1){
$y2 = $y->[$j];
$x2 = $x->[$j];
Sarà inoltre necessario un nuovo tipo di dati per la memorizzazione di una matrice di valori di tipo reale.
CREATE TYPE [dbo].[RealArray] AS TABLE(
[val] [real] NULL
)
Ecco la funzione f_QuickMedian , che restituisce la mediana per un determinato array. Il merito di questo va a Itzik Ben-Gan .
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
E lo stimatore 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
Esempio:
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
Ritorna:
m = 52.7079
c = -448.4853
Solo per un confronto, la versione perl restituisce i seguenti valori per lo stesso set di dati:
m = 52.7078651685394
c = -448.484943820225
Uso lo stimatore TheilSen per calcolare la metrica DaysToFill per i filesystem. Godere!
Questo molto probabilmente sarebbe adatto per fare qualcosa in SQLCLR, simile alla seguente domanda / risposta (anche qui su DBA.SE):
Esiste un'implementazione di SQL Server del problema del sottostring comune più lungo?
Quando avrò tempo dopo, vedrò quanto sarebbe fattibile.
Ho anche controllato T-SQL, Oracle e server in generale (troppo complessi per essere scritti in puro SQL).
Tuttavia, potresti essere interessato a questo (un pacchetto scientifico / statistico per Python). L'algoritmo è implementato anche lì e in Python. Python è un linguaggio che gli umani hanno almeno qualche possibilità di essere in grado di comprendere, a differenza del Perl.
La tua domanda mi ha incuriosito e ho scavato. Ci sono librerie C e C ++ che contengono questo algoritmo - ed è anche disponibile in alcuni pacchetti R. E anche il post di @srutzky sembra interessante.
+1 per una domanda interessante A proposito - e benvenuto nel forum :-)