Data Step

Datenbereinigung und Duplikatenentfernung bei aufeinanderfolgenden Daten

Simon 3 vues

Bei der Verarbeitung klinischer oder transaktionaler Daten treten häufig "unvollständige" Duplikate auf. Ein klassischer Fall ist, dass ein Ereignis (wie eine medizinische Behandlung) zweimal erfasst wird: einmal am Startdatum und einmal am Folgetag, wodurch zwei Beobachtungen für dasselbe Ereignis entstehen.

Dieser Artikel untersucht, wie ein Datensatz bereinigt werden kann, bei dem jede Beobachtung an zwei aufeinanderfolgenden Tagen wiederholt wird, mit dem Ziel, die erste Beobachtung (das älteste Datum) zu entfernen und nur die zweite (das jüngste Datum) beizubehalten.

Das Problem

Stellen wir uns einen Datensatz vor, der die Variablen id, group und treatmentdate enthält. Jede Behandlung erstreckt sich über zwei aufeinanderfolgende Tage und erzeugt zwei Zeilen. Darüber hinaus kann eine einzige Kennung (id) innerhalb derselben Gruppe mehrere unterschiedliche Behandlungszeiträume haben.

Beispiel für Rohdaten:

idgrouptreatmentdateHinweis
A1030Sep2017Zu löschen
A1001Oct2017Zu behalten
A2106Nov2017Zu löschen
A2107Nov2017Zu behalten
A1023Oct2017Zu löschen (Neue Episode für A1)
A1024Oct2017Zu behalten

Ein naiver Ansatz unter Verwendung von PROC SQL mit einem GROUP BY id, group und MAX(day) würde hier fehlschlagen, da er die Unterscheidung zwischen den verschiedenen Behandlungszeiträumen überschreiben würde (z.B. für A1 würde er nur den 24. Oktober behalten und den 1. Oktober verlieren).

Die optimale Lösung

Die robusteste Methode basiert auf der Verwendung des DATA Step in Kombination mit einer intelligenten Sortierung (PROC SORT). Die Idee ist, die Funktion DIF zu verwenden, um die Daten zwischen den Zeilen zu vergleichen, während Gruppenwechsel geschützt werden.

Schritt 1: Die Sortierung (PROC SORT)

Der Trick besteht darin, die Daten in absteigender Reihenfolge nach Datum zu sortieren. Indem das jüngste Datum zuerst platziert wird, verwandeln wir das Problem: Anstatt "nach der nächsten Zeile zu suchen, um zu sehen, ob sie gleich ist", können wir einfach die aktuelle Zeile mit der vorherigen vergleichen.

1 
2PROC SORT
3DATA=have out=inter;
4 
5BY id group DESCENDING treatmentdate;
6 
7RUN;
8 
Warum DESCENDING? Wenn wir die Daten 30Sep und 01Oct haben, platziert die absteigende Sortierung 01Oct an erster Stelle und 30Sep an zweiter. Da wir 01Oct behalten wollen, wird es zuerst verarbeitet (und standardmäßig beibehalten), während 30Sep als "Vortag" der vorherigen Zeile identifiziert und gelöscht werden kann.

Schritt 2: Die Bereinigung (DATA Step)

Hier ist der Code zum Filtern der Daten:

1DATA want;
2 SET inter;
3 BY id group DESCENDING treatmentdate;
4 
5 /* La condition magique */
6 IF dif1(treatmentdate) = -1 and not first.group THEN delete;
7RUN;

Detaillierte Code-Analyse

  1. dif1(treatmentdate): Diese Funktion berechnet die Differenz zwischen dem Wert von treatmentdate der aktuellen Zeile und dem der vorherigen Zeile (Zeile N - Zeile N-1).

    • In unserem sortierten Fall: Vorherige Zeile = 01Oct, Aktuelle Zeile = 30Sep.

    • Berechnung: 30Sep - 01Oct = -1.

    • Wenn das Ergebnis -1 ist, bestätigt dies, dass die aktuelle Zeile genau einen Tag vor der vorherigen Zeile liegt.

  2. not first.group: Dies ist eine entscheidende Absicherung.

    • Die Funktion DIF "sieht" die Gruppen nicht; sie vergleicht Zeile 10 stur mit Zeile 9, selbst wenn sich die ID ändert.

    • Wenn die letzte Zeile von Patient A der 05Nov und die erste Zeile von Patient B der 04Nov ist, gibt DIF -1 zurück. Ohne diesen Schutz würden Sie versehentlich die erste Zeile von Patient B löschen.

    • not first.group stellt sicher, dass die Löschung niemals in der ersten Zeile einer neuen Gruppe erfolgt.

  3. Reihenfolge der Bedingungen: Es ist zwingend erforderlich, dif(...) zuerst zu platzieren oder sicherzustellen, dass es für jede Zeile ausgeführt wird. In SAS©, wenn Sie if condition1 and condition2 verwenden und condition1 falsch ist, wird condition2 manchmal nicht ausgewertet. Für die Funktion DIF ist es jedoch entscheidend, dass sie jede Zeile "liest", um ihren verschobenen Speicher korrekt zu halten.

Warum keine anderen Methoden verwenden?

  • Mathematischer Ansatz (MOD(_N_, 2)): Man könnte versucht sein, jede zweite Zeile beizubehalten (if mod(_n_,2)=0). Dies ist sehr riskant. Wenn eine einzige Beobachtung in Ihrem Datensatz fehlt (durch Eingabefehler) oder Sie eine ungerade Anzahl von Zeilen haben, breitet sich die gesamte Verschiebung aus und korrumpiert den Rest der Daten.

  • SQL-Ansatz (HAVING MAX(date)): Wie bereits erwähnt, aggregiert SQL oft zu weit. Wenn ein Patient zwei verschiedene Behandlungen in einem Monat hat, riskiert GROUP BY, nur eine davon zu behalten (die letzte des Monats), wodurch die Zwischenhistorie verloren geht.

Um die älteste Beobachtung eines Paares aufeinanderfolgender Daten zu löschen:

  1. Sortieren Sie nach ID und Datum in absteigender Reihenfolge.

  2. Berechnen Sie die Differenz zur vorherigen Zeile (DIF).

  3. Löschen Sie, wenn die Differenz -1 (Vortag) beträgt, aber schützen Sie die erste Zeile jeder Gruppe (FIRST.group).