SAS: Cálculo de la edad

 

Se puede calcular la edad de muchas maneras, pero la más cómoda es utilizando la función intck(). Esta función calcula distintas entre fechas expresadas en distintas unidades de medidas: días, meses, años, etc. Ya he escrito algo sobre esta función en este artículo.

Para calcular la edad natural a fecha de hoy podemos utilizar la siguiente fórmula, que es muy simple:

edad = intck('year',fec_nacimiento,date(),'c');

En la función anterior, ‘year’ indica que se va a medir la diferencia entre las fechas en años, el segundo parámetro es la fecha de nacimiento que tengamos. En el tercero indicamos la fecha de hoy con la función date() y finalmente, el cuarto parámetro indica que la diferencia en años resultante tiene que ser a años cumplidos, o sea, que tener 35 años y 11 meses implica que la función devolverá 35 años.

Si queremos calcular esa edad a una fecha concreta del pasado, o del futuro, solo debemos sustituir el segundo parámetro por la fecha que queramos, por ejemplo ’31dec2022’d:

edad = intck('year',fec_nacimiento,'31dec2022'd,'c');

Finalmente, hay otra edad que se calcula en el mundo de los seguros que es la edad técnica y que sirve para calcular el riesgo asociado a un individuo y su prima correspondiente. Esta edad técnica es el redondeo de la edad natural al entero más cercano, o sea, 35 años y 7 meses implica que tienes 36 años técnicos. Esto se calcularía así:

edad_tecnica = intck('year',fec_nacimiento,date());
edad_tecnica2 = round((date() - fec_nacimiento) / 365.25);

Entre estas dos últimas hay una pequeña diferencia cuando te aproximas a la fecha de cumpleaños, pero he visto la segunda muchas veces en muchos ejemplos de código. Yo, prefiero la primera, que es más exacta.

SAS: Operar con fechas: función intck()

La función intck(), al igual que intnx(), es una de las más utilizadas para trabajar con fechas. intck() calcula distancias entre dos fechas que se le proporcionan. Los usos más comunes son calcular la edad, calcular la antigüedad de un cliente o contrato, calcular la añada, etc. Veamos su sintaxis básica:

Sintaxis

intck(intervalo, fecha_inicio; fecha_fin)

Esta función calcula, como hemos dicho, la distancia entre fecha_inicio y fecha_fin expresándola en las unidades de un intervalo concreto. Esto es, se puede medir esa distancia en años, en meses o en días si fuera necesario. Vamos a ver algunos ejemplos:

intck(‘day’,’1jul2021’d,’1dec2021’d) 153 Restar días es equivalente a hacer la operación ‘1dec2021’d – ‘1jul2021’d, ya que las fechas se pueden operar directamente.
intck(‘month’,’1jul2021’d,’1dec2021’d) 5 Transcurren 5 meses entre las dos fechas: julio, agosto, septiembre, octubre y noviembre.
intck(‘month’,’31jul2021’d,’1dec2021’d) 5 Sin embargo, en este caso también se contabilizan 5 meses a pesar de que se está contando desde el último día de julio.
intck(‘year’,’31dec2020’d,’1jan2021’d) 1 Lo mismo ocurre contando años desde el último día de un año y el primero del siguiente.

Método

El método es el cuarto (y último) parámetro que podemos incluir en intck. Indica como vamos a tratar la operación de cálculo de la distancia entre fechas, si vamos a utilizar el método continuo o discreto.

Método continuo

El método continuo considera que es necesario que se cumpla un periodo completo para contarlo, esto quiere decir, si estamos midiendo una distancia en meses tienen que haber pasado todos los días de un mes hasta llegar al mismo día de mes del mes siguiente para que cuente 1. Si falta un solo día el resultado de la operación será cero. Este es el tipo de operación que debemos utilizar para calcular la edad y debemos indicarlo con una ‘C’ como cuarto parámetro.
Vamos a verlo con un ejemplo:

intck(‘year’,’31dec2020’d,’1jan2021’d,’C’) 0
intck(‘year’,’31dec2020’d,’31dec2021’d,’C’) 1

