2015-07-21 20 views
77

PytanieWybierz pierwszy i ostatni wiersz z danych zgrupowanych

Korzystanie dplyr, jak mam wybrać górne i dolne obserwacje/wiersze danych zgrupowanych w jednym stwierdzeniem?

danych & Przykład

względu ramka danych

df <- data.frame(id=c(1,1,1,2,2,2,3,3,3), 
       stopId=c("a","b","c","a","b","c","a","b","c"), 
       stopSequence=c(1,2,3,3,1,4,3,1,2)) 

można uzyskać górną i obserwacje dolnej z każdej z grup, stosując slice, ale za pomocą dwóch oddzielnych statments:

firstStop <- df %>% 
    group_by(id) %>% 
    arrange(stopSequence) %>% 
    slice(1) %>% 
    ungroup 

lastStop <- df %>% 
    group_by(id) %>% 
    arrange(stopSequence) %>% 
    slice(n()) %>% 
    ungroup 

Czy mogę połączyć te dwa statmenety w jeden, który wybiera zarówno obserwacje górne i dolne?

Odpowiedz

126

Jest prawdopodobnie szybszy sposób:

df %>% 
    group_by(id) %>% 
    arrange(stopSequence) %>% 
    filter(row_number()==1 | row_number()==n()) 
+37

'RowNumber()% w% c (1, N ()) "pozwoliłoby uniknąć dwukrotnego uruchomienia skanowania wektorowego – MichaelChirico

+5

@MichaelChirico I podejrzewasz, że pomijasz '_'? tj. 'filter (row_number()% in% c (1, n()))' –

6

Coś jak:

library(dplyr) 

df <- data.frame(id=c(1,1,1,2,2,2,3,3,3), 
       stopId=c("a","b","c","a","b","c","a","b","c"), 
       stopSequence=c(1,2,3,3,1,4,3,1,2)) 

first_last <- function(x) { 
    bind_rows(slice(x, 1), slice(x, n())) 
} 

df %>% 
    group_by(id) %>% 
    arrange(stopSequence) %>% 
    do(first_last(.)) %>% 
    ungroup 

## Source: local data frame [6 x 3] 
## 
## id stopId stopSequence 
## 1 1  a   1 
## 2 1  c   3 
## 3 2  b   1 
## 4 2  c   4 
## 5 3  b   1 
## 6 3  a   3 

Z do można bardzo dużo wykonać dowolną liczbę operacji na grupy, ale na @ jeremycg odpowiedź jest bardziej odpowiednie tylko dla tego zadania.

+1

Nie rozważano pisania funkcji - z pewnością dobry sposób na zrobienie czegoś bardziej złożonego. – tospig

+1

To wydaje się zbyt skomplikowane w porównaniu do samego użycia 'slice', jak' df%>% aranżacji (stopSequence)%>% group_by (id)%>% slice (c (1, n())) ' – Frank

+3

Nie zgadzam się (i ja wskazał na jeremycg jako lepszą odpowiedź w tym wpisie), ale posiadanie przykładu 'do' może pomóc innym, gdy' slice' nie zadziała (tj. bardziej złożone operacje na grupie). I, możesz umieścić swój komentarz jako odpowiedź (jest najlepszy). – hrbrmstr

13

Nie dplyr, ale jest to o wiele bardziej bezpośredni za pomocą data.table:

library(data.table) 
setDT(df) 
df[ df[order(id, stopSequence), .I[c(1L,.N)], by=id]$V1 ] 
# id stopId stopSequence 
# 1: 1  a   1 
# 2: 1  c   3 
# 3: 2  b   1 
# 4: 2  c   4 
# 5: 3  b   1 
# 6: 3  a   3 

Bardziej szczegółowe wyjaśnienie:

# 1) get row numbers of first/last observations from each group 
# * basically, we sort the table by id/stopSequence, then, 
#  grouping by id, name the row numbers of the first/last 
#  observations for each id; since this operation produces 
#  a data.table 
# * .I is data.table shorthand for the row number 
# * here, to be maximally explicit, I've named the variable V1 
#  as row_num to give other readers of my code a clearer 
#  understanding of what operation is producing what variable 
first_last = df[order(id, stopSequence), .(row_num = .I[c(1L,.N)]), by=id] 
idx = first_last$row_num 

# 2) extract rows by number 
df[idx] 

Koniecznie sprawdź Getting Started wiki dla coraz data.table podstawy pokryty

+1

Lub 'df [df [order (stopSequence), .I [c (1, .N)], keyby = id] $ V1]'. Widok "id" pojawia się dwa razy jest dla mnie dziwny. – Frank

+0

Możesz ustawić klawisze w wywołaniu 'setDT'. Tak więc "zamówienie" nie ma potrzeby tutaj. –

+1

@ArtemKlevtsov - nie zawsze jednak chcesz ustawić klucze. – SymbolixAU

66

Tylko dla kompletności: Możesz podać slice wektor indeksu s:

df %>% arrange(stopSequence) %>% group_by(id) %>% slice(c(1,n())) 

co daje

id stopId stopSequence 
1 1  a   1 
2 1  c   3 
3 2  b   1 
4 2  c   4 
5 3  b   1 
6 3  a   3 
4

Znam pytanie określony dplyr. Ale ponieważ inni już napisali rozwiązań korzystania z innych pakietów, postanowiłem mieć go przy użyciu innych pakietów zbyt:

pakiet podstawowy:

df <- df[with(df, order(id, stopSequence, stopId)), ] 
merge(df[!duplicated(df$id), ], 
     df[!duplicated(df$id, fromLast = TRUE), ], 
     all = TRUE) 

danych.Tabela:

df <- setDT(df) 
df[order(id, stopSequence)][, .SD[c(1,.N)], by=id] 

sqldf:

library(sqldf) 
min <- sqldf("SELECT id, stopId, min(stopSequence) AS StopSequence 
     FROM df GROUP BY id 
     ORDER BY id, StopSequence, stopId") 
max <- sqldf("SELECT id, stopId, max(stopSequence) AS StopSequence 
     FROM df GROUP BY id 
     ORDER BY id, StopSequence, stopId") 
sqldf("SELECT * FROM min 
     UNION 
     SELECT * FROM max") 

W jednym zapytania:

sqldf("SELECT * 
     FROM (SELECT id, stopId, min(stopSequence) AS StopSequence 
       FROM df GROUP BY id 
       ORDER BY id, StopSequence, stopId) 
     UNION 
     SELECT * 
     FROM (SELECT id, stopId, max(stopSequence) AS StopSequence 
       FROM df GROUP BY id 
       ORDER BY id, StopSequence, stopId)") 

wyjściowa:

id stopId StopSequence 
1 1  a   1 
2 1  c   3 
3 2  b   1 
4 2  c   4 
5 3  a   3 
6 3  b   1 
Powiązane problemy