2015-01-03 9 views
6

Poszukuję pomocy po zmarnowaniu prawie dnia. Mam dużą ramkę danych (bdf) i małą ramkę danych (sdf). Chcę dodać zmienną z do bdf w zależności od wartości sdf $ (która zmienia się w zależności od zmiennej czasowej).Tworzenie zmiennej w ramce danych R w zależności od innej ramki danych

Oto powtarzalne przykład:

bdf <- data.frame(tb = seq(as.POSIXct("2013-05-19 17:11:22 GMT", tz="GMT"), by=5624*24, length.out=10)) 

bdf 
       tb 
1 2013-05-19 17:11:22 
2 2013-05-21 06:40:58 
3 2013-05-22 20:10:34 
4 2013-05-24 09:40:10 
5 2013-05-25 23:09:46 
6 2013-05-27 12:39:22 
7 2013-05-29 02:08:58 
8 2013-05-30 15:38:34 
9 2013-06-01 05:08:10 
10 2013-06-02 18:37:46 


sdf <- data.frame(ts = as.POSIXct(c("2013-05-22", "2013-05-25", "2013-05-30"), tz="GMT"), y = c(0.2, -0.1, 0.3)) 

> sdf 
     ts y 
1 2013-05-22 0.2 
2 2013-05-25 -0.1 
3 2013-05-30 0.3 

Chcę utworzyć zmienną oo w bdf z następującymi wartościami SDF $ y:

  • 0,2 wiersze, gdzie BDF $ tb waha się od pierwsza wartość bdf $ tb do połowy między pierwszą a drugą wartością sdf $ ts. W tym prostym przykładzie, tak jest w przypadku wierszy od 1 do 3 dbf, które mają czasy bdf $ tb poniżej "2013-05-23 12:00:00 GMT".

  • -0,1 dla wierszy, gdzie bdf $ tb waha się od połowy między 1 a 2 wartością sdf $ ts do połowy między 2 a 3 wartością sdf $ ts. W tym prostym przykładzie, tak jest w przypadku wierszy 4 i 5 dbf, które mają czasy bdf $ tb między "2013-05-23 12:00:00 GMT" a "2013-05-27 12:00:00 GMT" .

  • 0.3 dla wszystkich wierszy, gdzie bdf $ tb waha się od połowy między 2 a 3 wartością sdf $ ts do ostatniej wartości bdf $ tb. W tym prostym przykładzie, tak jest w przypadku wierszy od 1 do 6 do 10 dbf, które mają czasy większe niż "2013-05-23 12:00:00 GMT".

Stąd, w końcu duża dataframe BDF powinna wyglądać następująco:

    tb z 
1 2013-05-19 17:11:22 0.2 
2 2013-05-21 06:40:58 0.2 
3 2013-05-22 20:10:34 0.2 
4 2013-05-24 09:40:10 -0.1 
5 2013-05-25 23:09:46 -0.1 
6 2013-05-27 12:39:22 0.3 
7 2013-05-29 02:08:58 0.3 
8 2013-05-30 15:38:34 0.3 
9 2013-06-01 05:08:10 0.3 
10 2013-06-02 18:37:46 0.3 

nie mogłem osiągnąć sukces przy użyciu dplyr :: mutować, ale nigdzie stosując pętle ... Każda pomoc będzie bardzo doceniane. Mam nadzieję, że jasno opisałem tę kwestię jako przestrzegającą etykiety (to moje pierwsze pytanie).

+1

brzmi jak _join do najbliższej wartości_ W pakiecie 'data.table' może' roll = "najbliższy" 'ale nie mam w tym żadnego doświadczenia i jestem ciekawy, czy jest to możliwe w' dplyr' – ckluss

Odpowiedz

3

To wydaje się absolutnie konieczne, ale w bazie R

bdf$z <- numeric(nrow(bdf)) 
for(i in seq_along(bdf$z)){ 
    ind <- which.min(abs(bdf$tb[i] - sdf$ts)) 
    bdf$z[i] <- sdf$y[ind] 
} 