Método discreto

El método discreto contabiliza cuantos finales de periodo han ocurrido entre ambas fechas. Este es el valor por defecto del método y se indica con una ‘D’ como último parámetro. De esta manera, tomando el ejemplo anterior, la distancia en años entre el 31 de diciembre y el 1 de enero del año siguiente es 1. Este método es apropiado para calcular, por ejemplo, cuántas renovaciones ha tenido una póliza de seguros.

intck(‘year’,’31dec2020’d,’1jan2021’d,’D’) 1
intck(‘year’,’31dec2020’d,’31dec2021’d,’D’) 2

SAS: Convertir de texto a fecha

Convertir de un texto a una fecha es una pregunta habitual para nuevos programadores en SAS, hay varios enfoques válidos para hacerlo:
La mejor solución al problema es aplicar la función input() sobre la cadena de entrada para cambiarle el informat al nuevo campo (fecha) e inmediatamente después aplicar el formato con put(). Incluyo dos ejemplos distintos de formato de entrada y de salida:

data SALIDA2;
    cadena1 = "2021/07/15";
    fecha2 = put(input(cadena,yymmdd10.),date9.);
    cadena2 = "20220115";
    fecha2 = input(put(cadena,z8.),yymmdd8.);
run;

Otra solución es tratar la cadena de texto obteniendo cada uno de los tres componentes de la fecha: día, mes, año y pasándolos como parámetro a una función mdy() para formar la fecha.

data SALIDA;
    cadena = "2021/07/15";
    dia = input(put(substr(cadena,9,2),$2.),8.);
    mes = input(put(substr(cadena,6,2),$2.),8.);
    anyo = input(put(substr(cadena,1,4),$4.),8.);
    fecha = put(mdy(mes,dia,anyo),date9.);
run;

Este enfoque es engorroso y largo pero da total libertad al programador: Por ejemplo, en caso de que la entrada no sea exactamente una fecha, sino un dato como «202107» pero sin embargo quisiéramos conseguir una salida que fuera el primer día del mes.

data SALIDA;
    cadena = "202107";
    mes = input(put(substr(cadena,5,2),$2.),8.);
    anyo = input(put(substr(cadena,1,4),$4.),8.);
    fecha = put(mdy(mes,1,anyo),date9.);
run;

Truco SAS: Convertir fechas datetime de Unix a SAS

Como sabemos, los formatos de fechas en SAS y en otros sistemas operativos o bases de datos se interpretan y se almacenan como números que representan el número de días (o de segundos, en el caso de los datetime) transcurridos desde una fecha de origen. Las fechas anteriores a ella se almacenan como números negativos.

Lo que puede cambiar entre estos distintos sistemas es esa fecha origen a partir de la que se empieza a contar, de forma que una misma cifra puede representar fechas distintas dependiendo de qué sistema haya generado esa fecha. Esto da lugar a algunos problemas que debemos resolver cuando se realizan migraciones de datos de un sistema a otro. En nuestro caso, como programadores en SAS, debemos tener cuidado al importar fechas generadas con el timestamp de Unix y tener en cuenta que Unix no comienza a contar las fechas desde la misma fecha origen que SAS. Mientras SAS comienza a contar en ’01jan1960′; Unix comienza a contar en ’01jan1970′. Esos 10 años de diferencias hay que tenerlos en cuenta.

Por ello, si tenemos un datetime generado en Unix será necesario aplicar la siguiente corrección para que al convertir el número en fecha SAS éstas sean equivalentes:

fecha_SAS = fecha_Unix + 315619200;

Esa cantidad que sumamos es el número de segundos que representan esos 10 años. Existe otra forma de hacerlo, quizás un poco más elegante, que es utilizando la función dhms():

fecha_SAS = dhms('01jan1970'd, 0, 0, fecha_Unix);

