Data Step

Nettoyage de données et suppression de doublons sur dates consécutives

Simon 6 Aufrufe

Dans le traitement de données cliniques ou transactionnelles, il est fréquent de rencontrer des doublons "imparfaits". Un cas classique est celui où un événement (comme un traitement médical) est enregistré deux fois : une fois à la date de début et une fois le jour suivant, créant ainsi deux observations pour un même événement.

Cet article explore comment nettoyer un jeu de données où chaque observation est répétée sur deux jours consécutifs, avec pour objectif de supprimer la première observation (la date la plus ancienne) et de ne conserver que la seconde (la date la plus récente).

Le Problème

Imaginons un jeu de données contenant les variables id, group et treatmentdate. Chaque traitement s'étend sur deux jours consécutifs, générant deux lignes. De plus, un même identifiant (id) peut avoir plusieurs épisodes de traitement distincts dans le temps au sein du même groupe.

Exemple de données brutes :

idgrouptreatmentdateNote
A1030Sep2017À supprimer
A1001Oct2017À garder
A2106Nov2017À supprimer
A2107Nov2017À garder
A1023Oct2017À supprimer (Nouvel épisode pour A1)
A1024Oct2017À garder

Une approche naïve utilisant PROC SQL avec un GROUP BY id, group et MAX(day) échouerait ici, car elle écraserait la distinction entre les différents épisodes de traitement (par exemple, pour A1, elle ne garderait que le 24 octobre et perdrait le 1er octobre).

La Solution Optimale

La méthode la plus robuste repose sur l'utilisation du DATA Step combiné à un tri intelligent (PROC SORT). L'idée est d'utiliser la fonction DIF pour comparer les dates entre les lignes, tout en protégeant les changements de groupes.

Étape 1 : Le Tri (PROC SORT)

L'astuce consiste à trier les données par ordre décroissant de date. En mettant la date la plus récente en premier, nous transformons le problème : au lieu de "chercher la ligne suivante pour voir si c'est la même", nous pouvons simplement comparer la ligne actuelle à la précédente.

1 
2PROC SORT
3DATA=have out=inter;
4 
5BY id group DESCENDING treatmentdate;
6 
7RUN;
8 
Pourquoi DESCENDING ? Si nous avons les dates 30Sep et 01Oct, le tri décroissant place 01Oct en première position et 30Sep en seconde. Comme nous voulons garder 01Oct, elle sera traitée en premier (et conservée par défaut), tandis que 30Sep pourra être identifiée comme "la veille" de la ligne précédente et supprimée.

Étape 2 : Le Nettoyage (DATA Step)

Voici le code pour filtrer les données :

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;

Analyse détaillée du code

  1. dif1(treatmentdate) : Cette fonction calcule la différence entre la valeur de treatmentdate de la ligne actuelle et celle de la ligne précédente (Ligne N - Ligne N-1).

    • Dans notre cas trié : Ligne précédente = 01Oct, Ligne actuelle = 30Sep.

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

    • Si le résultat est -1, cela confirme que la ligne actuelle est exactement un jour avant la ligne précédente.

  2. not first.group : C'est une sécurité cruciale.

    • La fonction DIF ne "voit" pas les groupes ; elle compare bêtement la ligne 10 à la ligne 9, même si l'ID change.

    • Si la dernière ligne du patient A est le 05Nov et la première ligne du patient B est le 04Nov, DIF donnera -1. Sans cette protection, vous supprimeriez par erreur la première ligne du patient B.

    • not first.group assure que la suppression ne se fait jamais sur la première ligne d'un nouveau groupe.

  3. Ordre des conditions : Il est impératif de placer dif(...) en premier ou de s'assurer qu'il est exécuté pour chaque ligne. Dans SAS©, si vous utilisez if condition1 and condition2, et que condition1 est fausse, condition2 n'est parfois pas évaluée. Cependant, pour la fonction DIF, il est vital qu'elle "lise" chaque ligne pour maintenir sa mémoire décalée correcte.

Pourquoi ne pas utiliser d'autres méthodes ?

  • Approche mathématique (MOD(_N_, 2)) : On pourrait être tenté de garder une ligne sur deux (if mod(_n_,2)=0). C'est très risqué. Si une seule observation manque dans votre jeu de données (par erreur de saisie) ou si vous avez un nombre impair de lignes, tout le décalage se propage et corrompt le reste des données.

  • Approche SQL (HAVING MAX(date)) : Comme mentionné plus haut, SQL agrège souvent trop largement. Si un patient a deux traitements distincts dans le mois, le GROUP BY risque de n'en garder qu'un seul (le dernier du mois), perdant ainsi l'historique intermédiaire.

Pour supprimer l'observation la plus ancienne d'une paire de dates consécutives :

  1. Triez par ID et par date en ordre décroissant.

  2. Calculez la différence avec la ligne précédente (DIF).

  3. Supprimez si la différence est de -1 (jour précédent), mais protégez la première ligne de chaque groupe (FIRST.group).