wake-up-neo.com

Wählen Sie aus den gruppierten Daten die erste und letzte Zeile aus

Frage

Wie wähle ich mit dplyr die oberen und unteren Beobachtungen/Zeilen gruppierter Daten in einer Anweisung aus?

Daten & Beispiel

Gegeben ein Datenrahmen

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))

Ich kann die oberen und unteren Beobachtungen aus jeder Gruppe mit slice abrufen, wobei zwei verschiedene Statements verwendet werden:

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

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

Kann ich diese beiden Statmen zu einem kombinieren, das sowohl obere als auch untere Beobachtungen auswählt?

100
tospig

Es gibt wahrscheinlich einen schnelleren Weg:

df %>%
  group_by(id) %>%
  arrange(stopSequence) %>%
  filter(row_number()==1 | row_number()==n())
172
jeremycg

Nur zur Vollständigkeit: Sie können slice einen Vektor von Indizes übergeben:

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

was gibt

  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
85
Frank

Nicht dplyr, aber data.table ist viel direkter:

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

Ausführlichere Erklärung:

# 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]

Schauen Sie sich das Wiki Getting Started an, um die data.table-Grundlagen zu verstehen

14
MichaelChirico

So etwas wie:

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

Mit do können Sie eine beliebige Anzahl von Operationen in der Gruppe ausführen, aber die Antwort von @ jeremycg ist für diese Aufgabe viel geeigneter.

6
hrbrmstr

Ich kenne die Frage dplyr. Da andere jedoch bereits Lösungen mit anderen Paketen veröffentlicht haben, habe ich mich auch für andere Pakete entschieden:

Basispaket:

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

datentabelle:

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")

In einer Abfrage:

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)")

Ausgabe:

  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
6
mpalanco

Verwenden von data.table im Jahr 2018:

# convert to data.table
setDT(df) 
# order, group, filter
df[order(stopSequence)][, .SD[c(1, .N)], by = id]

   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
0
sindri_baldur

Eine andere Basis-R-Alternative wäre die erste order nach id und stopSequence, split basierend auf id. Für jede id wählen wir nur den ersten und den letzten Index aus und unterteilen den Datenrahmen unter Verwendung dieser Indizes.

df[sapply(with(df, split(order(id, stopSequence), id)), function(x) 
                   c(x[1], x[length(x)])), ]


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

Oder ähnlich mit by

df[unlist(with(df, by(order(id, stopSequence), id, function(x) 
                   c(x[1], x[length(x)])))), ]
0
Ronak Shah

Ein weiterer Ansatz mit lapply und einer dplyr-Anweisung. Wir können eine beliebige Anzahl beliebiger zusammenfassender Funktionen auf dieselbe Anweisung anwenden:

lapply(c(first, last), 
       function(x) df %>% group_by(id) %>% summarize_all(funs(x))) %>% 
bind_rows()

Sie könnten beispielsweise auch an Zeilen mit dem Wert max stopSequence interessiert sein und Folgendes tun: 

lapply(c(first, last, max("stopSequence")), 
       function(x) df %>% group_by(id) %>% summarize_all(funs(x))) %>%
bind_rows()
0
Sahir Moosvi