Będąc trochę niezdarny, ma przewagę w jasności, która może pomieścić łatwą adaptację do dplyr

library(dplyr) 
bdf %>% rowwise() %>% 
    mutate(z= sdf$y[which.min(abs(as.numeric(tb)-as.numeric(sdf$ts)))]) 

#Source: local data frame [10 x 2] 
#Groups: <by row> 

#     tb z 
#1 2013-05-19 17:11:22 0.2 
#2 2013-05-21 06:40:58 0.2 
#3 2013-05-22 20:10:34 0.2 
#4 2013-05-24 09:40:10 -0.1 
#5 2013-05-25 23:09:46 -0.1 
#6 2013-05-27 12:39:22 0.3 
#7 2013-05-29 02:08:58 0.3 
#8 2013-05-30 15:38:34 0.3 
#9 2013-06-01 05:08:10 0.3 
#10 2013-06-02 18:37:46 0.3 
+0

Druga opcja to moja ulubiona. Jest to najprostsze rozwiązanie, ponieważ nie wymaga żadnego dodatkowego pakietu i jest bardzo krótkie. – gattuso

+0

Źle zrozumiał system głosowania i chcesz zagłosować na tę odpowiedź – gattuso

3

Oto moje podejście:

library(zoo) 
m <- c(rollmean(as.POSIXct(sdf$ts), 2), Inf) 
transform(bdf, z = sdf$y[sapply(tb, function(x) which.max(x < m))]) 
#     tb z 
#1 2013-05-19 17:11:22 0.2 
#2 2013-05-21 06:40:58 0.2 
#3 2013-05-22 20:10:34 0.2 
#4 2013-05-24 09:40:10 -0.1 
#5 2013-05-25 23:09:46 -0.1 
#6 2013-05-27 12:39:22 0.3 
#7 2013-05-29 02:08:58 0.3 
#8 2013-05-30 15:38:34 0.3 
#9 2013-06-01 05:08:10 0.3 
#10 2013-06-02 18:37:46 0.3 

Aktualizacja: usunięto konwersja do numerycznej (nie wymagane)

Krótkie wyjaśnienie:

  • as.POSIXct(sdf$ts) konwertuje daty do POSIXct stylu Date-razy
  • rollmean(as.POSIXct(sdf$ts), 2) oblicza średnią kroczącą z dwóch kolejnych rzędów. Tak się składa, że ​​dokładnie jest to czas, który chcesz wykorzystać do oddzielenia obserwacji. rollmean jest z pakietu zoo. Obliczanie wartości rollmean(..,2) oznacza, że ​​wektor wyjściowy jest skracany o 1 w porównaniu do wektora wejściowego.
  • Dlatego właśnie zawijam wynik rollmean w c(.., Inf), co oznacza, że ​​wartość nieskończoności jest dodawana do wektora obrotu jako ostatnia wartość. To zapewni, że ostatnie pozycje z w sdf również zostaną zwrócone (0,3 w konkretnym przykładzie).
  • używam transform dodać kolumnę z do bdf
  • sapply(tb, function(x) which.max(x < m)) pętli poprzez wpisy w bdf$tb i dla każdego wpisu, oblicza maksymalny wskaźnik dla których bdf$tb jest mniejsza (wcześniej) niż m (który przechowuje wektor rollmean wpisami).Dla każdego wpisu bdf$tb zwracany jest tylko maksymalny (najnowszy) indeks.
  • To wektor indeksów jest używany w sdf$y[sapply(tb, function(x) which.max(x < m))] wyodrębnić odpowiednie elementy sdf$y które następnie zostaną zapisane/skopiowane do nowego z kolumny bdf

nadzieję, że pomoże

+0

Użycie 'rollmean' jest całkiem sprytne. Może być użyty do zapełnienia wektora 'findInterval' w mojej metodzie i z boku problemów, które miałem z' difftime'. –

3

