2011-11-20 50 views
27

Làm cách nào tôi có thể chọn hàng đầu tiên và cuối cùng cho mỗi id duy nhất trong khung dữ liệu sau?Làm thế nào để chọn hàng đầu tiên và cuối cùng trong một biến nhóm trong một khung dữ liệu?

tmp <- structure(list(id = c(15L, 15L, 15L, 15L, 21L, 21L, 22L, 22L, 
22L, 23L, 23L, 23L, 24L, 24L, 24L, 24L), d = c(1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L), gr = c(2L, 1L, 
1L, 1L, 1L, 2L, 1L, 1L, 2L, 1L, 1L, 2L, 1L, 1L, 1L, 2L), mm = c(3.4, 
4.9, 4.4, 5.5, 4, 3.8, 4, 4.9, 4.6, 2.7, 4, 3, 3, 2, 4, 2), area = c(1L, 
2L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 2L, 3L)), .Names = c("id", 
"d", "gr", "mm", "area"), class = "data.frame", row.names = c(NA, 
-16L)) 
tmp 
#> id d gr mm area 
#> 1 15 1 2 3.4 1 
#> 2 15 1 1 4.9 2 
#> 3 15 1 1 4.4 1 
#> 4 15 1 1 5.5 2 
#> 5 21 1 1 4.0 2 
#> 6 21 1 2 3.8 2 
#> 7 22 1 1 4.0 2 
#> 8 22 1 1 4.9 2 
#> 9 22 1 2 4.6 2 
#> 10 23 1 1 2.7 2 
#> 11 23 1 1 4.0 2 
#> 12 23 1 2 3.0 2 
#> 13 24 1 1 3.0 2 
#> 14 24 1 1 2.0 3 
#> 15 24 1 1 4.0 2 
#> 16 24 1 2 2.0 3 

Trả lời

22

Một giải pháp plyr (tmp là khung dữ liệu của bạn):

library("plyr") 
ddply(tmp, .(id), function(x) x[c(1, nrow(x)), ]) 
# id d gr mm area 
# 1 15 1 2 3.4 1 
# 2 15 1 1 5.5 2 
# 3 21 1 1 4.0 2 
# 4 21 1 2 3.8 2 
# 5 22 1 1 4.0 2 
# 6 22 1 2 4.6 2 
# 7 23 1 1 2.7 2 
# 8 23 1 2 3.0 2 
# 9 24 1 1 3.0 2 
# 10 24 1 2 2.0 3 

Hoặc với dplyr (xem thêm here):

library("dplyr") 
tmp %>% 
group_by(id) %>% 
slice(c(1, n())) %>% 
ungroup() 
# # A tibble: 10 × 5 
#  id  d gr mm area 
# <int> <int> <int> <dbl> <int> 
# 1  15  1  2 3.4  1 
# 2  15  1  1 5.5  2 
# 3  21  1  1 4.0  2 
# 4  21  1  2 3.8  2 
# 5  22  1  1 4.0  2 
# 6  22  1  2 4.6  2 
# 7  23  1  1 2.7  2 
# 8  23  1  2 3.0  2 
# 9  24  1  1 3.0  2 
# 10 24  1  2 2.0  3 
+0

Cảm ơn rất nhiều cho giải pháp này rất hữu ích. Rất cảm kích!! – Francesco

70

Một giải pháp nhanh chóng và ngắn data.table:

tmp[, .SD[c(1,.N)], by=id] 

trong đó .SD đại diện cho mỗi (S) ubset (D) ata, .N là số hàng trong mỗi nhóm và tmpdata.table; ví dụ. theo mặc định được cung cấp bởi fread() theo mặc định hoặc bằng cách chuyển đổi data.frame bằng cách sử dụng setDT().

Lưu ý rằng nếu một nhóm chỉ chứa một hàng, hàng đó sẽ xuất hiện hai lần trong đầu ra vì hàng đó là cả hàng đầu tiên và hàng cuối cùng của nhóm đó. Để tránh sự lặp lại trong trường hợp đó, nhờ @Thell:

tmp[, .SD[unique(c(1,.N))], by=id] 

Ngoài ra, sau đây làm cho logic rõ ràng đối với trường hợp .N==1 đặc biệt:

tmp[, if (.N==1) .SD else .SD[c(1,.N)], by=id] 

Bạn không cần .SD[1] trong lần đầu tiên một phần của if vì trong trường hợp đó, .N1 vì vậy .SD phải là một hàng.

Bạn có thể quấn j trong {} và có toàn bộ trang mã bên trong {} nếu bạn muốn. Chỉ cần biểu thức cuối cùng bên trong {} trả về một đối tượng giống như được xếp chồng lên nhau list (chẳng hạn như một đồng bằng list, data.table hoặc data.frame).

tmp[, { ...; if (.N==1) .SD else .SD[c(1,.N)] } , by=id] 
+7

'.SD [duy nhất (c (1, .N))] 'cho khi một nhóm có một thành viên. – Thell

+2

mượn từ trại dữ liệu 'setkey (tmp, id)' và 'tmp [, .SD [c (1, .N)], .EACHI]' – Kerry

+0

@Kerry Tôi phải thừa nhận là tôi không biết 'setkey (tmp, id); tmp [, .SD [c (1, .N)], .EACHI] 'sẽ hoạt động mà không có bất kỳ thứ gì tôi có. Trường hợp chính xác là nó trên DataCamp? Cảm ơn. –

4

Đây là giải pháp trong cơ sở R. Nếu có nhiều nhóm với cùng một id mã này trả về hàng đầu tiên và cuối cùng cho từng nhóm riêng lẻ đó.

EDIT: 12 tháng 1 năm 2017

