Die effiziente und genaue Alter Berechnung (in Jahren, Monaten oder Wochen) in R gegeben, Geburtsdatum und einem beliebigen Datum
Ich bin mit der gemeinsamen Aufgabe, die Berechnung des Alters (in Jahren, Monaten oder Wochen) gegeben, das Geburtsdatum und ein willkürliches Datum. Die Sache ist die, dass sehr oft habe ich zu tun das über viele viele Datensätze (>300 Millionen), also performance ist ein zentrales Thema hier.
Nachdem eine schnelle Suche in SO und Google fand ich 3 alternativen:
- Eine gemeinsame arithmetische Verfahren (/365.25) (link)
- Verwendung von Funktionen
new_interval()
undduration()
aus Paketlubridate
(link) - Funktion
age_calc()
aus Paketeeptools
(link, link, link)
So, hier ist mein Spielzeug-code:
# Some toy birthdates
birthdate <- as.Date(c("1978-12-30", "1978-12-31", "1979-01-01",
"1962-12-30", "1962-12-31", "1963-01-01",
"2000-06-16", "2000-06-17", "2000-06-18",
"2007-03-18", "2007-03-19", "2007-03-20",
"1968-02-29", "1968-02-29", "1968-02-29"))
# Given dates to calculate the age
givendate <- as.Date(c("2015-12-31", "2015-12-31", "2015-12-31",
"2015-12-31", "2015-12-31", "2015-12-31",
"2050-06-17", "2050-06-17", "2050-06-17",
"2008-03-19", "2008-03-19", "2008-03-19",
"2015-02-28", "2015-03-01", "2015-03-02"))
# Using a common arithmetic procedure ("Time differences in days"/365.25)
(givendate-birthdate)/365.25
# Use the package lubridate
require(lubridate)
new_interval(start = birthdate, end = givendate) /
duration(num = 1, units = "years")
# Use the package eeptools
library(eeptools)
age_calc(dob = birthdate, enddate = givendate, units = "years")
Reden wir später über die Genauigkeit und den Fokus zuerst auf die Leistung. Hier ist der code:
# Now let's compare the performance of the alternatives using microbenchmark
library(microbenchmark)
mbm <- microbenchmark(
arithmetic = (givendate - birthdate) / 365.25,
lubridate = new_interval(start = birthdate, end = givendate) /
duration(num = 1, units = "years"),
eeptools = age_calc(dob = birthdate, enddate = givendate,
units = "years"),
times = 1000
)
# And examine the results
mbm
autoplot(mbm)
Hier die Ergebnisse:
Fazit: die Leistung der lubridate
und eeptools
Funktionen ist viel schlimmer als das arithmetische Methode (/365.25 ist mindestens 10 mal schneller). Leider ist die arithmetische Methode ist nicht genau genug, und ich kann nicht leisten, die paar Fehler, die diese Methode machen.
"weil der Weg zu den modernen Gregorianischen Kalender
ist konstruiert, es ist keine einfache arithmetische
Methode, erzeugt ein person Alter, angegeben nach
gemeinsame Nutzung gemeinsame Nutzung bedeutet, dass eine person, die
Alter sollte immer eine ganze Zahl sein, die sich erhöht, genau auf
ein Geburtstag". (link)
Als ich Las einige Beiträge, die lubridate
und eeptools
machen keine solchen Fehler (obwohl ich noch nicht angeschaut, der code/Lesen Sie mehr über die Funktionen wissen, welche Methode Sie verwenden) und das ist, warum ich wollte, Sie zu benutzen, aber Ihre Leistung nicht für meine eigentlichen Anwendung.
Irgendwelche Ideen auf eine effiziente und genaue Methode zur Berechnung des Alters?
BEARBEITEN
Ops, es scheint lubridate
auch Fehler macht. Und offenbar basiert auf diesem Beispiel Spielzeug, es macht mehr Fehler als der arithmetische Methode (siehe Zeilen 3, 6, 9, 12). (mache ich etwas falsch?)
toy_df <- data.frame(
birthdate = birthdate,
givendate = givendate,
arithmetic = as.numeric((givendate - birthdate) / 365.25),
lubridate = new_interval(start = birthdate, end = givendate) /
duration(num = 1, units = "years"),
eeptools = age_calc(dob = birthdate, enddate = givendate,
units = "years")
)
toy_df[, 3:5] <- floor(toy_df[, 3:5])
toy_df
birthdate givendate arithmetic lubridate eeptools
1 1978-12-30 2015-12-31 37 37 37
2 1978-12-31 2015-12-31 36 37 37
3 1979-01-01 2015-12-31 36 37 36
4 1962-12-30 2015-12-31 53 53 53
5 1962-12-31 2015-12-31 52 53 53
6 1963-01-01 2015-12-31 52 53 52
7 2000-06-16 2050-06-17 50 50 50
8 2000-06-17 2050-06-17 49 50 50
9 2000-06-18 2050-06-17 49 50 49
10 2007-03-18 2008-03-19 1 1 1
11 2007-03-19 2008-03-19 1 1 1
12 2007-03-20 2008-03-19 0 1 0
13 1968-02-29 2015-02-28 46 47 46
14 1968-02-29 2015-03-01 47 47 47
15 1968-02-29 2015-03-02 47 47 47
lubridate
. Mein einziger Vorschlag, wenn Sie realllly müssen die Leistungssteigerung ist die arithmetische Methode zuerst, und dann wiederholen alle den "close calls" mit den lubridate Methode (so, zum Beispiel, wenn abs(floor(age) - age) < 0.01)
dann verwenden lubridate)Danke. sind Sie und erfahrene Benutzer von
lubridate
?, ..., als ich es in der Frage bearbeitet, ich fand, dass es Fehler macht (vielleicht mehr als die arithmetische Methode) aber ich habe gelesen in mehreren posts, dass lubridate
war in der Tat eine der R-Pakete, die in der Lage war, genau zu berechnen Altersgruppen. So, jetzt Frage ich mich, wenn ich mache etwas falsch. (Ich denke nicht, ich bin im Grunde nach den Beispielen und es ist ziemlich einfach, aber nur double-checking)Keine Lösung, aber
difftime(givendate, birthdate) / 365.25
zu sein scheint rund 5% schneller als (givendate - birthdate) / 365.25)
. Könnte nützlich sein im Fall, dass Sie am Ende mit der Arithmetik.guter Aufruf!
-.Date
nur Anrufe difftime
mehr robust. Noch schneller sollte (unclass(givendate) - unclass(birthdate)) / 365.25
da diese weiter springt der Aufwand difftime
.InformationsquelleAutor Hernando Casas | 2015-06-29
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ok, so fand ich diese Funktion in einem anderen post:
Es wurde geschrieben von @Jim sagte: "Die folgende Funktion nimmt einen Vektoren der Date-Objekte und berechnet das Alter, richtig Buchhaltung für die Schaltjahre. Scheint eine einfachere Lösung als jede der anderen Antworten".
Ist es zwar einfacher und es tut den trick, den ich suchte. Im Durchschnitt ist er tatsächlich schneller als die arithmetische Methode (über 75% schneller).
Und zumindest in meinem Beispiel, macht keine Fehler (und es sollte nicht in jedem Beispiel; es ist eine ziemlich einfache Funktion mit
ifelse
s).Ich halte es nicht als eine vollständige Lösung, weil ich wollte auch mit dem Alter in Monaten und Wochen, und diese Funktion ist spezifisch für die Jahre. Ich poste es hier trotzdem, weil es das problem löst, für das Alter in Jahren. Ich werde nicht akzeptieren, weil:
InformationsquelleAutor Hernando Casas
Den Grund lubridate zu sein scheint, die Fehler machen, die oben ist, dass Sie die Berechnung der Dauer (die genaue Höhe der Zeit, das Auftritt, zwischen zwei Augenblicken, wo 1 Jahr = 31536000s), sondern als Zeiten (die änderung in der Zeit, die Auftritt, zwischen zwei Augenblicken).
Um die änderung in der Zeit (in Jahren, Monaten, Tagen, etc) Sie verwenden müssen
welche gibt die folgende Ausgabe
Nur extrahieren Jahre, können Sie die folgenden
Beachten Sie, dass dies werfen die folgende Warnmeldung (nicht sicher, warum):
und leider scheint auch langsamer als die oben genannten Methoden!
new_interval()
ist jetzt abgeschrieben in derlubridate
- Paket, sointerval()
statt. Auch die "nicht sinnvoll für Faktoren" Warnmeldung (die ausas.period()
behoben worden ist, so mehr erscheint.InformationsquelleAutor JWilliman
Ich wurde hämmern an dieser und schließlich haben etwas, das a) perfekt genaue* (im Gegensatz zu alle der anderen Optionen so weit) und b) Recht schnell (siehe meine benchmarks in der anderen Antwort). Es stützt sich auf eine Reihe von arithmetischen habe ich von hand und die wunderbare
foverlaps
Funktion aus derdata.table
Paket.Die Essenz des Ansatzes ist die Verwendung der integer-Repräsentation der
Date
s, sowie zu erkennen, dass alle Geburtsdaten fallen in eine von vier 1461 (= 365 * 4 + 1)-Tag Zyklen, je nachdem, Wann das Nächstes Jahr ist, wenn es dauert 366 Tage für Ihre Geburtstag, zu kommen.Hier ist die Funktion:
Vergleich auf Ihre Haupt-Beispiel:
Diese Art von Ansatz kann erweitert werden, zu handhaben Monaten/Wochen ziemlich leicht. Monaten wird ein bisschen langatmig (angeben 4 Jahre im Wert von Monats-Längen), so dass ich nicht stören; Wochen ist einfach (Wochen unberührt Schaltjahr überlegungen, so können wir nur Division durch 7).
Ich auch viele Fortschritte gemacht, auf diese Weise mit
base
Funktionalitäten, aber a) war es ganz hässlich (muss eine nicht-lineare transformation 0-1460 vermeiden Sie verschachtelteifelse
Aussagen, etc.) und b) das Ende einer for-Schleife (in form vonapply
über die gesamte Liste von Daten) unumgänglich war, also beschloss ich, das würde die Dinge verlangsamen viel. (die transformation istx1 = (unclass(birthdays) - 59) %% 1461; x2 = x1 * (729 - x1) /402232 + x1
für die Nachwelt)Die ich Hinzugefügt habe, diese Funktion zu mein Paket.
*(für Termine reicht, wenn nicht-Sprung Jahrhunderten nicht von Belang; ich glaube, dass die Erweiterung um solche Daten sollte nicht allzu aufwändig, jedoch)
InformationsquelleAutor MichaelChirico
Ich im Begriff war, zu verlassen, dies in den Kommentaren, aber ich denke, es ist würdig eines separaten Antwort. @Molx Punkte, die "arithmetische Methode" ist nicht so einfach wie es scheint-werfen Sie einen Blick auf den code für
-.Date
was am wichtigsten ist:So, der "arithmetische Methode", die auf Objekte der Klasse
Date
ist wirklich ein wrapper für diedifftime
Funktion. Wasdifftime
? Auch dies hat eine Reihe von overhead, wenn das, was man nach der raw-Geschwindigkeit.Der Schlüssel ist, dass
Date
Objekte gespeichert sind als integer-Zahl der Tage seit/bis Jan. 1, 1970 (obwohl Sie eigentlich nicht so gespeichert, wie Sieinteger
, daher die Geburt derIDate
Klassedata.table
), so können wir nur subtrahieren diese und mit ihm getan werden, aber zu vermeiden, die-.Date
- Methode aufgerufen wird, müssen wirunclass
unsere Eingänge:So weit wie bang für Ihre buck geht, dieser Ansatz ist noch mehrere Größenordnungen schneller als auch @Jim
age
Methode.Hier sind einige mehr skaliert-up test data:
(ohne
eeptools
denn es ist fast unmöglich, langsamer--ein Blick auf den code fürage_calc
schlägt der code geht so weit, erstellen Sie eine Abfolge von Terminen für jedes paar von Daten (O(n^2)
-ish), nicht zu vergessen würzen vonifelse
s)Damit betonen wir auch die Torheit des benchmarking auf der small-scale-Daten.
Den großen Preis von @Jim Methode ist, daß
as.POSIXlt
ist immer teurer als Ihre Vektoren wachsen.Das Problem der Ungenauigkeit bleibt, aber es sei denn, diese Genauigkeit ist von größter Bedeutung, es scheint, die
unclass
Methode ist beispiellos.InformationsquelleAutor MichaelChirico