Domanda

Devo calcolare una cifra che equivale al numero "più vicino" di mesi tra due date. Tuttavia, la funzione SAS standard (INTCK) non è orientata a considerare il GIORNO dei suoi parametri di data (ad es. Il codice riportato sotto si risolve in 0 quando ho bisogno di arrotondare a 1).

Qual è il modo "più accurato" per risolvere questo problema?

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

EDIT: risposta al commento di Martins.

Vorrei arrotondare a 0 mesi - Non credo che il confine sia rilevante. La funzione che sto cercando di replicare (NEAREST_MONTHS) proviene da DCS (applicazione del profeta di Sungard). Sto aspettando la possibilità di eseguire alcuni test all'interno dell'applicazione stessa per capire di più su come tratta le date (pubblicherò i risultati qui)).

Il file della guida contiene quanto segue: Categoria Data

Descrizione

Restituisce la differenza tra due date al numero di mesi più vicino. Se la seconda data è successiva alla prima, viene restituito 0.

Sintassi

NEAREST_MONTHS (Later_Date, Earlier_Date)

Tipo di valore restituito Numero intero

Esempi

NEAREST_MONTHS (data1, data2) Restituisce 8 se la data1 è il 20/03/1997 e la data2 è il 23/7/1996

NEAREST_MONTHS (data1, data2) Restituisce 26 se la data1 è 20/3/1997 e la data2 è 1/2/1995

È stato utile?

Soluzione

L'ho scritto come una funzione che penso calcola allo stesso modo dell'applicazione DCS. Utilizza alcune funzionalità nuove di SAS nella versione 9.2, inclusi gli allineamenti continui nelle date. Funziona anche in avanti o indietro nel tempo (ad esempio, fornisce un numero intero negativo se precedente_data è dopo successivo_data). Ho usato più di 15 giorni oltre l'intervallo come limite per arrotondare al mese successivo, ma puoi modificarlo se preferisci.

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;

Questo fornisce lo stesso riferimento:

x=8
x=26

Altri suggerimenti

Puoi usare INTNX per vedere se arrotondare per eccesso o per difetto, ad esempio


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 definisci un mese in 30 giorni, arrotonderesti 15 giorni o meno a 0 mesi e 16 giorni o più fino a 1 mese. Ciò può essere ottenuto mediante:

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;

Puoi anche adottare l'approccio per contare i mesi interi ("dal primo all'ultimo primo") nell'intervallo, quindi aggiungere tutti i giorni rimanenti per vedere se si sommano a 0, 1 o 2 mesi. In questo modo:

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;

Il primo metodo è più facile da capire e mantenere, ma il secondo è più preciso per intervalli di grandi dimensioni (da 01FEB06 a 01FEB09 è di 36 mesi, ma il metodo 1 ti dirà che sono solo 35).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top