2017-10-05 4 views
2

Ich habe Daten, wo ich versuche, Muster zu identifizieren. Die Daten in jeder Tabelle sind jedoch nicht vollständig (es fehlen Zeilen). Ich möchte die Tabelle in Stücke von vollständigen Daten trennen und dann die Muster von jedem identifizieren. Ich habe eine Spalte, in der ich identifizieren kann, ob die Daten vollständig sind oder nicht sequence genannt werden.SQL Split Daten durch kontinuierliche zunehmende Sequenz und dann Teilmenge jeweils durch ein Muster

Die Daten werden wie folgt aussehen:

Sequence  Position 
1    open 
2    closed 
3    open 
4    open 
5    closed 
8    closed 
9    open 
11    open 
13    closed 
14    open 
15    open 
18    closed 
19    open 
20    closed 

Zuerst habe ich die Daten in komplette Abschnitte aufteilen möchte:

Sequence  Position 
    1    open 
    2    closed 
    3    open 
    4    open 
    5    closed 
--------------------------- 
    8    closed 
    9    open 
--------------------------- 
    11    open 
--------------------------- 
    13    closed 
    14    open 
    15    open 
--------------------------- 
    18    closed 
    19    open 
    20    closed 

Dann würde ich das Muster so zu identifizieren, wie closed open, ..., open, closed, dass wir gehen von geschlossen zu offen für n Zeilen (wobei n mindestens 1 ist) und dann zurück zu geschlossen

Aus den Beispieldaten würde dies lauten:

 Sequence  Position 
     2    closed 
     3    open 
     4    open 
     5    closed 
    --------------------------- 
     18    closed 
     19    open 
     20    closed 

Dies lässt meine Final Table, wo ich die Analyse durchführen kann, da ich weiß, dass es keine gebrochenen Sequenzen gibt. Ich habe auch eine andere Spalte, in der position binär ist, wenn das einfacher ist, mit zu arbeiten.

Die Tabellen sind groß, obwohl ich denke, dass ich Schleifen schreiben kann, um mein Ergebnis herauszufinden, glaube ich nicht, dass diese Methode effizient genug wäre. Alternativ würde ich die ganze Tabelle in R ziehen, dann die Ergebnistabelle finden, aber dies erfordert alles in R zieht zuerst so frage ich mich, ob dies in SQL

EDIT machbar ist: Verschiedene Beispieldaten, die mehr Vertreter sind:

Sequence  Position 
    1    open 
    2    closed 
    3    open 
    4    open 
    5    closed 
    8    closed 
    9    open 
    11    open 
    13    closed 
    14    open 
    15    open 
    18    closed 
    19    open 
    20    closed 
    21    closed 
    22    closed 
    23    closed 
    24    open 
    25    open 
    26    closed 
    27    open 

Hinweis sollte die gleichen Ergebnisse haben aber auch mit

23    closed 
    24    open 
    25    open 
    26    closed 

21, 22 und 27 sind nicht, wie sie die closed passen nicht, open ..., open, closed Muster

aber wenn wir 28 closed hätten wir 27 und 28 wollen, da es keine Zeitlücke und das Muster passen würde. Wenn statt 28 es 29 closed wäre, würden wir 27 oder 29 nicht wollen (weil, obwohl das Muster richtig ist, die Folge bricht).

Um etwas Kontext hinzuzufügen, denken Sie an eine Maschine, die von Stop, zu Laufen, zu Stoppen geht. Wir nehmen die Daten auf, haben aber Lücken in der Aufzeichnung, die hier durch das Aufbrechen der Sequenzen repräsentiert sind. Sowie fehlende Daten in der Mitte des Stop-Running-Stop-Zyklus; Die Daten werden manchmal auch aufgenommen, wenn das Gerät bereits läuft oder die Aufzeichnung stoppt, bevor das Gerät stoppt. Ich möchte diese Daten nicht, da es sich nicht um einen vollständigen Zyklus von Stop, Running und Stop handelt. Ich möchte nur diese vollständigen Zyklen, und wo die Sequenz kontinuierlich war. Dies bedeutet, dass ich meinen ursprünglichen Datensatz in einen mit nur vollständigen Zyklen nacheinander umwandeln kann.

+0

Ich würde vorschlagen, dass Sie eine SQL Fiddle oder Rextester einrichten. –

+0

was willst du eigentlich Spilled bedeutet? Tabellen dafür spinnen? –

+0

Nein, nur ein 'select', das die Daten filtert. – Olivia

Antwort

1

Sie können es verwenden.

DECLARE @MyTable TABLE (Sequence INT, Position VARCHAR(10)) 

