2010-08-31 73 views
10

Tôi có hai khung dữ liệu trong R. Một khung có một người năm sinh:Tính lứa tuổi trong R

YEAR 
/1931 
/1924 

và sau đó cột khác cho thấy một thời gian gần đây.

RECENT 
09/08/2005 
11/08/2005 

Điều tôi muốn làm là trừ năm để tôi có thể tính tuổi của họ trong nhiều năm, tuy nhiên tôi không biết cách tiếp cận điều này. Bất kỳ giúp đỡ xin vui lòng?

+0

liên quan: https://stackoverflow.com/questions/31126726/efficient-and-accurate-age-calculation-in-years -months-hoặc-week-in-r-given-b? noredirect = 1 & lq = 1 –

Trả lời

2

Bạn có thể làm một số các định dạng:

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

Với dữ liệu của bạn:

> 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 

Kể từ khi bạn có dữ liệu của bạn trong một data.frame (Tôi sẽ cho rằng nó được gọi là df), nó sẽ giống như thế này:

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

Hoạt động với dữ liệu tôi đã đăng ở đây, nhưng tập dữ liệu của tôi thực sự có nhiều hàng hơn. Có cách nào tôi có thể thực hiện điều này bằng cách tự gọi các khung dữ liệu? – Brian

+0

Trong cách lấy mẫu. Chỉ cần thay thế gần đây và năm với các cột df của bạn. – Shane

1

Dựa trên câu trả lời trước, chuyển đổi cột của bạn thành đối tượng ngày s và trừ. Một số chuyển đổi các loại giữa ký tự và số là cần thiết:

> 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 

Lưu ý: Tôi giả định bạn có dấu gạch chéo trong cột năm của bạn.

Ngoài ra, mẹo khi đặt câu hỏi về ngày là bao gồm ngày quá mười hai để chúng tôi biết bạn là người tháng/ngày/năm hay người ngày/tháng/năm.

+2

Sử dụng các lớp học! 'as.Date()' thực hiện công việc cho bạn một cách thực tế. – Vince

2

Với dữ liệu trong ví dụ của bạn:

> 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 

Extract năm với strptime chức năng:

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

Tại sao? Vẻ đẹp của lập trình hướng đối tượng là có các phương thức nhận biết các đối tượng ngày tháng, do đó bạn không phải làm điều này. – Vince

+2

Tại sao không? Điều này giải quyết vấn đề chỉ với một chuyển đổi. – eyjo

8

Bạn có thể giải quyết điều này với các gói lubridate.

> library(lubridate) 

Tôi không nghĩ/1931 là lớp ngày chung. Vì vậy, tôi sẽ giả định tất cả các mục là chuỗi ký tự.

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

Trước tiên, hãy thông báo cho R rằng ngày gần đây là ngày. Tôi sẽ giả định ngày tháng theo thứ tự tháng/ngày/năm, vì vậy tôi sử dụng mdy(). Nếu họ đặt hàng ngày/tháng/năm, chỉ cần sử dụng dmy().

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

Bây giờ, hãy biến năm thành số để chúng tôi có thể thực hiện một số phép tính với chúng.

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

Bây giờ, chỉ cần thực hiện phép tính. year() chiết xuất giá trị năm của ngày RECENT.

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

p.s.nếu mục năm của bạn đang thực sự đầy đủ ngày, bạn có thể nhận được sự khác biệt trong năm với

> 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

Theo ví dụ của bạn, tôi nhận được 'ngày' thay vì' năm' –

17

Hàm ở phía dưới có một vectơ của các đối tượng ngày và tính toán các thời đại, chiếm một cách chính xác cho năm nhuận. Dường như là một giải pháp đơn giản hơn bất kỳ câu trả lời nào khác.

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

Chỉ sử dụng các hàm cơ bản. Cũng xử lý năm nhuận đúng cách. Nên là câu trả lời được bình chọn hàng đầu. – nograpes

+0

Để [tránh 'ifelse'] (http://stackoverflow.com/questions/16275149/does-ifelse-really-calculate-both-of-its-vectors-every-time-is-it-slow): 'ra <- số nguyên (chiều dài (năm)); ra [idx <- to_lt ​​$ mon MichaelChirico

1

Tôi nghĩ rằng đây có thể là một chút trực quan hơn và không cần định dạng hoặc tước:

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

cho đầu ra:

77.55342 

Sau đó, bạn có thể sử dụng sàn(), tròn (), hoặc trần() để làm tròn thành một số nguyên.

+0

Điều này không tính đến năm nhuận. – nograpes

+0

Bạn có thể làm 365,25, đủ gần. Nếu bạn đang tìm kiếm lứa tuổi, không phải là thực tế (số ngày) tuổi quan trọng hơn tuổi lịch? –

+1

Đôi khi, số ngày thực tế sống là hoàn toàn tốt (và có lẽ tốt hơn), nhưng trong các tình huống khác, bạn thực sự muốn số năm dương lịch đã trôi qua. Mặc dù hai người 65 tuổi (theo định nghĩa chung) có thể đã sống một số ngày khác nhau, chúng tôi thường không muốn làm nên sự khác biệt đó. Ví dụ, nếu bạn tính toán nếu một người nào đó đủ điều kiện để nghỉ hưu, gần như mọi người đều sử dụng cả năm chứ không phải là ngày để tính toán đó. – nograpes

0

cách thực sự vững chắc mà còn hỗ trợ vectơ sử dụng gói 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 
} 

này cũng chiếm tới năm nhuận. Tôi chỉ kiểm tra xem ai đó đã có một sinh nhật chưa.

1

Tôi sử dụng chức năng tùy chỉnh, xem mã bên dưới, thuận tiện để sử dụng trong biến đổi và khá linh hoạt (bạn cần gói lubridate).

Ví dụ

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 

Chức năng

#' 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

Đó là một chức năng tuyệt vời. Nhưng, tại sao bạn quyết định sử dụng '/ dyears (1)' cho 'if (dec)'? Không phải là tuổi hơn một cái gì đó giống như một khoảng thời gian và sẽ yêu cầu '/ năm (1)'? – Tjebo

+1

Đó là một sai lầm, nhưng đề xuất của bạn không thực sự hiệu quả, tôi đã thay đổi nó cho '(lubridate :: days (365) + lubridate :: hours (6))' –

+0

thú vị để kết hợp 'days()' và ' giờ() '. tôi có thể vui lòng vì sao bạn chọn 'giờ (6)'? Ah, oops, nghĩ rằng tôi đã nhận nó. Biến nó thành số thập phân. Liệu tài khoản này có cho năm nhuận không? – Tjebo

Các vấn đề liên quan