Ich habe zwei Termine, sagen wir mal 14.01.2013und 26.03.2014.
Ich möchte die Differenz zwischen diesen beiden Daten in Wochen (?), Monaten (im Beispiel 14), Quartalen (4) und Jahren (1) ermitteln.
Kennen Sie den besten Weg, um dies zu bekommen?
Ich habe zwei Termine, sagen wir mal 14.01.2013und 26.03.2014.
Ich möchte die Differenz zwischen diesen beiden Daten in Wochen (?), Monaten (im Beispiel 14), Quartalen (4) und Jahren (1) ermitteln.
Kennen Sie den besten Weg, um dies zu bekommen?
Antworten:
was ist damit:
# 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()und as.yearqtr()sind im Paket zoo. year()ist im Paket lubridate. Was denken Sie?
year in Kalenderjahren einen Unterschied. Wenn Sie also den Unterschied in einem Bruchteil eines Jahres kennen müssen, ist er nicht geeignet.
Alle vorhandenen Antworten sind unvollständig (IMO) und treffen entweder Annahmen über die gewünschte Ausgabe oder bieten keine Flexibilität für die gewünschte Ausgabe.
Basierend auf den Beispielen aus dem OP und den angegebenen erwarteten Antworten des OP denke ich, dass dies die Antworten sind, nach denen Sie suchen (plus einige zusätzliche Beispiele, die die Extrapolation erleichtern).
(Dies erfordert nur Basis R und erfordert keinen Zoo oder Schmiermittel)
In Datetime-Objekte konvertieren
date_strings = c("14.01.2013", "26.03.2014")
datetimes = strptime(date_strings, format = "%d.%m.%Y") # convert to datetime objects
Unterschied in Tagen
Sie können den Diff in Tagen verwenden, um einige unserer späteren Antworten zu erhalten
diff_in_days = difftime(datetimes[2], datetimes[1], units = "days") # days
diff_in_days
#Time difference of 435.9583 days
Unterschied in Wochen
Der Unterschied in Wochen ist ein Sonderfall von units = "weeks"indifftime()
diff_in_weeks = difftime(datetimes[2], datetimes[1], units = "weeks") # weeks
diff_in_weeks
#Time difference of 62.27976 weeks
Beachten Sie, dass dies dem Teilen unserer diff_in_days durch 7 (7 Tage in einer Woche) entspricht.
as.double(diff_in_days)/7
#[1] 62.27976
Unterschied in Jahren
Mit ähnlicher Logik können wir Jahre aus diff_in_days ableiten
diff_in_years = as.double(diff_in_days)/365 # absolute years
diff_in_years
#[1] 1.194406
Sie scheinen zu erwarten, dass der Unterschied in Jahren "1" ist, also gehe ich davon aus, dass Sie nur absolute Kalenderjahre oder etwas anderes zählen möchten, was Sie einfach mit verwenden können floor()
# get desired output, given your definition of 'years'
floor(diff_in_years)
#[1] 1
Unterschied in Quartalen
# get desired output for quarters, given your definition of 'quarters'
floor(diff_in_years * 4)
#[1] 4
Unterschied in Monaten
Kann dies als Umrechnung von diff_years berechnen
# 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
Ich weiß, dass diese Frage alt ist, aber da ich dieses Problem gerade noch lösen musste, dachte ich, ich würde meine Antworten hinzufügen. Ich hoffe es hilft.
months_diff<0 ist.
date_strings = c("14.07.2014", "10.03.2015")Geben Sie -4stattdessen 7 Monate gemäß der ersten Definition.
diff_in_years, z. B. in Ihrem Beispiel ist die wahre Antwort, dass fast 8 volle Monate vergangen sind. Sie erhalten die richtige Antwort, indem diff_in_years*12 = 7.857534ich meine Antwort korrigiere - danke.
365Jahre teilen , um Jahre zu erhalten, gilt dies aufgrund von Schaltjahren nur für 3 von 4 Jahren. Die Division durch 365.25wäre genauer, insbesondere für die Berechnung eines Alters.
Wochenlang können Sie folgende Funktionen verwenden 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
Funktioniert aber difftimenicht mit Dauer über Wochen.
Das Folgende ist eine sehr suboptimale Lösung cut.POSIXtfür diese Zeiträume, aber Sie können sie umgehen:
seq1 <- seq(date1,date2, by="days")
nlevels(cut(seq1,"months"))
15
nlevels(cut(seq1,"quarters"))
5
nlevels(cut(seq1,"years"))
2
Dies ist jedoch die Anzahl der Monate, Quartale oder Jahre, die sich über Ihr Zeitintervall erstrecken, und nicht die Dauer Ihres Zeitintervalls, ausgedrückt in Monaten, Quartalen, Jahren (da diese keine konstante Dauer haben). In Anbetracht des Kommentars, den Sie zu @SvenHohenstein abgegeben haben, würde ich denken, dass Sie ihn nlevels(cut(seq1,"months")) - 1für das verwenden können, was Sie erreichen möchten .
Ich habe das gerade für eine andere Frage geschrieben und bin dann hierher gestolpert.
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)
}
Anwendungsbeispiele:
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) { ... }und nur, returnwenn Sie etwas zur Hälfte Ihrer Funktion zurückgeben. Die letzte Zeile sollte einfach sein calc.age.
returnmit meinen Funktionen - ich finde es klarer. Verwenden Sie in Ihrem eigenen Code natürlich den Stil, der zu Ihnen passt.
1950-01-17und 2015-01-01. Es kehrt zurück 65, aber diese Person würde nicht vor dem 17.01.2015 65 Jahre alt werden ... Irgendeine Idee warum?
yy = seq.Date(from = as.Date("2010-01-01"), to = as.Date("2015-01-01"), by = "year")und dann versuchen age(dob = as.Date("1950-01-17"), age.day = yy), springt das Ergebnis über 62. Und nur wenn das Geburtsdatum zwischen 1949 und 1952 liegt. Sehr seltsam ...
Hier ist eine Lösung:
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
Termine sind hier ein großes Thema und die gegebenen Antworten sind großartig. Hier die noch ausstehende lubridateAntwort (obwohl die Funktion von @Gregor dieses Paket verwendet)
Die Dokumentation zur Schmierzeitspanne ist sehr hilfreich, um den Unterschied zwischen Zeiträumen und Dauer zu verstehen. Ich mag auch das Lubridate Cheatsheet und diesen sehr nützlichen Thread
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"
Perioden akzeptieren keine Wochen als Einheiten. Sie können die Dauer jedoch in Wochen umrechnen :
as.duration(span)/ dweeks(1)
#makes duration object (in seconds) and divides by duration of a week (in seconds)
#> [1] 62.28571
Erstellt am 04.11.2019 durch das reprex-Paket (v0.3.0)
Versuchen Sie dies für eine monatelange Lösung
StartDate <- strptime("14 January 2013", "%d %B %Y")
EventDates <- strptime(c("26 March 2014"), "%d %B %Y")
difftime(EventDates, StartDate)
%mnumerische Monate (z. B. 1 für Januar) anstelle von zu verwenden %B.
Eine "genauere" Berechnung. Das heißt, die Anzahl der Woche / Monat / Quartal / Jahr für eine nicht vollständige Woche / Monat / Quartal / Jahr ist der Bruchteil der Kalendertage in dieser Woche / Monat / Quartal / Jahr. Beispielsweise beträgt die Anzahl der Monate zwischen dem 22.02.2016 und dem 31.03.2016 8/29 + 31/31 = 1,27586
Erklärung inline mit Code
#' 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
Bitte lassen Sie mich wissen, wenn Sie weitere Grenzfälle finden, in denen die oben genannte Lösung nicht funktioniert.