Giải pháp này có thể là trực quan hơn một chút so với câu trả lời khác của tôi xa hơn dưới đây:

lmy.df = read.table(text = ' 
    id d gr  mm area 
    15 1  2 3.40  1 
    15 1  1 4.90  2 
    15 1  1 4.40  1 
    15 1  1 5.50  2 
    21 1  1 4.00  2 
    21 1  2 3.80  2 
    22 1  1 4.00  2 
    23 1  1 2.70  2 
    23 1  1 4.00  2 
    23 1  2 3.00  2 
    24 1  1 3.00  2 
    24 1  1 2.00  3 
    24 1  1 4.00  2 
    24 1  2 2.00  3 
', header = TRUE) 

head <- aggregate(lmy.df, by=list(lmy.df$id), FUN = function(x) { first = head(x,1) }) 
tail <- aggregate(lmy.df, by=list(lmy.df$id), FUN = function(x) { last = tail(x,1) }) 
head$order = 'first' 
tail$order = 'last' 

my.output <- rbind(head, tail) 
my.output 
# Group.1 id d gr mm area order 
#1  15 15 1 2 3.4 1 first 
#2  21 21 1 1 4.0 2 first 
#3  22 22 1 1 4.0 2 first 
#4  23 23 1 1 2.7 2 first 
#5  24 24 1 1 3.0 2 first 
#6  15 15 1 1 5.5 2 last 
#7  21 21 1 2 3.8 2 last 
#8  22 22 1 1 4.0 2 last 
#9  23 23 1 2 3.0 2 last 
#10  24 24 1 2 2.0 3 last 

EDIT: 18 tháng 6 năm 2016

Kể từ khi đăng câu trả lời ban đầu của tôi, tôi đã học được cách tốt hơn là sử dụng lapply hơn apply. Điều này là do apply không hoạt động nếu mỗi nhóm có cùng số hàng.Xem ở đây: Error when numbering rows by group

lmy.df = read.table(text = ' 
    id d gr  mm area 
    15 1  2 3.40  1 
    15 1  1 4.90  2 
    15 1  1 4.40  1 
    15 1  1 5.50  2 
    21 1  1 4.00  2 
    21 1  2 3.80  2 
    22 1  1 4.00  2 
    23 1  1 2.70  2 
    23 1  1 4.00  2 
    23 1  2 3.00  2 
    24 1  1 3.00  2 
    24 1  1 2.00  3 
    24 1  1 4.00  2 
    24 1  2 2.00  3 
', header = TRUE) 


lmy.seq <- rle(lmy.df$id)$lengths 
lmy.df$first <- unlist(lapply(lmy.seq, function(x) seq(1,x))) 
lmy.df$last <- unlist(lapply(lmy.seq, function(x) seq(x,1,-1))) 
lmy.df 

lmy.df2 <- lmy.df[lmy.df$first==1 | lmy.df$last == 1,] 
lmy.df2 

# id d gr mm area first last 
#1 15 1 2 3.4 1  1 4 
#4 15 1 1 5.5 2  4 1 
#5 21 1 1 4.0 2  1 2 
#6 21 1 2 3.8 2  2 1 
#7 22 1 1 4.0 2  1 1 
#8 23 1 1 2.7 2  1 3 
#10 23 1 2 3.0 2  3 1 
#11 24 1 1 3.0 2  1 4 
#14 24 1 2 2.0 3  4 1 

Dưới đây là một ví dụ trong đó mỗi nhóm có hai hàng:

lmy.df = read.table(text = ' 
    id d gr  mm area 
    15 1  2 3.40  1 
    15 1  1 4.90  2 
    21 1  1 4.00  2 
    21 1  2 3.80  2 
    22 1  1 4.00  2 
    22 1  1 6.00  2 
    23 1  1 2.70  2 
    23 1  2 3.00  2 
    24 1  1 3.00  2 
    24 1  2 2.00  3 
', header = TRUE) 

lmy.seq <- rle(lmy.df$id)$lengths 

lmy.df$first <- unlist(lapply(lmy.seq, function(x) seq(1,x))) 
lmy.df$last <- unlist(lapply(lmy.seq, function(x) seq(x,1,-1))) 
lmy.df 

lmy.df2 <- lmy.df[lmy.df$first==1 | lmy.df$last == 1,] 
lmy.df2 

# id d gr mm area first last 
#1 15 1 2 3.4 1  1 2 
#2 15 1 1 4.9 2  2 1 
#3 21 1 1 4.0 2  1 2 
#4 21 1 2 3.8 2  2 1 
#5 22 1 1 4.0 2  1 2 
#6 22 1 1 6.0 2  2 1 
#7 23 1 1 2.7 2  1 2 
#8 23 1 2 3.0 2  2 1 
#9 24 1 1 3.0 2  1 2 
#10 24 1 2 2.0 3  2 1 

Original câu trả lời:

my.seq <- data.frame(rle(my.df$id)$lengths) 

my.df$first <- unlist(apply(my.seq, 1, function(x) seq(1,x))) 
my.df$last <- unlist(apply(my.seq, 1, function(x) seq(x,1,-1))) 

my.df2 <- my.df[my.df$first==1 | my.df$last == 1,] 
my.df2 

    id d gr mm area first last 
1 15 1 2 3.4 1  1 4 
4 15 1 1 5.5 2  4 1 
5 21 1 1 4.0 2  1 2 
6 21 1 2 3.8 2  2 1 
7 22 1 1 4.0 2  1 3 
9 22 1 2 4.6 2  3 1 
10 23 1 1 2.7 2  1 3 
12 23 1 2 3.0 2  3 1 
13 24 1 1 3.0 2  1 4 
16 24 1 2 2.0 3  4 1 
Các vấn đề liên quan