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

时间:2014-11-26 21:11:47

标签: r data.table

尝试使用R中的data.table修复重复数据删除问题。

列A是名称列表,其中一些名称多次出现。 B列是日期列表。我还想复制其他一些列(日期名称发生的事情。)

但是我只想查看新数据表中每个人的最多活动,每个名称对应于最近的日期有1个条目。

示例数据

    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)以便使用{{1}要删除重复项,它按键顺序重新排序表(按名称字母顺序排列)。然后使用unique()返回每个名称的第一个外观,这不一定是最近的日期。

如果我在两列unique(dt)上设置密钥,则无法使用setkeyv(dt,c(name.last,date))删除重复项,因为所有密钥都是唯一的。

问题类似于此处的一篇帖子:Collapsing data frame by selecting one row per group。但是我不能假设要选择的数据是第一个还是最后一个,除非你可以建议一种操作我的数据的方法来设置密钥。

3 个答案:

答案 0 :(得分:3)

有很多方法可以在不对数据表进行排序的情况下执行此操作(虽然排序是首选,因为duplicated非常有效并且您也避免使用by - 将会达到此目的。 / p>

首先,您必须确保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(就像您已经做过的那样)在from.last = TRUE中指定duplicated并删除!

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.table s unique函数(也应该非常有效)

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

答案 1 :(得分: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")

希望这很有帮助。

答案 2 :(得分:1)

在写这篇文章时我发现了它。为后人......

按名称和日期对表格进行排序,以便您可以依赖于您希望在组中的第一个或最后一个日期。例如:dt[order(names,-date)]

然后,而不是设置一个键并使用unique(),只需一个简单的:

dt[!duplicated(names)]

其中names是重复列。

应输出所需的表格。如果有更优雅/可靠的方法,我有兴趣听到它们。