La función dhms() construye un datetime a partir de una fecha y un número de horas, minutos y segundos. Con la instrucción anterior, se generará un datetime sumando el número de segundos expresados en la fecha_Unix a la fecha origen de Unix, que es el ’01jan1970′. Es una forma más elegante y además resulta más fácil recordar la fecha de origen de Unix, que el número tan grande de segundos que representan esa diferencia.
Si las fechas de Unix están representadas en milisegundos solo debemos utilizar una pequeña variante sobre lo anterior:

fecha_SAS = dhms('01jan1970'd, 0, 0, fecha_Unix/1000);

SAS: Operar con fechas: función intnx()

La función intnx() es una de las funciones más útiles, junto con intck(), para el trabajo con fechas y merece por si misma una entrada en el blog. Vamos a empezar viendo su sintaxis básica y su uso:

Sintáxis

intnx(intervalo, fecha, incremento)
Esta función calcula de una forma fácil la fecha que se deriva de sumar cierta cantidad (incremento) de periodos de tiempo (intervalo) a una fecha concreta. Por ejemplo, intnx('month','1jan2020'd,5) da como resultado el 1 de junio de 2020, porque si a partir del 1 de enero sumamos 5 meses es el 1 de junio. Esto es tremendamente útil porque sumar meses considerándolos como 30 días no da una fecha concreta del mes que buscamos si no que varía en función de la cantidad de días que tengan los meses intermedios.

Con respecto a los intervalos que podemos usar están, entre otros: ‘day’, ‘week’, ‘month’, ‘qtr’ (trimestre), ‘year’. La fecha debe incluirse en formato numérico (no vale una cadena de texto) y el incremento es cualquier número entero positivo o negativo.

intnx(‘month’,’1jul2021’d,-7) ‘1jan2021’d
intnx(‘day’,’15jul2021’d,-15) ‘1jul2021’d
intnx(‘week’,’11oct2021’d,-1) ‘4oct2021’d
intnx(‘year’,’1jan2021’d,-1) ‘1jan2020’d
intnx(‘qtr’,’1jan2021’d,-1) ‘1oct2020’d

El alineamiento

Adicionalmente disponemos de un cuarto parámetro; el alineamiento, que indica en qué parte del intervalo temporal que hemos definido queremos situarnos: al principio, a la mitad, al final o el mismo día que la fecha indicada. El alineamiento tomará respectivamente los valores: ‘B’, ‘M’, ‘E’, ‘S’, por sus siglas en inglés. El valor ‘B’ es el valor por defecto si no se indica nada.

intnx(‘year’,’6jul2021’d,0) ‘1jan2021’d Como ‘B’ es el valor por defecto, si no indicamos nada obtendremos el primer día del mismo (0) periodo (year) de la fecha indicada (‘6jul2021’).
intnx(‘year’,’6jul2021’d,0,’E’) ’31dec2021’d Cuando se indica ‘E’ obtendremos el último día del mismo (0) periodo (year) a la fecha indicada (‘6jul2021’).
intnx(‘year’,’6jul2021’d,1,’S’) ‘6jul2022’d Con ‘S’ obtendremos la misma fecha del siguiente periodo.
intnx(‘week’,’6jul2021’d,1,’S’) ’13jul2021’d En este caso, como se indican sumar un periodo de una semana y tenemos el alineamiento nos indica que debe ser el mismo día de la semana (martes), obtendremos la fecha de 7 días después.
intnx(‘week’,’6jul2021’d,1,’E’) ’17jul2021’d Utilizar el alineamiento ‘E’ indica tener que ir al último día de la siguiente semana, el sábado. Las semanas comienzan el domingo.

Con todo esto, ¿cómo obtener los registros correspondientes al mes anterior de una tabla cuyos registros están informados con un campo fecha?
intnx('month',date(),-1,'B') <= fecha <= intnx('month',date(),-1,'E')
Esta condición permite obtener todos los registros del mes anterior al actual de una tabla. Lo mismo sería para obtener la semana anterior o el trimestre anterior.

Multiplicadores de intervalo

Finalmente, existe la posibilidad de añadir multiplicadores a los intervalos. Para ello, se añade simplemente un número detrás del nombre del intervalo. Por ejemplo: intnx('month3',date(),1) sería una forma de indicar la fecha correspondiente al inicio del próximo trimestre y es equivalente a expresarlo como intnx('qtr',date(),1).

