使用data.table在R中,根据列B,有条件地删除与列A匹配的行。

3
试图使用R中的data.table解决去重问题。
列A是姓名列表,其中一些姓名出现多次。列B是日期列表。我还想复制其他列(关于“姓名”和“日期”的事件)。
但是,我只想在一个新的datatable中查看每个人最活跃的记录,该记录对应于最近的日期,并且每个名称仅对应一个条目。
以下是示例数据:
    name.last       date
 1:     Adams 2014-10-20
 2:     Adams 2014-07-07
 3:   Barnett 2014-11-06
 4:   Barnett 2014-09-22
 5:      Bell 2014-10-22
 6:      Bell 2014-07-29
 7:     Burns 2014-09-08
 8:     Burns 2014-09-03
 9:   Camacho 2014-08-12
10:   Camacho 2014-07-08
11:  Casillas 2014-10-07
12:  Casillas 2014-07-17
13:    Chavez 2014-09-23
14:    Chavez 2014-09-17
15:   Chavira 2014-07-15
16:   Chavira 2014-07-07
17:    Claren 2014-10-30
18:    Claren 2014-10-23
19:  Colleary 2014-11-11
20:  Colleary 2014-11-07

答案仅返回每个姓名的第一个(因为这里的行按每个名字最近的日期排序)。但是,如果我设置dt键setkey(dt,name.last)以使用unique()删除重复项,则会按键顺序重新排序表格(按名称字母顺序排序)。然后使用unique(dt)返回每个名称的第一次出现,这不一定是最近的日期。
如果我在两个列上同时设置键setkeyv(dt,c(name.last,date)),则无法使用unique()删除重复项,因为所有键都是唯一的。
问题类似于此处发布的问题:Collapsing data frame by selecting one row per group。但是,除非您可以建议一种方法来操作我的数据使其成为设置键后的第一个或最后一个数据,否则我不能假设要选择的数据是第一个或最后一个。
3个回答

3

有很多方法可以在不排序数据表的情况下完成这个任务(尽管排序是首选,因为duplicated非常高效,并且您也避免使用by - 我们会讲到这一点)。

首先,您必须确保date的类别是Date,以便使事情变得更容易。

dt[, date := as.Date(date)]

第一个简单方法(虽然不是最有效的)

dt[, max(date), name.last]
#     name.last         V1
#  1:     Adams 2014-10-20
#  2:   Barnett 2014-11-06
#  3:      Bell 2014-10-22
#  4:     Burns 2014-09-08
#  5:   Camacho 2014-08-12
#  6:  Casillas 2014-10-07
#  7:    Chavez 2014-09-23
#  8:   Chavira 2014-07-15
#  9:    Claren 2014-10-30
# 10:  Colleary 2014-11-11

第二种(首选)方法与您的类似,但是使用了data.tables中的 setorder 方法(适用于 data.table版本 >= 1.9.4),应该是最有效的。

setorder(dt, name.last, -date)[!duplicated(name.last)]
#     name.last       date
#  1:     Adams 2014-10-20
#  2:   Barnett 2014-11-06
#  3:      Bell 2014-10-22
#  4:     Burns 2014-09-08
#  5:   Camacho 2014-08-12
#  6:  Casillas 2014-10-07
#  7:    Chavez 2014-09-23
#  8:   Chavira 2014-07-15
#  9:    Claren 2014-10-30
# 10:  Colleary 2014-11-11

您可以使用setkey(如您已经做过的那样),并在duplicated中指定from.last = TRUE,然后删除!来实现相同的效果。

setkey(dt, name.last, date)[duplicated(name.last, from.last = TRUE)]

#     name.last       date
#  1:     Adams 2014-10-20
#  2:   Barnett 2014-11-06
#  3:      Bell 2014-10-22
#  4:     Burns 2014-09-08
#  5:   Camacho 2014-08-12
#  6:  Casillas 2014-10-07
#  7:    Chavez 2014-09-23
#  8:   Chavira 2014-07-15
#  9:    Claren 2014-10-30
# 10:  Colleary 2014-11-11

第三种方法是使用 data.tableunique 函数(应该也非常高效)。

unique(setorder(dt, name.last, -date), by = "name.last")
#     name.last       date
#  1:     Adams 2014-10-20
#  2:   Barnett 2014-11-06
#  3:      Bell 2014-10-22
#  4:     Burns 2014-09-08
#  5:   Camacho 2014-08-12
#  6:  Casillas 2014-10-07
#  7:    Chavez 2014-09-23
#  8:   Chavira 2014-07-15
#  9:    Claren 2014-10-30
# 10:  Colleary 2014-11-11

最后一种方法是使用.SD。它是最不高效的,但在某些情况下非常有用,比如当您想要返回所有列并且无法使用诸如duplicated等函数时。

setorder(dt, name.last, -date)[, .SD[1], name.last]
#     name.last       date
#  1:     Adams 2014-10-20
#  2:   Barnett 2014-11-06
#  3:      Bell 2014-10-22
#  4:     Burns 2014-09-08
#  5:   Camacho 2014-08-12
#  6:  Casillas 2014-10-07
#  7:    Chavez 2014-09-23
#  8:   Chavira 2014-07-15
#  9:    Claren 2014-10-30
# 10:  Colleary 2014-11-11

2

如果我理解你的问题正确,我认为你可以使用sqldf包更干净地完成这个过程,但缺点是你必须了解SQL。

install.packages("sqldf")
library("sqldf")
dt <-data.frame(read.table(header = TRUE, text = " name.last       date
1:     Adams 2014-10-20
2:     Adams 2014-07-07
3:   Barnett 2014-11-06
4:   Barnett 2014-09-22
5:      Bell 2014-10-22
6:      Bell 2014-07-29
7:     Burns 2014-09-08
8:     Burns 2014-09-03
9:   Camacho 2014-08-12
10:   Camacho 2014-07-08
11:  Casillas 2014-10-07
12:  Casillas 2014-07-17
13:    Chavez 2014-09-23
14:    Chavez 2014-09-17
15:   Chavira 2014-07-15
16:   Chavira 2014-07-07
17:    Claren 2014-10-30
18:    Claren 2014-10-23
19:  Colleary 2014-11-11
20:  Colleary 2014-11-07")
)
head(dt)
colnames(dt) <- c('names', 'date')
sqldf("select names, min(date), max(date) from dt group by names")

希望这对您有所帮助。


1
在撰写此文时,我想到了解决的办法。供后来者参考...
按名称和日期对表进行排序,这样您就可以依赖所需日期是组中的第一项或最后一项。例如:dt[order(names,-date)]
然后,不要设置密钥并使用unique(),只需简单地运行: dt[!duplicated(names)] 其中names是重复的列。
应输出所需的表格。如果有更优雅/可靠的方法,请告诉我。

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