INSERT INTO @MyTable 
VALUES 
(1,'open'), 
(2,'closed') , 
(3,'open'), 
(4,'open'), 
(5,'closed'), 
(8,'closed'), 
(9,'open'), 
(11,'open'), 
(13,'closed'), 
(14,'open') , 
(15,'open'), 
(18,'closed'), 
(19,'open'), 
(20,'closed'), 
(21,'closed'), 
(22,'closed'), 
(23,'closed'), 
(24,'open'), 
(25,'open'), 
(26,'closed'), 
(27,'open') 


;WITH CTE AS(
    SELECT * , 
     CASE WHEN Position ='closed' AND LAG(Position) OVER(ORDER BY [Sequence]) ='closed' THEN 1 ELSE 0 END CloseMark 
    FROM @MyTable 
) 
,CTE_2 AS 
(
    SELECT 
     [New_Sequence] = [Sequence] + (SUM(CloseMark) OVER(ORDER BY [Sequence] ROWS UNBOUNDED PRECEDING)) 
     , [Sequence] 
     , Position 
    FROM CTE 
) 
,CTE_3 AS (
    SELECT *, 
    RN = ROW_NUMBER() OVER(ORDER BY [New_Sequence]) 
    FROM CTE_2 
) 
,CTE_4 AS 
(
    SELECT ([New_Sequence] - RN) G 
    , MIN(CASE WHEN Position = 'closed' THEN [Sequence] END) MinCloseSq 
    , MAX(CASE WHEN Position = 'closed' THEN [Sequence] END) MaxCloseSq 
    FROM CTE_3 
    GROUP BY ([New_Sequence] - RN) 
) 
SELECT 
    CTE.Sequence, CTE.Position 
FROM CTE_4 
    INNER JOIN CTE ON (CTE.Sequence BETWEEN CTE_4.MinCloseSq AND CTE_4.MaxCloseSq) 
WHERE 
    CTE_4.MaxCloseSq > CTE_4.MinCloseSq 
    AND (CTE_4.MaxCloseSq IS NOT NULL AND CTE_4.MinCloseSq IS NOT NULL) 

Ergebnis:

Sequence Position 
----------- ---------- 
2   closed 
3   open 
4   open 
5   closed 
---   --- 
18   closed 
19   open 
20   closed 
---   --- 
23   closed 
24   open 
25   open 
26   closed 
+0

Das scheint auf meinen echten Daten nicht zu funktionieren. Meine Daten haben viel längere Perioden von geschlossen und offen, die sich wiederholen. Aber das Format ist das gleiche. Was ist los? - Ich rede wie 1000 geschlossen dann tausend offen etc. – Olivia

+0

Können Sie mehr Daten für das Testen hinzufügen? –

+0

Es tut mir leid, ich habe bemerkt, dass meine Daten das Problem sind. Ich erstelle eine Sequenz mit 'round (((round (float, datetime), 5) - 42961.58227) * 99999.97 + 1), 1)' bemerkte aber einige Duplikate/seltsame Daten, also werde ich sie einfach entfernen und es erneut versuchen - danke für die schnelle Antwort aber – Olivia

0

Ich denke, es gibt tatsächlich eine relativ einfache Möglichkeit, dies zu betrachten.

  • Mit Blick auf die Sequenz des vorherigen schließen
  • Mit Blick auf die kumulativen öffnet für die vorherige Schließung und der aktuellen Nähe
  • Arithmetik tun alles, um sicherzustellen, die Zwischenprodukte: Sie können die Schließfolgenummer identifizieren sind in den

Diese verwandelt sich in eine Abfragedaten:

select t.*, 
     lag(sequence) over (partition by position order by sequence) as prev_sequence, 
     lag(cume_opens) over (partition by position order by cume_opens) as prev_cume_opens 
from (select t.*, 
      sum(case when position = 'open' then 1 else 0 end) over (order by sequence) as cume_opens 
     from t 
    ) t 
where position = 'close' and 
     (cume_opens - prev_cume_opens) = sequence - prev_sequence - 1 and 
     sequence > prev_sequence - 1; 

Nachdem Sie nun die Sequenzen identifiziert haben, können Sie die ursprünglichen Reihen zu erhalten kommen zurück zu:

select t.* 
from t join 
    (select t.*, 
      lag(sequence) over (partition by position order by sequence) as prev_sequence, 
      lag(cume_opens) over (partition by position order by cume_opens) as prev_cume_opens 
     from (select t.*, 
        sum(case when position = 'open' then 1 else 0 end) over (order by sequence) as cume_opens 
      from t 
      ) t 
     where position = 'close' and 
      (cume_opens - prev_cume_opens) = sequence - prev_sequence - 1 and 
      sequence > prev_sequence - 1 
    ) seqs 
    on t.sequence between seqs.prev_sequence and seqs.sequence; 

Ich gebe zu, dass ich nicht getestet haben. Ich denke aber, dass die Idee funktioniert. Die eine Sache ist, dass es mehrere "nahe" Perioden pro Sequenzgruppe wählt.

Verwandte Themen