intnx(‘month3’,’1jan2021’d,1) ‘1apr2021’d Obtenemos el primer día del trimestre siguiente al actual.
intnx(‘month2’,’1jan2021’d,1) ‘1mar2021’d Utilizando el multiplicador 2 con el mes obtendremos periodos de 2 meses.
intnx(‘month2.2′,’1mar2021’d,0,’B’) ‘1feb2021’d Aquí utilizamos el multiplicador para definir periodos temporales de dos meses, pero lo hemos modificado con un índice «.2» que indica que estos periodos temporales de dos meses comienzan el segundo mes del año. Por tento la fecha de inicio de ese periodo es el 1 de febrero.
intnx(‘month2.2′,’1mar2021’d,0,’E’) ’31mar2021’d Al igual que en el ejemplo anterior, utilizamos el mismo multiplicador y pedimos la última fecha de ese periodo que es el 31 de marzo. ‘month2.1’ sería equivalente a ‘month2’.

Intervalos personalizados

Vistas todas las opciones que nos ofrece predefinidas intnx() aún podemos definir intervalos propios en vez de los conocidos ‘month’ o ‘year’. Pongámonos en el caso de una empresa que realice los cierres mensuales el día 16 de cada mes. Una opción sería utilizar el intervalo predefinido ‘semimonth2.2’.

intnx(‘semimonth2.2′,’1jan2021’d,0,’B’) ’16dec2020’d
intnx(‘semimonth2.2′,’1jan2021’d,0,’E’) ’15jan2021’d

Sin embargo, esta empresa tiene una particularidad, y es que los meses de febrero cierran el día 14, no el 15. Para resolver esto tenemos dos opciones o añadir una lógica condicionada al mes en curso o definir nuestro propio intervalo. Para definir nuestro propio intervalo utilizaremos la instrucción options intervalds a la que se le pasan el nombre del intervalo y el de la tabla que contiene la definición de esos intervalos. Luego definimos esa tabla de la manera en que se muestra y ya podremos utilizar nuestros propios intervalos con la función intnx() y de la misma manera que los predefinidos:

options intervalds=(cierre=dstest);
data dstest;
format begin end date9.;
begin='16jan2021'd; end='14feb2021'd; output;
begin='15feb2021'd; end='15mar2021'd; output;
begin='16mar2021'd; end='15apr2021'd; output;
begin='16apr2021'd; end='15jun2021'd; output;
begin='16jun2021'd; end='15jul2021'd; output;
begin='16jul2021'd; end='15aug2021'd; output;
begin='16aug2021'd; end='15sep2021'd; output;
begin='16sep2021'd; end='15oct2021'd; output;
begin='16oct2021'd; end='15nov2021'd; output;
begin='16nov2021'd; end='15dec2021'd; output;
begin='16dec2021'd; end='15jan2022'd; output;
begin='16jan2022'd; end='14feb2022'd; output;
begin='15feb2022'd; end='15mar2022'd; output;
begin='16mar2022'd; end='15apr2022'd; output;
begin='16apr2022'd; end='15jun2022'd; output;
begin='16jun2022'd; end='15jul2022'd; output;
begin='16jul2022'd; end='15aug2022'd; output;
begin='16aug2022'd; end='15sep2022'd; output;
begin='16sep2022'd; end='15oct2022'd; output;
begin='16oct2022'd; end='15nov2022'd; output;
begin='16nov2022'd; end='15dec2022'd; output;
begin='16dec2022'd; end='15jan2023'd; output;
run;

data salida;
format fecha_inicio fecha_fin date9.;
fecha_inicio=intnx('cierre',date(),0,'B');
fecha_fin=intnx('cierre',date(),0,'E');
run;

SAS: Operaciones con fechas

Trabajar con fechas y con formatos de fechas es una de las cosas a las que más cuesta acostumbrarse a aquellos que comienzan a trabajar con SAS. Aquí quiero recoger un pequeño resumen de las funciones de fecha más habituales en SAS, cómo funcionan y cuales son las diferencias entre ellas. Aquí veremos solo funciones de fecha, no entraré en las funciones fecha-hora.

