Ich habe ein Datenformat von Zeitstempeln, die einen kategorischen Status angeben. Der Status ist bis zum nächsten Zeitstempel gültig. Zu diesem Zeitpunkt kann sich die Kategorie ändern.Zeitreihen von kategorischen Daten - wie prozentualer Anteil jeder Kategorie über Zeitspannen berechnet werden kann?
Ich würde gern in der Lage sein, den prozentualen Anteil der Zeit zu bestimmen, die in jeder Kategorie über regelmäßige Zeiträume verbracht wird, wie monatlich, vierteljährlich oder jährlich.
Dies scheint wie ein allgemein genug Problem, aber ich konnte nicht eine elegante Lösung oder Bibliothek finden, um es zu lösen.
Zum Beispiel mit dem folgenden Beispieldatenrahmen:
date status
2016-02-20 09:11:00 a
2016-03-06 02:38:00 c
2016-03-10 15:20:00 b
2016-03-10 21:20:00 a
2016-03-11 11:51:00 b
2016-03-12 01:19:00 c
2016-03-22 14:39:00 c
2016-03-23 11:37:00 b
2016-03-25 17:38:00 c
2016-03-26 01:24:00 c
2016-03-26 12:40:00 a
2016-04-12 10:28:00 c
... Ich möchte vielleicht wöchentlich berichten von 3/1-3/7, 3/8-3/14, 3/15- 3/21, die prozentuale Zeit in jeder Woche von 'a', 'b' und 'c' Status.
Ich begann brachiale Kraft eine Lösung zu kodieren (es ist hässlich ...), als ich entschied, vielleicht sollte ich hier fragen, ob es eine elegantere Art ist, es zu tun.
======== Herausgegeben eine unelegant Brute-Force-Lösung unter ========
time_analysis <- function(df, starttime, endtime) {
# - assumes sorted by date
startindex <- sum(df$date <= starttime) # find the index of the entry which contains the start time
endindex <- sum(df$date <= endtime) + 1 # find the index of the entry which contains the end time
if ((startindex == 0) || (endindex > nrow(df))) {
print("Date outside of available data")
return(NULL)
}
df2 <- df[ startindex:endindex, ] # subset the dataframe to include the range, but still need to trim ends
df2$date[1] <- starttime # trim to the start time
df2$date[nrow(df2)] <- endtime # trim back the end time
df2$status[nrow(df2)] <- df2$status[nrow(df2)-1] # status hasn't changed yet, so still the previous status
duration <- diff(df2$date) # vector of the time within each segment, 1 fewer elements than the dataframe
units(duration) <- 'days'
duration <- as.numeric(duration) # need to convert to numeric, or else can't divide by total duration
df2 <- df2[ -nrow(df2), ] # remove the last row, to make length same as the duration vector
df2$duration <- duration # add the duration column
total <- sum(df2$duration) # to allow calculations within the ddply
return(ddply(df2[, c('status','duration')], 'status', function(x) { # calculate by each status category
return(c(
date = starttime,
totaldays = round(sum(x$duration), 2),
fraction = round(sum(x$duration)/total, 3)))
}))
}
Und unten wäre eine Probe Verwendung hinzuzufügen, das würde die Berichterstattung in etwa zwei Wochen teilen. Ich hasse die Verwendung der manuellen Datumscodierung und die Verwendung einer Schleife in R, bin aber zu unerfahren, um einen besseren Weg zu kennen.
times <- c("2016-03-01","2016-03-15","2016-04-01","2016-04-15","2016-05-01","2016-05-15")
result <- data.frame()
for (i in 1:(length(times) - 1)) {
result <- rbind(result, time_analysis(d, times[i], times[i+1]))
}
print(result, row.names = FALSE)
Nachgeben (ausgenommen einige Fehler für Termine außerhalb des Bereichs):
status date totaldays fraction
a 2016-03-01 5.71 0.409
b 2016-03-01 0.81 0.058
c 2016-03-01 7.43 0.532
a 2016-03-15 5.47 0.322
b 2016-03-15 2.25 0.132
c 2016-03-15 9.28 0.546
===== Und nach der Einlieferung, fand eine viel schönere Art und Weise, die Zeiten zu generieren:
times <- as.character(seq(as.Date("2016-03-01"), as.Date("2016-05-15"), by = '2 weeks'))
Es würde helfen, wenn Sie die gewünschte Ausgabe für Ihre Beispieleingabe geben, damit die Antworten verifiziert werden können. Es scheint, als ob Sie einfach das 'diff()' Ihrer Datums-/Zeitspalte nehmen und das mit dem entsprechenden Status aggregieren könnten (den letzten Status ignorierend, für den Sie keine Endzeit haben). Es ist wichtig anzugeben, wie Sie Ihre Intervallpausen auswählen und was Sie für Zeiten tun möchten, die diese Intervalle umfassen. – MrFlick
Etwas wie 'do.call (rbind, tapply (df $ status, Monate (df $ date), Funktion (x) {prop.table (Tabelle (x)) * 100}))', vielleicht – alistaire
@alistaire Das wird nicht funktionieren, wenn der Datensatz mehrere Jahre umfasst, da der gleiche Monat in verschiedenen Jahren zusammen aggregiert werden würde. Der Aufruf 'monates()' kann durch 'format()' ersetzt werden, um sowohl das Jahr als auch den Monat, z.B. 'format (df $ datum, '% Y-% m')'. – bgoldst