Edycja Uwaga: I początkowo uzyskać nieco inny wynik niż to, co teraz uważam, było związane z moim brakiem zrozumienia obiektów R obiektów R. Obszary czasowe w obiektach POSIXt również pozostają tajemnicą dla mnie, ale teraz widzę, że kiedy wymusiłem na obiekcie "numer", że mam wartość w "dniach".

Funkcja findInterval jest bardzo użyteczna jako funkcja tworzenia indeksu, która odwzorowuje wektor wartości, w którym jeden ma wiele sąsiednich niepokrywających się interwałów. Naprawdę masz tylko dwa punkty czasowe, które dzielą się na trzy interwały.

bdf$z <- c(0.2,-0.1,0.3)[findInterval(bdf$tb, 
       c(-Inf, 
    sdf$ts[2] - 0.5*as.numeric(difftime(sdf$ts[2], sdf$ts[1], units="secs")), 
    sdf$ts[3] - 0.5*as.numeric(difftime(sdf$ts[3], sdf$ts[2],units="sec")), 
       Inf))] 

> bdf 
        tb z 
1 2013-05-19 17:11:22 0.2 
2 2013-05-21 06:40:58 0.2 
3 2013-05-22 20:10:34 0.2 
4 2013-05-24 09:40:10 -0.1 
5 2013-05-25 23:09:46 -0.1 
6 2013-05-27 12:39:22 0.3 
7 2013-05-29 02:08:58 0.3 
8 2013-05-30 15:38:34 0.3 
9 2013-06-01 05:08:10 0.3 
10 2013-06-02 18:37:46 0.3 

ja również sprawdzane, aby zobaczyć czy mój wynik będzie mieć wpływ, czy przerwy w findIntervals były zamknięte na ich prawo zamiast w lewo (domyślnie) i nie widzi różnicy.

6

Oto rozwiązanie przy użyciu data.table „s toczenia dołącza:

require(data.table) 
setkey(setDT(sdf), ts) 
sdf[bdf, roll = "nearest"] 
#      ts y 
# 1: 2013-05-19 17:11:22 0.2 
# 2: 2013-05-21 06:40:58 0.2 
# 3: 2013-05-22 20:10:34 0.2 
# 4: 2013-05-24 09:40:10 -0.1 
# 5: 2013-05-25 23:09:46 -0.1 
# 6: 2013-05-27 12:39:22 0.3 
# 7: 2013-05-29 02:08:58 0.3 
# 8: 2013-05-30 15:38:34 0.3 
# 9: 2013-06-01 05:08:10 0.3 
# 10: 2013-06-02 18:37:46 0.3 
  • setDT konwertuje data.frame do data.table przez odniesienie.

  • setkey sortuje data.table poprzez odniesienie w porządku rosnącym według dostępnych kolumn, a znaki te kolumny jak kluczowych kolumn (tak, że możemy dołączyć do tych kolumn klucza później.

  • W data.table, x[i] wykonuje dołączyć kiedy i jest data.table. ja odsyłam do this answer nadrobić data.table sprzężeń, jeśli nie jesteś już zaznajomiony.

  • x[i] Perfo rms an equi-join. Oznacza to, że znajduje pasujące indeksy wierszy w x dla każdego wiersza w i, a następnie wyodrębnia te wiersze z x, aby zwrócić wynik sprzężenia wraz z odpowiednim wierszem z i. W przypadku, gdy wiersz w pozycji i nie znajdzie zgodnych indeksów wierszy w x, ten wiersz domyślnie będzie miał NA dla x.

    Jednak x[i, roll = .] wykonuje walcowanie łączenia. Jeśli nie ma zgodności, ostatnia obserwacja jest przenoszona do przodu (roll = TRUE lub -Inf) lub następna obserwacja może być przeniesiona do tyłu (roll = Inf) lub zwinięta do najbliższej wartości (roll = "nearest"). I w tym przypadku potrzebujesz roll = "nearest" IIUC.

HTH

Powiązane problemy