Lo primero que hay que saber de las fechas en SAS es que, como en muchos otros lenguajes de programación, en realidad se representan internamente por un número y la fecha que vemos es una máscara o formato de representación de ese número. Al ser números se pueden realizar operaciones aritméticas con ellas directamente sin problema, por lo que el día de ayer se puede calcular como date()-1.

La fecha 0 es en SAS el 1 de enero de 1960. En otros lenguajes la fecha 0 puede ser otra. El 31 de diciembre de 1959 tiene el valor -1.

Para expresar una fecha en SAS podemos hacerlo por un número o por la notación '1jan2021'd. La fecha actual se obtiene con date().

  • datedate()
    Da la fecha del sistema, o sea, la fecha de la máquina en la que corre SAS. El resultado, sin aplicar ningún formato, será un número.

    Ejemplo Resultado
    date() 22240
    put(date(),date9.) 21NOV2020
  • datepartdatepart([datetime])
    Convierte un valor tipo fecha-hora en una fecha.

    Ejemplo Resultado
    datepart(datetime()) 22240
    put(datepart('15apr2020:00:00:00'dt),date9.) 15APR2020
  • intnxintnx([intervalo], [fecha], [incremento], [alineamiento])
    La función intnx() calcula la fecha resultante de aplicar un incremento (positivo o negativo) a la «fecha» indicada. El «incremento» representa el número de unidades del tipo definido en el parámetro «intervalo» que hay que añadir a esa fecha. Este «incremento» puede ser positivo o negativo. El «alineamiento» puede tomar los valores ‘B’ inicio del periodo; ‘E’ fin del periodo; ‘M’ medio del periodo y ‘S’ mismo día del periodo, y significa que si vamos a sumar x meses a una fecha si indicamos «B», la fecha resultante será el primer día del mes después de sumar x meses a la fecha dada. Lo vemos mejor en unos ejemplos:

    Ejemplo Resultado
    intnx('month','31aug2020'd,1) 1SEP2020
    intnx('month','31aug2020'd,1,'S') 30SEP2020
    intnx('month','18jan2020'd,-1,'E') 31DEC2019
    intnx('year','5jun1999'd,0,'B') 01JAN1999
    intnx('week','18apr2020'd,1,'S') 25APR2020
    intnx('qtr','18apr2020'd,0,'E') 30JUN2020
  • intckintck([intervalo], [fecha-inicio], [fecha-fin], [método])
    La función intck() calcula la diferencia entre las dos fechas indicándolo en función de la unidad temporal expresada en el parámetro intervalo. El ‘método’ sirve para indicar cómo se va a realizar el cálculo: puede tomar los valores ‘CONTINUOUS’ o ‘C’ para calcular el número de aniversarios sucedidos antes de la fecha-fin, o ‘DISCRETE’ o ‘D’ que toma el tiempo como una variable discreta y cuenta cuantas unidades hay entre las dos fechas proporcionadas. Esto último se ve mejor en el siguiente ejemplo donde se observa que cuando comienza un nuevo periodo temporal natural (en este caso estoy contando años) se incrementa la cuenta que ofrece el método ‘DISCRETE’.

    Ejemplo Resultado
    intck('year','1jul2000'd,'1jan2001'd,'D'); 1
    intck('year','1jul2000'd,'31dec2001'd,'D') 1
    intck('year','1jul2000'd,'1jan2002'd,'D') 2
    intck('year','1jul2000'd,'1jan2002'd,'C') 1
    yrdif('1jul2000'd,'1jan2002'd) 1
  • DAY(), MONTH(), YEAR()
    Estas funciones dan como resultado un número que representa, respectivamente, el día el mes y el año de la fecha que se indica entre paréntesis.

    Ejemplo Resultado
    day(date()) 21
    month('25may2020'd) 5
    year('25may2020'd) 2020
  • WEEK(), WEEKDAY()
    Estas funciones devuelven, respectivamente, el número que ocupa esa fecha dentro de un año (entre 1 y 53); y el número del día de la semana desde 1-domingo hasta 7-sábado.

    Ejemplo Resultado
    week('21nov2020'd) 46
    weekdate('21nov2020'd) /*sábado*/ 7
    weekdate('22nov2020'd) /*domingo*/ 1
  • QTR()QTR([fecha])
    Da como resultado un entero que indica el trimestre del año (de 1 a 4) en que se encuentra la fecha.

    Ejemplo Resultado
    QTR('21nov2020'd) 4
  • YRDIFYRDIF([fecha-inicio],[fecha-fin])
    YRDIF da la diferencia exacta en años entre dos fechas. El resultado es un número con decimales que representa exactamente la diferencia en años. Se puede añadir un tercer parámetro que hace que se consideren años de meses de 30 días y 360 días por año y otras opciones.

    Ejemplo Resultado
    yrdif('1jul2000'd,'1jan2002'd) 1.504109589

