Pertanyaan
Menggunakan dplyr
, bagaimana cara memilih observasi / baris atas dan bawah dari data yang dikelompokkan dalam satu pernyataan?
Data & Contoh
Diberikan bingkai data
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))
Saya bisa mendapatkan pengamatan atas dan bawah dari setiap kelompok menggunakan slice
, tetapi menggunakan dua pernyataan terpisah:
firstStop <- df %>%
group_by(id) %>%
arrange(stopSequence) %>%
slice(1) %>%
ungroup
lastStop <- df %>%
group_by(id) %>%
arrange(stopSequence) %>%
slice(n()) %>%
ungroup
Dapatkah saya menggabungkan dua statmenets ini menjadi salah satu yang menyeleksi kedua atas dan bawah pengamatan?
Jawaban:
Mungkin ada cara yang lebih cepat:
df %>% group_by(id) %>% arrange(stopSequence) %>% filter(row_number()==1 | row_number()==n())
sumber
rownumber() %in% c(1, n())
akan meniadakan kebutuhan untuk menjalankan pemindaian vektor dua kali_
? yaitufilter(row_number() %in% c(1, n()))
Hanya untuk kelengkapan: Anda dapat memberikan
slice
vektor indeks:df %>% arrange(stopSequence) %>% group_by(id) %>% slice(c(1,n()))
pemberian yang mana
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
sumber
filter
- belum menguji ini, tapi lihat di sinimtcars[1, ] %>% slice(c(1, n()))
jadi pilihan di antara mereka bergantung pada apa yang ingin Anda kembalikan. Saya berharap waktunya akan dekat kecualin
sangat besar (di mana irisan mungkin disukai), tetapi belum diuji juga.Tidak
dplyr
, tapi jauh lebih langsung menggunakandata.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
Penjelasan lebih detail:
# 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]
Pastikan untuk memeriksa wiki Memulai untuk mengetahui
data.table
dasar - dasarnyasumber
df[ df[order(stopSequence), .I[c(1,.N)], keyby=id]$V1 ]
. Melihatid
muncul dua kali itu aneh bagiku.setDT
panggilan. Jadiorder
tidak perlu menelepon ke sini.df[order(stopSequence), .SD[c(1L,.N)], by = id]
. Lihat di siniid
. Saya pikirdf[order(stopSequence), .SD[c(1L, .N)], keyby = id]
harus melakukan trik (dengan perbedaan kecil pada solusi di atas sehingga hasilnya akankey
dieditSesuatu seperti:
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
Dengan
do
Anda cukup banyak dapat melakukan sejumlah operasi pada grup tetapi jawaban @ jeremycg jauh lebih tepat hanya untuk tugas ini.sumber
slice
, sepertidf %>% arrange(stopSequence) %>% group_by(id) %>% slice(c(1,n()))
do
contoh di sini dapat membantu orang lain ketikaslice
tidak berfungsi (yaitu operasi yang lebih kompleks pada grup). Dan, Anda harus memposting komentar Anda sebagai jawaban (itu yang terbaik).Saya tahu pertanyaan itu ditentukan
dplyr
. Tetapi, karena orang lain sudah memposting solusi menggunakan paket lain, saya memutuskan untuk mencoba menggunakan paket lain juga:Paket dasar:
df <- df[with(df, order(id, stopSequence, stopId)), ] merge(df[!duplicated(df$id), ], df[!duplicated(df$id, fromLast = TRUE), ], all = TRUE)
tabel data:
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")
Dalam satu kueri:
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)")
Keluaran:
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
sumber
menggunakan
which.min
danwhich.max
:library(dplyr, warn.conflicts = F) df %>% group_by(id) %>% slice(c(which.min(stopSequence), which.max(stopSequence))) #> # A tibble: 6 x 3 #> # Groups: id [3] #> id stopId stopSequence #> <dbl> <fct> <dbl> #> 1 1 a 1 #> 2 1 c 3 #> 3 2 b 1 #> 4 2 c 4 #> 5 3 b 1 #> 6 3 a 3
patokan
Ini juga jauh lebih cepat daripada jawaban yang diterima saat ini karena kita menemukan nilai min dan maks berdasarkan grup, daripada mengurutkan seluruh kolom stopSequence.
# create a 100k times longer data frame df2 <- bind_rows(replicate(1e5, df, F)) bench::mark( mm =df2 %>% group_by(id) %>% slice(c(which.min(stopSequence), which.max(stopSequence))), jeremy = df2 %>% group_by(id) %>% arrange(stopSequence) %>% filter(row_number()==1 | row_number()==n())) #> Warning: Some expressions had a GC in every iteration; so filtering is disabled. #> # A tibble: 2 x 6 #> expression min median `itr/sec` mem_alloc `gc/sec` #> <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl> #> 1 mm 22.6ms 27ms 34.9 14.2MB 21.3 #> 2 jeremy 254.3ms 273ms 3.66 58.4MB 11.0
sumber
Menggunakan
data.table
:# 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
sumber
Pendekatan lain dengan pernyataan lapply dan dplyr. Kita dapat menerapkan nomor acak dari fungsi ringkasan apa pun ke pernyataan yang sama:
lapply(c(first, last), function(x) df %>% group_by(id) %>% summarize_all(funs(x))) %>% bind_rows()
Misalnya, Anda dapat tertarik pada baris dengan nilai stopSequence maks dan melakukan:
lapply(c(first, last, max("stopSequence")), function(x) df %>% group_by(id) %>% summarize_all(funs(x))) %>% bind_rows()
sumber
Alternatif R dasar yang berbeda adalah yang pertama
order
olehid
danstopSequence
,split
mereka berdasarkanid
dan untuk setiapid
kita hanya memilih indeks pertama dan terakhir dan subset kerangka data menggunakan indeks tersebut.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
Atau penggunaan serupa
by
df[unlist(with(df, by(order(id, stopSequence), id, function(x) c(x[1], x[length(x)])))), ]
sumber