У мене є дві дати, скажемо 14.01.2013
і 26.03.2014
.
Я хотів би отримати різницю між цими двома датами з точки зору тижнів (?), Місяців (у прикладі 14), кварталів (4) та років (1).
Чи знаєте ви найкращий спосіб отримати це?
У мене є дві дати, скажемо 14.01.2013
і 26.03.2014
.
Я хотів би отримати різницю між цими двома датами з точки зору тижнів (?), Місяців (у прикладі 14), кварталів (4) та років (1).
Чи знаєте ви найкращий спосіб отримати це?
Відповіді:
як що до цього:
# get difference between dates `"01.12.2013"` and `"31.12.2013"`
# weeks
difftime(strptime("26.03.2014", format = "%d.%m.%Y"),
strptime("14.01.2013", format = "%d.%m.%Y"),units="weeks")
Time difference of 62.28571 weeks
# months
(as.yearmon(strptime("26.03.2014", format = "%d.%m.%Y"))-
as.yearmon(strptime("14.01.2013", format = "%d.%m.%Y")))*12
[1] 14
# quarters
(as.yearqtr(strptime("26.03.2014", format = "%d.%m.%Y"))-
as.yearqtr(strptime("14.01.2013", format = "%d.%m.%Y")))*4
[1] 4
# years
year(strptime("26.03.2014", format = "%d.%m.%Y"))-
year(strptime("14.01.2013", format = "%d.%m.%Y"))
[1] 1
as.yearmon()
і as.yearqtr()
є в упаковці zoo
. year()
є в упаковці lubridate
. Як ти гадаєш?
year
це дасть різницю лише в календарних роках , тому, якщо вам потрібно знати різницю за деяку частку року, це не буде придатним.
Усі існуючі відповіді є недосконалими (IMO) і або роблять припущення щодо бажаного результату, або не забезпечують гнучкості для бажаного результату.
На основі прикладів з OP та заявлених очікуваних відповідей OP, я думаю, це саме ті відповіді, які ви шукаєте (плюс деякі додаткові приклади, які полегшують екстраполяцію).
(Для цього потрібна лише база R і не потрібен зоопарк або мастило)
Перетворити на об'єкти Datetime
date_strings = c("14.01.2013", "26.03.2014")
datetimes = strptime(date_strings, format = "%d.%m.%Y") # convert to datetime objects
Різниця в днях
Ви можете використовувати різницю за кілька днів, щоб отримати деякі з наших пізніших відповідей
diff_in_days = difftime(datetimes[2], datetimes[1], units = "days") # days
diff_in_days
#Time difference of 435.9583 days
Різниця в тижнях
Різниця в тижнях - це окремий випадок units = "weeks"
вdifftime()
diff_in_weeks = difftime(datetimes[2], datetimes[1], units = "weeks") # weeks
diff_in_weeks
#Time difference of 62.27976 weeks
Зверніть увагу, що це те саме, що розділити наші diff_in_days на 7 (7 днів на тиждень)
as.double(diff_in_days)/7
#[1] 62.27976
Різниця в роках
Подібною логікою ми можемо виводити роки з diff_in_days
diff_in_years = as.double(diff_in_days)/365 # absolute years
diff_in_years
#[1] 1.194406
Ви, мабуть, очікуєте, що різниця в роках буде "1", тому я припускаю, що ви просто хочете порахувати абсолютні календарні роки або щось інше, що ви можете легко зробити за допомогою floor()
# get desired output, given your definition of 'years'
floor(diff_in_years)
#[1] 1
Різниця в кварталах
# get desired output for quarters, given your definition of 'quarters'
floor(diff_in_years * 4)
#[1] 4
Різниця в місяцях
Можна обчислити це як конвертацію з diff_years
# months, defined as absolute calendar months (this might be what you want, given your question details)
months_diff = diff_in_years*12
floor(month_diff)
#[1] 14
Я знаю, що це питання застаріле, але враховуючи, що мені все-таки довелося вирішити цю проблему, я думав, що додам свої відповіді. Сподіваюся, це допоможе.
months_diff
<0
date_strings = c("14.07.2014", "10.03.2015")
дати -4
замість цього 7 місяців згідно з першим визначенням ..
diff_in_years
, наприклад, у вашому прикладі, правдивою відповіддю є те, що минуло майже 8 повних місяців. Ви отримуєте правильну відповідь, просто коли diff_in_years*12 = 7.857534
я виправив свою відповідь - дякую.
365
на одержання років, це стосується лише 3 з 4 років через високосний рік. Поділ на 365.25
було б більш точним, особливо для обчислення віку.
Тижнями ви можете використовувати функцію difftime
:
date1 <- strptime("14.01.2013", format="%d.%m.%Y")
date2 <- strptime("26.03.2014", format="%d.%m.%Y")
difftime(date2,date1,units="weeks")
Time difference of 62.28571 weeks
Але difftime
не працює з тривалістю протягом тижнів.
Нижче наведено дуже неоптимальне рішення, яке використовується cut.POSIXt
для цих тривалістей, але ви можете обійти це:
seq1 <- seq(date1,date2, by="days")
nlevels(cut(seq1,"months"))
15
nlevels(cut(seq1,"quarters"))
5
nlevels(cut(seq1,"years"))
2
Однак це кількість місяців, кварталів або років, що охоплюються вашим часовим інтервалом, а не тривалість вашого часового інтервалу, виражена в місяцях, кварталах, роках (оскільки вони не мають постійної тривалості). Беручи до уваги коментар, який ви зробили щодо відповіді @SvenHohenstein, я вважаю, що ви можете використати nlevels(cut(seq1,"months")) - 1
те, чого ви намагаєтесь досягти.
Я просто написав це для іншого питання, а потім наткнувся тут.
library(lubridate)
#' Calculate age
#'
#' By default, calculates the typical "age in years", with a
#' \code{floor} applied so that you are, e.g., 5 years old from
#' 5th birthday through the day before your 6th birthday. Set
#' \code{floor = FALSE} to return decimal ages, and change \code{units}
#' for units other than years.
#' @param dob date-of-birth, the day to start calculating age.
#' @param age.day the date on which age is to be calculated.
#' @param units unit to measure age in. Defaults to \code{"years"}. Passed to \link{\code{duration}}.
#' @param floor boolean for whether or not to floor the result. Defaults to \code{TRUE}.
#' @return Age in \code{units}. Will be an integer if \code{floor = TRUE}.
#' @examples
#' my.dob <- as.Date('1983-10-20')
#' age(my.dob)
#' age(my.dob, units = "minutes")
#' age(my.dob, floor = FALSE)
age <- function(dob, age.day = today(), units = "years", floor = TRUE) {
calc.age = interval(dob, age.day) / duration(num = 1, units = units)
if (floor) return(as.integer(floor(calc.age)))
return(calc.age)
}
Приклади використання:
my.dob <- as.Date('1983-10-20')
age(my.dob)
# [1] 31
age(my.dob, floor = FALSE)
# [1] 31.15616
age(my.dob, units = "minutes")
# [1] 16375680
age(seq(my.dob, length.out = 6, by = "years"))
# [1] 31 30 29 28 27 26
if (floor) { ... }
і використовуйте лише return
тоді, коли ви повертаєте щось на половині своєї функції. Останній рядок повинен просто бути calc.age
.
return
зі своїми функціями - я вважаю це зрозумілішим. Звичайно, у власному коді використовуйте стиль, який вам підходить.
1950-01-17
і 2015-01-01
. Повертається 65
, але цій людині не виповнилося б 65 років до 17.01.2015 ... Будь-яка ідея, чому?
yy = seq.Date(from = as.Date("2010-01-01"), to = as.Date("2015-01-01"), by = "year")
а потім спробуєте age(dob = as.Date("1950-01-17"), age.day = yy)
, результат перескакує понад 62. І лише в тому випадку, якщо DOB знаходиться між 1949 і 1952 роками. Дуже дивно ...
Ось рішення:
dates <- c("14.01.2013", "26.03.2014")
# Date format:
dates2 <- strptime(dates, format = "%d.%m.%Y")
dif <- diff(as.numeric(dates2)) # difference in seconds
dif/(60 * 60 * 24 * 7) # weeks
[1] 62.28571
dif/(60 * 60 * 24 * 30) # months
[1] 14.53333
dif/(60 * 60 * 24 * 30 * 3) # quartes
[1] 4.844444
dif/(60 * 60 * 24 * 365) # years
[1] 1.194521
Тут ще бракує lubridate
відповіді (хоча функція Грегора побудована на цьому пакеті)
Документація про часовий інтервал змащення дуже корисна для розуміння різниці між періодами та тривалістю. Мені також подобається масляний шпаргалка і ця дуже корисна тема
library(lubridate)
dates <- c(dmy('14.01.2013'), dmy('26.03.2014'))
span <- dates[1] %--% dates[2] #creating an interval object
#creating period objects
as.period(span, unit = 'year')
#> [1] "1y 2m 12d 0H 0M 0S"
as.period(span, unit = 'month')
#> [1] "14m 12d 0H 0M 0S"
as.period(span, unit = 'day')
#> [1] "436d 0H 0M 0S"
Періоди не приймають тижні як одиниці. Але ви можете перевести тривалість у тижні:
as.duration(span)/ dweeks(1)
#makes duration object (in seconds) and divides by duration of a week (in seconds)
#> [1] 62.28571
Створено 04.11.2019 пакетом reprex (v0.3.0)
спробуйте це протягом місяця рішення
StartDate <- strptime("14 January 2013", "%d %B %Y")
EventDates <- strptime(c("26 March 2014"), "%d %B %Y")
difftime(EventDates, StartDate)
%m
числові місяці (наприклад, 1 січня) замість %B
.
Більш "точний" розрахунок. Тобто число тижня / місяця / кварталу / року для неповного тижня / місяця / кварталу / року є часткою календарних днів у цьому тижні / місяці / кварталі / році. Наприклад, кількість місяців між 22.02.2016 та 31.03.2016 дорівнює 8/29 + 31/31 = 1,27586
пояснення в рядку з кодом
#' Calculate precise number of periods between 2 dates
#'
#' @details The number of week/month/quarter/year for a non-complete week/month/quarter/year
#' is the fraction of calendar days in that week/month/quarter/year.
#' For example, the number of months between 2016-02-22 and 2016-03-31
#' is 8/29 + 31/31 = 1.27586
#'
#' @param startdate start Date of the interval
#' @param enddate end Date of the interval
#' @param period character. It must be one of 'day', 'week', 'month', 'quarter' and 'year'
#'
#' @examples
#' identical(numPeriods(as.Date("2016-02-15"), as.Date("2016-03-31"), "month"), 15/29 + 1)
#' identical(numPeriods(as.Date("2016-02-15"), as.Date("2016-03-31"), "quarter"), (15 + 31)/(31 + 29 + 31))
#' identical(numPeriods(as.Date("2016-02-15"), as.Date("2016-03-31"), "year"), (15 + 31)/366)
#'
#' @return exact number of periods between
#'
numPeriods <- function(startdate, enddate, period) {
numdays <- as.numeric(enddate - startdate) + 1
if (grepl("day", period, ignore.case=TRUE)) {
return(numdays)
} else if (grepl("week", period, ignore.case=TRUE)) {
return(numdays / 7)
}
#create a sequence of dates between start and end dates
effDaysinBins <- cut(seq(startdate, enddate, by="1 day"), period)
#use the earliest start date of the previous bins and create a breaks of periodic dates with
#user's period interval
intervals <- seq(from=as.Date(min(levels(effDaysinBins)), "%Y-%m-%d"),
by=paste("1",period),
length.out=length(levels(effDaysinBins))+1)
#create a sequence of dates between the earliest interval date and last date of the interval
#that contains the enddate
allDays <- seq(from=intervals[1],
to=intervals[intervals > enddate][1] - 1,
by="1 day")
#bin all days in the whole period using previous breaks
allDaysInBins <- cut(allDays, intervals)
#calculate ratio of effective days to all days in whole period
sum( tabulate(effDaysinBins) / tabulate(allDaysInBins) )
} #numPeriods
Будь ласка, повідомте мене, якщо ви знайдете більше граничних випадків, коли вищевказане рішення не працює.