Formatos de fecha en SAS

Existen infinidad de formatos de fecha o fecha-hora en SAS, literalmente cientos, en todos los idiomas y en todos los tipos de calendarios. Creo que conocer lo que SAS nos puede ofrecer en cuanto a estos formatos nos puede ayudar mucho. Y además, si esto no es suficiente para ti, aún te puedes crear un formato personalizado.

Vamos a hacer un repaso de los formatos de fecha que me parecen más interesantes. No creo que nadie utilice todos, pero desde luego aquí hay para todas las preferencias. Os los ofrezco clasificados por el uso más natural que me parece que tiene cada formato. Incluyo también algunos formatos de fecha en español.

Formato Resultado Uso
Partes de la fecha
DAY. 19 Día del mes.
WEEKDAY. 1 Día de la semana (1=domingo).
DOWNAME. Sunday Nombre del día de la semana en inglés.
ESPDFDWN. domingo Nombre del día de la semana en español.
MONTH. 4 Número del mes.
MONNAME. April Nombre del mes en inglés.
ESPDFMN. abril Nombre del mes en español.
QTR. 2 Número del trimestre.
YEAR. 2020 Año
Fechas solo con números.
DDMMYY10. 19/04/2020 Compatible con Excel.
DDMMYYB10. 19 04 2020
DDMMYYC10. 19:04:2020
DDMMYYD10. 19-04-2020 Compatible con Excel.
DDMMYYP10. 19.04.2020
YYMMDDS10. 2020/04/19
YYMMDDB10. 2020 04 19
YYMMDDC10. 2020:04:19
YYMMDDD10. 2020-04-19 Compatible con Excel.
YYMMDDP10. 2020.04.19
Fechas con el nombre del mes.
DATE. 19apr20
DATE9. 19apr2020 Formato habitual de trabajo.
ESPDFDE. 20abr2020 Igual al anterior, en español.
Para fechar nombres de fichero o tabla
DDMMYYN10. 19042020 Para fechar (orden invertido).
YYMMDDN10. 20200419 Para fechar ficheros.
YYMMN6. 202004 Para fechar ficheros mensuales.
Fechas para informes
YYWEEKW7. 2020W04 Informes semanales.
YYMMN6. 202004 Informes mensuales.
YYMMS. 2020/04 Informes mensuales.
MMYYS. 04/2020 Informes mensuales.
YYMON. 2020APR Informes mensuales.
MONYY. APR20 Informes mensuales.
YYQ6. 2020Q2 Informes trimestrales
YYS6. 2020S1 Informes semestrales.
Fechas para texto o documentos.
WORDDATE. April 19, 2020 Fecha en texto en inglés.
WORDDATX. 19 April 2020
ESPDFWDX. 30 de enero de 2011 Como los anteriores pero en formato español.
WEEKDATE. Sunday, April 19, 2020 Fecha en texto con día de la semana en inglés.
ESPDFWKX. domingo, 30 de enero de 2011 Como el anterior pero en formato español.

Hay muchísimos más formatos de fecha que se pueden encontrar en la documentación de SAS.