使用R语言对数据框进行行子集和重复处理

4
假设我们有以下带有列名“id”,“time”和“x”的数据:
df<-
structure(
list(
id = c(1L, 1L, 1L, 2L, 2L, 3L, 3L),
time = c(20L, 6L, 7L, 11L, 13L, 2L, 6L),
x = c(1L, 1L, 0L, 1L, 1L, 1L, 0L)
),
.Names = c("id", "time", "x"),
class = "data.frame",
row.names = c(NA,-7L)
)

每个id都有多个时间和x的观测值。我想提取每个id的最后一个观测值,并形成一个新的数据框,根据原始数据中每个id的观测次数重复这些观测值。使用以下代码可以提取每个id的最后一个观测值:

library(dplyr) 
df<-df%>% 
group_by(id) %>% 
filter( ((x)==0 & row_number()==n())| ((x)==1 & row_number()==n()))

仍未解决的是重复方面的问题。期望的输出应如下所示:
df <-
structure(
list(
id = c(1L, 1L, 1L, 2L, 2L, 3L, 3L),
time = c(7L, 7L, 7L, 13L, 13L, 6L, 6L),
x = c(0L, 0L, 0L, 1L, 1L, 0L, 0L)
),
.Names = c("id", "time", "x"),
class = "data.frame",
row.names = c(NA,-7L)
)

感谢您的帮助。事先感谢。

谢谢,所有的答案都很好地解决了我的问题。 - T Richard
4个回答

4
我们可以使用ave函数来找到每个ID的最大行号,并从数据框中进行子集。
df[ave(1:nrow(df), df$id, FUN = max), ]

#    id time x
#3    1    7 0
#3.1  1    7 0
#3.2  1    7 0
#5    2   13 1
#5.1  2   13 1
#7    3    6 0
#7.1  3    6 0

2
你可以使用last()来获取每个id中的最后一行。
df %>%
    group_by(id) %>%
    mutate(time = last(time),
           x = last(x))

last(x)返回单个值,因此它会扩展到mutate()调用中的所有行。

这也可以使用mutate_at应用于任意数量的变量:

df %>%
    group_by(id) %>%
    mutate_at(vars(-id), ~ last(.))

聪明,但是现在在 df 中有30个变量时再试试吧 :-) - thelatemail
@thelatemail 好的 :-) - Marius
1
或者 df %>% group_by(id) %>% mutate_all(funs(last)) - Ronak Shah
@Marius - :-D! - thelatemail

2

slice 在 tidyverse 中应该会成为你的好帮手:

df %>%
  group_by(id) %>%
  slice(rep(n(),n()))
## A tibble: 7 x 3
## Groups:   id [3]
#     id  time     x
#  <int> <int> <int>
#1     1     7     0
#2     1     7     0
#3     1     7     0
#4     2    13     1
#5     2    13     1
#6     3     6     0
#7     3     6     0

data.table 中,您还可以使用联接的 mult= 参数:
library(data.table)
setDT(df)
df[df[,.(id)], on="id", mult="last"]
#   id time x
#1:  1    7 0
#2:  1    7 0
#3:  1    7 0
#4:  2   13 1
#5:  2   13 1
#6:  3    6 0
#7:  3    6 0

在基础的R语言中,使用 merge 函数也可以实现该操作:

merge(df["id"], df[!duplicated(df$id, fromLast=TRUE),])
#  id time x
#1  1    7 0
#2  1    7 0
#3  1    7 0
#4  2   13 1
#5  2   13 1
#6  3    6 0
#7  3    6 0

2
使用 data.table,您可以尝试
library(data.table)
setDT(df)[,.(time=rep(time[.N],.N), x=rep(x[.N],.N)), by=id]
   id time  x
1:  1    7  0
2:  1    7  0
3:  1    7  0
4:  2   13  1
5:  2   13  1
6:  3    6  0
7:  3    6  0

按照 @thelatemai 的提示,你也可以尝试避免为列命名,方法如下

df[, .SD[rep(.N,.N)], by=id]
   id time x
1:  1    7 0
2:  1    7 0
3:  1    7 0
4:  2   13 1
5:  2   13 1
6:  3    6 0
7:  3    6 0

1
如果您只是像 df[, .SD[rep(.N,.N)], by=id] 这样对整个子数据框进行子集操作,就可以避免所有列命名的问题,尽管这可能不是非常高效。如果您有兴趣,我相信您也可以通过 .I 行计数器来进行一些巧妙的处理。 - thelatemail
@thelatemail,你的变体很棒,我会在回答中包含它。 - Chriss Paul

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接