Problema de FECHA SAS: cálculo de & # 8220; MES MÁS CERCANO & # 8221;

StackOverflow https://stackoverflow.com/questions/1042835

  •  22-07-2019
  •  | 
  •  

Pregunta

Necesito calcular una cifra que equivale al número 'más cercano' de meses entre dos fechas. Sin embargo, la función SAS estándar (INTCK) no está diseñada para considerar el DÍA de sus parámetros de fecha (por ejemplo, el código a continuación se resuelve en 0 cuando necesito redondear a 1).

¿Cuál es la forma "más perfecta" de resolver este problema?

data _null_;
    x="01APR08"d;
    y="28APR08"d;
    z=intck('MONTH',x,y);
    put z= ;
run;

EDITAR: respuesta al comentario de Martins.

Redondearía a 0 meses. No creo que la frontera sea relevante. La función que intento replicar (NEAREST_MONTHS) proviene de DCS (aplicación de profeta Sungard). Ahora estoy esperando la oportunidad de realizar algunas pruebas dentro de la aplicación para comprender mejor cómo trata las fechas (publicará los resultados aquí)).

El archivo de ayuda contiene lo siguiente: Categoría Fecha

Descripción

Devuelve la diferencia entre dos fechas al número de meses más cercano. Si la segunda fecha es posterior a la primera, se devuelve 0.

Sintaxis

NEAREST_MONTHS (Later_Date, Earlier_Date)

Tipo de retorno Entero

Ejemplos

NEAREST_MONTHS (fecha1, fecha2) Devuelve 8 si date1 es 20/3/1997 y date2 es 23/7/1996

NEAREST_MONTHS (fecha1, fecha2) Devuelve 26 si date1 es 20/3/1997 y date2 es 1/2/1995

¿Fue útil?

Solución

Escribí esto como una función que creo que calcula de la misma manera que la aplicación DCS. Utiliza algunas características que son nuevas para SAS en la versión 9.2, incluidas las alineaciones continuas en las fechas. También funciona hacia adelante o hacia atrás en el tiempo (es decir, da un número entero negativo si before_date es posterior a later_date). Utilicé más de 15 días más allá del intervalo como límite para redondear al próximo mes, pero puede ajustar esto si lo prefiere.

proc fcmp outlib=work.myfuncs.dates;
   function nearest_months(later_date,earlier_date);
        /* Return missing if inputs are missing */
        if (earlier_date eq . ) OR (later_date eq . ) then
            nearest_month=.;
        else do; 
            /* Use 'cont' argument for continuous dates */
            months=intck('MONTH',earlier_date,later_date,'cont');
            if months < 0 then months=months+1;
            days= later_date - intnx('month', earlier_date,months,'same');

            /* Handle negatives (earlier dates) */
            if months < 0 then do;
                if days < -15 then months=months-1;
                nearest_month=months;
                end;
            else do;
                if days > 15 then months + 1;
                nearest_month=months;
                end;
        end;
        return(nearest_month);
   endsub;
run;
options cmplib=work.myfuncs;


data _null_;
x=nearest_months('20Mar1997'd, '23JUL1996'd);
put x=;
x=nearest_months('20Mar1997'd, '01FEB1995'd);
put x=;
run;

Esto da lo mismo que su referencia:

x=8
x=26

Otros consejos

Puede usar INTNX para ver si se debe redondear hacia arriba o hacia abajo, por ejemplo


data _null_;
  format x y date9. z 8.;
  x="01APR08"d;
  y="28APR08"d;
  z=intck('MONTH',x,y);

  * wl is x + z months;
  wl=intnx('MONTH',x,z);

  * wu is x + (z+1) months;
  wu=intnx('MONTH',x,z+1);

  * If y is closer to wu, then adjust z by 1;
  if (abs(y-wu) lt abs(y-wl)) then z = z+1;     

  put x y z=;
run;

Si define un mes como 30 días, redondeará 15 días o menos a 0 meses y 16 días o más a 1 mes. Esto se puede lograr de la siguiente manera:

data _null_;
  format x y date9. z 8.;
  x="14FEB09"d;
  y="02MAR09"d;

  z=round(intck('DAY',x,y)/31);
  put x y z=;
run;

También puede tomar el enfoque para contar los meses completos (" primero, primero al último ") en el intervalo, y luego sumar los días restantes para ver si suman 0, 1 o 2 meses. Así:

data _null_;
  format x y date9. z 8.;
  x="01FEB09"d;
  y="31MAR09"d;

  if day(x)=1 then do;
     z=intck('MONTH',x,intnx('MONTH',y,0,'BEGINNING'))
         + round((intck('DAY',intnx('MONTH',y,0,'BEGINNING'),y))/31);
  end;
  else do;
     z=intck('MONTH',intnx('MONTH',x,1,'BEGINNING'),intnx('MONTH',y,0,'BEGINNING'))
         + round((intck('DAY',x,intnx('MONTH',x,1,'BEGINNING'))+intck('DAY',intnx('MONTH',y,0,'BEGINNING'),y))/31);
  end;
  put x y z=;
run;

El primer método es más fácil de entender y mantener, pero el segundo es más preciso para intervalos grandes (01FEB06 a 01FEB09 son 36 meses, pero el método 1 le dirá que solo son 35).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top