Jak agregować dane minutowe z tygodnia na średnie godziny?

15

Jak uzyskałbyś godzinowe środki dla wielu kolumn danych dla okresu dziennego i pokazałbyś wyniki dla dwunastu „hostów” na tym samym wykresie? To znaczy, chciałbym wykreślić, jak wygląda okres 24 godzin, dla danych wartych tygodni. Ostatecznym celem byłoby porównanie dwóch zestawów tych danych, przed i po próbkowaniu.

            dates         Host CPUIOWait CPUUser CPUSys
1 2011-02-11 23:55:12     db       0      14      8
2 2011-02-11 23:55:10     app1     0       6      1
3 2011-02-11 23:55:09     app2     0       4      1

Udało mi się uruchomić xyplot (CPUUser ~ date | Host) z dobrym skutkiem. Jednak zamiast pokazywać każdą datę w tygodniu, chciałbym, aby oś X była godzinami dnia.

Próba przeniesienia tych danych do obiektu xts powoduje błędy, takie jak „order.by wymaga odpowiedniego obiektu opartego na czasie”

Oto str () ramki danych:

'data.frame':   19720 obs. of  5 variables:
$ dates    : POSIXct, format: "2011-02-11 23:55:12" "2011-02-11 23:55:10" ...
$ Host     : Factor w/ 14 levels "app1","app2",..: 9 7 5 4 3 10 6 8 2 1 ...  
$ CPUIOWait: int  0 0 0 0 0 0 0 0 0 0 ...
$ CPUUser  : int  14 6 4 4 3 10 4 3 4 4 ...
$ CPUSys   : int  8 1 1 1 1 3 1 1 1 1 ...

AKTUALIZACJA: Na wszelki wypadek postanowiłem wybrać wykres pudełkowy, aby pokazać zarówno medianę, jak i „wartości odstające”.

Głównie:

Data$hour <- as.POSIXlt(dates)$hour  # extract hour of the day
boxplot(Data$CPUUser ~ Data$hour)    # for a subset with one host or for all hosts
xyplot(Data$CPUUser ~ Data$hour | Data$Host, panel=panel.bwplot, horizontal=FALSE)

Dzięki

Scott Hoffman
źródło
Zgaduję, że otrzymujesz te błędy, xts()ponieważ dateskolumna jest czynnikiem.
Joshua Ulrich,
Jestem naprawdę nowy w R ... Utworzyłem kolumnę dat z funkcji strptime. Oryginalne dane pochodzą z read.csv.
Scott Hoffman,
1
Zobaczmy str()data.frame.
Roman Luštrik
@Roman Dzięki za funkcję str (), nie byłem tego świadomy. Tak więc, pozbywając się kolumny Factor, mogę wygenerować taki obiekt xts, x <-xts (d [, 3: 5], order.by = d [, 1]). Byłem wtedy w stanie złożyć wniosek. Hour, co skraca dane z 19720 obiektów do 480. Nie jestem pewien, czy to doprowadzi mnie tam, gdzie chcę, ale myślę, że jestem teraz bliżej.
Scott Hoffman

Odpowiedzi:

14

Oto jedno podejście wykorzystujące cut () do tworzenia odpowiednich współczynników godzinowych i ddply () z biblioteki plyr do obliczania średnich.

library(lattice)
library(plyr)

## Create a record and some random data for every 5 seconds 
## over two days for two hosts.
dates <- seq(as.POSIXct("2011-01-01 00:00:00", tz = "GMT"),
             as.POSIXct("2011-01-02 23:59:55", tz = "GMT"),
             by = 5)
hosts <- c(rep("host1", length(dates)), rep("host2", 
           length(dates)))
x1    <- sample(0:20, 2*length(dates), replace = TRUE)
x2    <- rpois(2*length(dates), 2)
Data  <- data.frame(dates = dates, hosts = hosts, x1 = x1, 
                    x2 = x2)

## Calculate the mean for every hour using cut() to define 
## the factors and ddply() to calculate the means. 
## getmeans() is applied for each unique combination of the
## hosts and hour factors.
getmeans  <- function(Df) c(x1 = mean(Df$x1), 
                            x2 = mean(Df$x2))
Data$hour <- cut(Data$dates, breaks = "hour")
Means <- ddply(Data, .(hosts, hour), getmeans)
Means$hour <- as.POSIXct(Means$hour, tz = "GMT")

## A plot for each host.
xyplot(x1 ~ hour | hosts, data = Means, type = "o",
       scales = list(x = list(relation = "free", rot = 90)))
