2010-08-31 374 views
10

que tienen dos tramas de datos en R. Un marco tiene un personas año de nacimiento:Calcular las edades en R

YEAR 
/1931 
/1924 

y luego otra columna muestra un tiempo más reciente.

RECENT 
09/08/2005 
11/08/2005 

Lo que quiero hacer es restar los años para que pueda calcular su edad en número de años, sin embargo no estoy seguro de cómo abordar esto. ¿Alguna ayuda, por favor?

+0

relevante: https://stackoverflow.com/questions/31126726/efficient-and-accurate-age-calculation-in-years -months-or-weeks-in-r-given-b? noredirect = 1 & lq = 1 –

Respuesta

2

Usted puede hacer algo de formateo:

as.numeric(format(as.Date("01/01/2010", format="%m/%d/%Y"), format="%Y")) - 1930 

con sus datos:

> yr <- c(1931, 1924) 
> recent <- c("09/08/2005", "11/08/2005") 
> as.numeric(format(as.Date(recent, format="%m/%d/%Y"), format="%Y")) - yr 
[1] 74 81 

Puesto que usted tiene sus datos en un hoja.de.datos (voy a suponer que se llama df), se será más como esto:

as.numeric(format(as.Date(df$recent, format="%m/%d/%Y"), format="%Y")) - df$year 
+0

Funciona para los datos que he publicado aquí, pero mi conjunto de datos en realidad tiene muchas más filas. ¿Hay alguna manera de lograr esto llamando a los marcos de datos? – Brian

+0

En la forma de muestra. Simplemente reemplace recientes y años con sus columnas df. – Shane

1

Basado en la respuesta anterior, convierta sus columnas al objeto de fecha s y restar Algunos de los tipos de conversión entre el carácter y numérico es necesario:

> foo=data.frame(RECENT=c("09/08/2005","11/08/2005"),YEAR=c("/1931","/1924")) 
> foo 
     RECENT YEAR 
1 09/08/2005 /1931 
2 11/08/2005 /1924 
> foo$RECENTd = as.Date(foo$RECENT, format="%m/%d/%Y") 
> foo$YEARn = as.numeric(substr(foo$YEAR,2,999)) 
> foo$AGE = as.numeric(format(foo$RECENTd,"%Y")) - foo$YEARn 
> foo 
     RECENT YEAR RECENTd YEARn AGE 
1 09/08/2005 /1931 2005-09-08 1931 74 
2 11/08/2005 /1924 2005-11-08 1924 81 

Nota He asumido que tiene que roza en su columna de año.

Además, la sugerencia para hacer preguntas sobre fechas es incluir un día que es más allá del duodécimo para que sepamos si usted es una persona de un mes/día/año o una persona de día/mes/año.

+2

¡Usa las clases! 'as.Date()' hace el trabajo por ti prácticamente. – Vince

2

dados los datos en su ejemplo:

> m <- data.frame(YEAR=c("/1931", "/1924"),RECENT=c("09/08/2005","11/08/2005")) 
> m 
    YEAR  RECENT 
1 /1931 09/08/2005 
2 /1924 11/08/2005 

Extracto años con la función strptime:

> strptime(m[,2], format = "%m/%d/%Y")$year - strptime(m[,1], format = "/%Y")$year 
[1] 74 81 
+0

¿Por qué? La belleza de la programación orientada a objetos es tener métodos que reconocen objetos de fecha para que no tenga que hacer esto. – Vince

+2

¿Por qué no? Esto resuelve el problema con solo una conversión. – eyjo

8

Puede resolver esto con el paquete lubridate.

> library(lubridate) 

No creo/1931 es una clase de fecha común. Así que supondré que todas las entradas son cadenas de caracteres.

> RECENT <- data.frame(recent = c("09/08/2005", "11/08/2005")) 
> YEAR <- data.frame(year = c("/1931", "/1924")) 

En primer lugar, notifiquemos a R que las fechas recientes son fechas. Asumiré que las fechas están en orden de mes/día/año, entonces uso mdy(). Si están en orden día/mes/año simplemente use dmy().

> RECENT$recent <- mdy(RECENT$recent) 
     recent 
1 2005-09-08 
2 2005-11-08 

Ahora, convirtamos los años en números para que podamos hacer algunos cálculos con ellos.

> YEAR$year <- as.numeric(substr(YEAR$year, 2, 5)) 

Ahora haz las cuentas. year() extrae el valor anual de las fechas RECIENTES.

> year(RECENT$recent) - YEAR 
    year 
1 74 
2 81 

p.s.Si las entradas de año son fechas realmente lleno, se puede obtener la diferencia en años con

> YEAR1 <- data.frame(year = mdy("01/08/1931","01/08/1924")) 
> as.period(RECENT$recent - YEAR1$year, units = "year") 
[1] 74 years and 8 months 81 years and 10 months 
+0

Siguiendo su ejemplo, obtengo 'días' en lugar de' años' –

17

La siguiente función toma unos vectores de objetos Date y calcula las edades, correcta contabilización de los años bisiestos. Parece ser una solución más simple que cualquiera de las otras respuestas.

