Pergunta

Eu preciso calcular um número que equivale ao número 'mais próximo' de meses entre duas datas. No entanto, a função SAS padrão (INTCK) não é voltado para considerar a data da sua parâmetros de data (por exemplo, código abaixo resolve 0 quando eu precisar dele para arredondar para 1).

O que é a maneira 'mais puro' de como resolver esse problema?

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

EDIT:. Resposta ao comentário Martins

Eu arredondar para 0 meses - Eu não acho que a fronteira é relevante. A função que eu estou tentando replicar (NEAREST_MONTHS) vem de DCS (aplicação profeta Sungard). Agora estou esperando a chance de realizar alguns testes dentro do próprio aplicativo para entender mais sobre como ele trata datas (vai postar resultados de volta aqui)).

O arquivo de ajuda contém o seguinte: Categoria Data

Descrição

Retorna a diferença entre duas datas para o próximo número de meses. Se a segunda data for posterior à primeira data, então 0 é retornado.

Sintaxe

NEAREST_MONTHS (Later_Date, Earlier_Date)

Tipo de retorno Integer

Exemplos

NEAREST_MONTHS (date1, date2) Devoluções 8 se date1 é 20/3/1997 e date2 é 23/7/1996

NEAREST_MONTHS (date1, date2) Retorna 26 se date1 é 20/3/1997 e date2 é 1995/01/02

Foi útil?

Solução

Eu escrevi isso como uma função que eu acho que calcula, da mesma forma como a aplicação DCS. Ele usa algumas características que são novos para SAS na versão 9.2, incluindo alinhamentos contínuos em datas. Ele também funciona para a frente ou para trás no tempo (isto é, dá um inteiro negativo se earlier_date é depois later_date). Eu usei mais de 15 dias além do intervalo como o corte de volta para o próximo mês, mas você pode ajustar isso se você preferir.

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;

Isto dá o mesmo que sua referência:

x=8
x=26

Outras dicas

Você pode usar INTNX para ver se a até rodada ou para baixo, por exemplo.


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;

Se você definir um mês para ser 30 dias, você faria redondos 15 dias ou menos até 0 meses e 16 dias ou mais até 1 mês. Isto pode ser conseguido pelo seguinte:

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;

Você também pode tomar a abordagem para contar os meses completos ( "first 1º ao último dia 1º") no intervalo, e depois adicionar-se quaisquer dias restantes para ver se eles resumem a 0, 1 ou 2 meses. Como esta:

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;

O primeiro método é mais fácil de entender e manter, mas o segundo é mais preciso para grandes intervalos (01FEB06 para 01FEB09 é de 36 meses, mas o método 1 irá dizer-lhe que é apenas 35).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top