Data Step

Limpieza de datos y eliminación de duplicados en fechas consecutivas

Simon 8 views

En el procesamiento de datos clínicos o transaccionales, es frecuente encontrar duplicados "imperfectos". Un caso clásico es cuando un evento (como un tratamiento médico) se registra dos veces: una vez en la fecha de inicio y una vez al día siguiente, creando así dos observaciones para un mismo evento.

Este artículo explora cómo limpiar un conjunto de datos donde cada observación se repite en dos días consecutivos, con el objetivo de eliminar la primera observación (la fecha más antigua) y conservar solo la segunda (la fecha más reciente).

El Problema

Imaginemos un conjunto de datos que contiene las variables id, group y treatmentdate. Cada tratamiento se extiende durante dos días consecutivos, generando dos líneas. Además, un mismo identificador (id) puede tener varios episodios de tratamiento distintos en el tiempo dentro del mismo grupo.

Ejemplo de datos brutos:

idgrouptreatmentdateNota
A1030Sep2017A eliminar
A1001Oct2017A conservar
A2106Nov2017A eliminar
A2107Nov2017A conservar
A1023Oct2017A eliminar (Nuevo episodio para A1)
A1024Oct2017A conservar

Un enfoque ingenuo utilizando PROC SQL con un GROUP BY id, group y MAX(day) fallaría aquí, ya que anularía la distinción entre los diferentes episodios de tratamiento (por ejemplo, para A1, solo mantendría el 24 de octubre y perdería el 1 de octubre).

La Solución Óptima

El método más robusto se basa en el uso del DATA Step combinado con una clasificación inteligente (PROC SORT). La idea es utilizar la función DIF para comparar las fechas entre las líneas, mientras se protegen los cambios de grupo.

Paso 1: La Clasificación (PROC SORT)

El truco consiste en clasificar los datos en orden descendente de fecha. Al colocar la fecha más reciente primero, transformamos el problema: en lugar de "buscar la siguiente línea para ver si es la misma", simplemente podemos comparar la línea actual con la anterior.

1 
2PROC SORT
3DATA=have out=inter;
4 
5BY id group DESCENDING treatmentdate;
6 
7RUN;
8 
¿Por qué DESCENDING? Si tenemos las fechas 30Sep y 01Oct, la clasificación descendente coloca 01Oct en primer lugar y 30Sep en segundo. Como queremos conservar 01Oct, se procesará primero (y se conservará por defecto), mientras que 30Sep podrá identificarse como "el día anterior" a la línea anterior y eliminarse.

Paso 2: La Limpieza (DATA Step)

Aquí está el código para filtrar los datos:

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

Análisis detallado del código

  1. dif1(treatmentdate) : Esta función calcula la diferencia entre el valor de treatmentdate de la línea actual y el de la línea anterior (Línea N - Línea N-1).

    • En nuestro caso ordenado: Línea anterior = 01Oct, Línea actual = 30Sep.

    • Cálculo: 30Sep - 01Oct = -1.

    • Si el resultado es -1, esto confirma que la línea actual es exactamente un día antes de la línea anterior.

  2. not first.group : Esta es una seguridad crucial.

    • La función DIF no "ve" los grupos; compara ciegamente la línea 10 con la línea 9, incluso si el ID cambia.

    • Si la última línea del paciente A es el 05Nov y la primera línea del paciente B es el 04Nov, DIF dará -1. Sin esta protección, eliminaría por error la primera línea del paciente B.

    • not first.group asegura que la eliminación nunca se realice en la primera línea de un nuevo grupo.

  3. Orden de las condiciones : Es imperativo colocar dif(...) en primer lugar o asegurarse de que se ejecute para cada línea. En SAS©, si usa if condition1 and condition2, y condition1 es falsa, condition2 a veces no se evalúa. Sin embargo, para la función DIF, es vital que "lea" cada línea para mantener su memoria desfasada correcta.

¿Por qué no usar otros métodos?

  • Enfoque matemático (MOD(_N_, 2)) : Se podría tener la tentación de mantener una línea de cada dos (if mod(_n_,2)=0). Esto es muy arriesgado. Si falta una sola observación en su conjunto de datos (por error de entrada) o si tiene un número impar de líneas, todo el desplazamiento se propaga y corrompe el resto de los datos.

  • Enfoque SQL (HAVING MAX(date)) : Como se mencionó anteriormente, SQL a menudo agrega demasiado ampliamente. Si un paciente tiene dos tratamientos distintos en el mes, el GROUP BY corre el riesgo de mantener solo uno (el último del mes), perdiendo así el historial intermedio.

Para eliminar la observación más antigua de un par de fechas consecutivas:

  1. Ordenar por ID y por fecha en orden descendente.

  2. Calcular la diferencia con la línea anterior (DIF).

  3. Eliminar si la diferencia es de -1 (día anterior), pero proteger la primera línea de cada grupo (FIRST.group).