age = function(from, to) { 
    from_lt = as.POSIXlt(from) 
    to_lt = as.POSIXlt(to) 

    age = to_lt$year - from_lt$year 

    ifelse(to_lt$mon < from_lt$mon | 
     (to_lt$mon == from_lt$mon & to_lt$mday < from_lt$mday), 
     age - 1, age) 
} 
+1

Limpio, rápido y utiliza solo funciones básicas. También maneja los años bisiestos correctamente. Debería ser la respuesta mejor votada. – nograpes

+0

Para [evitar 'ifelse'] (http://stackoverflow.com/questions/16275149/does-ifelse-really-calculate-both-of-its-vectors-every-time-is-it-slow):' out <- entero (longitud (año)); out [idx <- to_lt ​​$ mon MichaelChirico

1

Creo que esto podría ser un poco más intuitivo y no requiere formateo o maltratar:

as.numeric(as.Date("2002-02-02") - as.Date("1924-08-03"))/365 

da salida:

77.55342 

continuación, puede utilizar piso(), redondo (), o techo() para redondear a un número entero.

+0

Esto no tiene en cuenta los años bisiestos. – nograpes

+0

Podría hacer 365.25, que debería estar lo suficientemente cerca. Si está buscando edades, ¿la edad real (número de días) no es más importante que la edad del calendario? –

+1

A veces, el número real de días vividos está perfectamente bien (y tal vez mejor), pero en otras situaciones realmente desea el número de años calendario que han transcurrido. Aunque dos personas de 65 años (según la definición común) pueden haber vivido un número diferente de días, a menudo no queremos hacer esa distinción. Por ejemplo, si estaba calculando si alguien era elegible para la jubilación, casi todo el mundo usa años enteros en lugar de días para hacer ese cálculo. – nograpes

0

manera muy sólida que también es compatible con vectores utilizando el paquete lubridate:

age <- function(date.birth, date.ref = Sys.Date()) { 
    if (length(date.birth) > 1 & length(date.ref) == 1) { 
    date.ref <- rep(date.ref, length(date.birth)) 
    } 

    date.birth.monthdays <- paste0(month(date.birth), day(date.birth)) %>% as.integer() 
    date.ref.monthdays <- paste0(month(date.ref), day(date.ref)) %>% as.integer() 

    age.calc <- 0 

    for (i in 1:length(date.birth)) { 
    if (date.birth.monthdays[i] <= date.ref.monthdays[i]) { 
     # didn't had birthday 
     age.calc[i] <- year(date.ref[i]) - year(date.birth[i]) 
    } else { 
     age.calc[i] <- year(date.ref[i]) - year(date.birth[i]) - 1 
    } 
    } 
    age.calc 
} 

Esto también da cuenta de los años bisiestos. Solo compruebo si alguien ya ha cumplido años.

1

Uso una función personalizada, vea el código a continuación, conveniente de usar en mutate y bastante flexible (necesitará el paquete lubridate).

Ejemplos

get_age("2000-01-01") 
# [1] 17 
get_age(lubridate::as_date("2000-01-01")) 
# [1] 17 
get_age("2000-01-01","2015-06-15") 
# [1] 15 
get_age("2000-01-01",dec = TRUE) 
# [1] 17.92175 
get_age(c("2000-01-01","2003-04-12")) 
# [1] 17 14 
get_age(c("2000-01-01","2003-04-12"),dec = TRUE) 
# [1] 17.92176 14.64231 

función

#' Get age 
#' 
#' Returns age, decimal or not, from single value or vector of strings 
#' or dates, compared to a reference date defaulting to now. Note that 
#' default is NOT the rounded value of decimal age. 
#' @param from_date vector or single value of dates or characters 
#' @param to_date date when age is to be computed 
#' @param dec return decimal age or not 
#' @examples 
#' get_age("2000-01-01") 
#' get_age(lubridate::as_date("2000-01-01")) 
#' get_age("2000-01-01","2015-06-15") 
#' get_age("2000-01-01",dec = TRUE) 
#' get_age(c("2000-01-01","2003-04-12")) 
#' get_age(c("2000-01-01","2003-04-12"),dec = TRUE) 
get_age <- function(from_date,to_date = lubridate::now(),dec = FALSE){ 
    if(is.character(from_date)) from_date <- lubridate::as_date(from_date) 
    if(is.character(to_date)) to_date <- lubridate::as_date(to_date) 
    if (dec) { age <- lubridate::interval(start = from_date, end = to_date)/(lubridate::days(365)+lubridate::hours(6)) 
    } else { age <- lubridate::year(lubridate::as.period(lubridate::interval(start = from_date, end = to_date)))} 
    age 
} 
+0

Esa es una buena función. Pero, ¿por qué decidiste usar '/ dyears (1)' para 'if (dec)'? ¿La edad no es más como un período y requeriría '/ años (1)'? – Tjebo

+1

Fue un error, pero su sugerencia tampoco funciona, la cambié por '(lubridate :: days (365) + lubridate :: hours (6))' –

+0

interesante para combinar 'days()' y ' horas() '. ¿puedo amablemente como por qué elegiste 'horas (6)'? Ah, uy, creo que lo tengo. Girándolo en decimales. Sin embargo, esto representaría años bisiestos? – Tjebo

Cuestiones relacionadas