Jason Morgan
źródło
Dzięki za to ... Myślę, że może będę musiał przeformułować pytanie lub zadać nowe. Patrząc na to pytanie stats.stackexchange.com/questions/980/… , myślę, że teraz uzyskanie środków nie jest dokładnie tym, czego szukam.
Scott Hoffman,
@JVM Czy możesz wyjaśnić, jak działa funkcja getmeans i dlaczego nie użyłeś po prostu funkcji mean lub colMeans?
Scott Hoffman,
1
Funkcja ddply () tnie oryginalny zestaw danych na podzbiory zdefiniowane przez hosty i godzinę. Następnie przekazuje je do getmeans () jako data.frame. W twoim zadaniu użycie colMeans () prawdopodobnie działałoby dobrze, ale prawdopodobnie musiałbyś najpierw usunąć niepotrzebne kolumny. Zaletą używania ddply () w ten sposób jest to, że możesz obliczyć dowolne statystyki, którymi możesz być zainteresowany; np. sd (), range () itp.
Jason Morgan
6

Agregacja działa również bez użycia zoo(z losowymi danymi z 2 zmiennych przez 3 dni i 4 hostami jak z JWM). Zakładam, że masz dane ze wszystkich hostów dla każdej godziny.

nHosts <- 4  # number of hosts
dates  <- seq(as.POSIXct("2011-01-01 00:00:00"),
              as.POSIXct("2011-01-03 23:59:30"), by=30)
hosts  <- factor(sample(1:nHosts, length(dates), replace=TRUE),
                 labels=paste("host", 1:nHosts, sep=""))
x1     <- sample(0:20, length(dates), replace=TRUE)  # data from 1st variable
x2     <- rpois(length(dates), 2)                    # data from 2nd variable
Data   <- data.frame(dates=dates, hosts=hosts, x1=x1, x2=x2)

Nie jestem do końca pewien, czy chcesz uśredniać w ciągu każdej godziny, czy w ciągu każdej godziny przez wszystkie dni. Zrobię oba.

Data$hFac <- droplevels(cut(Data$dates, breaks="hour"))
Data$hour <- as.POSIXlt(dates)$hour  # extract hour of the day

# average both variables over days within each hour and host
# formula notation was introduced in R 2.12.0 I think
res1 <- aggregate(cbind(x1, x2) ~ hour + hosts, data=Data, FUN=mean)
# only average both variables within each hour and host
res2 <- aggregate(cbind(x1, x2) ~ hFac + hosts, data=Data, FUN=mean)

Wynik wygląda następująco:

> head(res1)
  hour hosts        x1       x2
1    0 host1  9.578431 2.049020
2    1 host1 10.200000 2.200000
3    2 host1 10.423077 2.153846
4    3 host1 10.241758 1.879121
5    4 host1  8.574713 2.011494
6    5 host1  9.670588 2.070588

> head(res2)
                 hFac hosts        x1       x2
1 2011-01-01 00:00:00 host1  9.192308 2.307692
2 2011-01-01 01:00:00 host1 10.677419 2.064516
3 2011-01-01 02:00:00 host1 11.041667 1.875000
4 2011-01-01 03:00:00 host1 10.448276 1.965517
5 2011-01-01 04:00:00 host1  8.555556 2.074074
6 2011-01-01 05:00:00 host1  8.809524 2.095238

Nie jestem też do końca pewien, jakiego rodzaju wykres chcesz. Oto wersja wykresu bez kości dla pierwszej zmiennej z osobnymi liniami danych dla każdego hosta.

# using the data that is averaged over days as well
res1L <- split(subset(res1, select="x1"), res1$hosts)
mat1  <- do.call(cbind, res1L)
colnames(mat1) <- levels(hosts)
rownames(mat1) <- 0:23
matplot(mat1, main="x1 per hour, avg. over days", xaxt="n", type="o", pch=16, lty=1)
axis(side=1, at=seq(0, 23, by=2))
legend(x="topleft", legend=colnames(mat1), col=1:nHosts, lty=1)

Ten sam wykres dla danych, który jest uśredniany tylko w ciągu każdej godziny.

res2L <- split(subset(res2, select="x1"), res2$hosts)
mat2  <- do.call(cbind, res2L)
colnames(mat2) <- levels(hosts)
rownames(mat2) <- levels(Data$hFac)
matplot(mat2, main="x1 per hour", type="o", pch=16, lty=1)
legend(x="topleft", legend=colnames(mat2), col=1:nHosts, lty=1)
karakal
źródło
Miła odpowiedź, dużo tam nie jestem zaznajomiony, więc muszę to wypróbować. Mimo to, patrząc na moje dane za pomocą twoich metod, myślę, że muszę również pokazać najwyższe punkty w moich danych. Dzięki
Scott Hoffman,
2

Możesz pobrać aggregate.zoofunkcję z pakietu zoo: http://cran.r-project.org/web/packages/zoo/zoo.pdf

Charlie

Charlie
źródło
Czy możesz mi pomóc zrozumieć, dlaczego otrzymuję NA podczas uruchamiania tego?
Scott Hoffman
Cześć Scott, tak naprawdę nie użyłem tej aggregate.zoofunkcji, chociaż użyłem zoopakietu. Czy upewniłeś się, że Twój obiekt był zoonajpierw obiektem? Dokumentacja, na którą wskazałem, powinna ci w tym pomóc.
Charlie