【问题标题】:How to pull specific field from a data.table based on multiple by-record criteria?如何根据多个按记录条件从 data.table 中提取特定字段?
【发布时间】:2016-03-21 19:00:09
【问题描述】:

使用 R 的 data.table 包,我希望能够根据多个条件提取特定记录。实际上,我正在处理一个包含数百万或数千万条记录的表,并且我想提取给定字段的倒数第二个条目,以获得该字段中终止 0 条目的记录。我可以识别哪些记录是感兴趣的,我可以识别 0 条目的日期,因此通过一些创造性的使用 lubridate 我可以创建一个 ID 列表和“我应该寻找的日期”。如何获取这个两列、100 行的列表并使用它从包含 1000 万条记录的表中返回我需要的特定 100 个值?

这是我正在尝试做的一个简单示例。

给定以下数据表A

    Name Date Amount
 1:    A    1    100
 2:    A    2    100
 3:    A    3    100
 4:    A    4     99
 5:    A    5     98
 6:    A    6     97
 7:    A    7     96
 8:    A    8     95
 9:    A    9     94
10:    A   10     93
11:    A   11     92
12:    A   12      0
13:    B    2    200
14:    B    3    200
15:    B    4    190
16:    B    5    180
17:    B    6    170
18:    B    7    160
19:    B    8    150
20:    B    9      0
21:    C    2    100
22:    C    3     95
23:    C    4     90
24:    C    5     90
25:    C    6     85
26:    C    7     80
27:    C    8      0

我想做的是为每条记录提取最后一个非零Amount。我能做的是创建一个表B

   Name Date
1:    A   11
2:    B    8
3:    C    7

我想要的是A 中的Amount B 中的每个NameDate。答案应该是c(92, 150, 80)

另一种方法是提取所有记录的子集,以使 NameDate 字段对有效。我可能可以将两者连接起来并以这种方式进行搜索,但实际上,Name 是一个长字母数字字符串,Date 被转换为 POSIX,因此可能会变得很难看。

此外,如果有更简单的方法可以通过特定字段返回倒数第二条记录,我可能会这样做错误,这可能就是我所需要的,其功能类似于:A[Name %in% X, second-to-last record, by = Name] where X是我感兴趣的记录列表。

谢谢。

代码

A <- structure(list(Name = c("A", "A", "A", "A", "A", "A", "A", "A", 
"A", "A", "A", "A", "B", "B", "B", "B", "B", "B", "B", "B", "C", 
"C", "C", "C", "C", "C", "C"), Date = c(1L, 2L, 3L, 4L, 5L, 6L, 
7L, 8L, 9L, 10L, 11L, 12L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 2L, 
3L, 4L, 5L, 6L, 7L, 8L), Amount = c(100L, 100L, 100L, 99L, 98L, 
97L, 96L, 95L, 94L, 93L, 92L, 0L, 200L, 200L, 190L, 180L, 170L, 
160L, 150L, 0L, 100L, 95L, 90L, 90L, 85L, 80L, 0L)), .Names = c("Name", 
"Date", "Amount"), row.names = c(NA, -27L), class = c("data.table", 
"data.frame"))

B <- data.table(Name = c('A', 'B', 'C'), Date = c(11, 8, 7))

【问题讨论】:

  • A[Amount!=0, tail(.SD,1), Name]
  • @ProcrastinatusMaximus 谢谢,如果我从表中的数百万条记录中预先提取我需要查看的数千条记录,那将非常有效。如果你把它写成答案,我会接受它,因为它解决了我的问题 8-)。但是,您知道更普遍的问题的答案吗?假设我想要的金额值对应于不同的日期,不一定是倒数第二个?再次感谢!
  • @Avraham 如果您想扩展您的问题,我认为编辑它会很好,因为还没有人回答。就个人而言,我不知道您所说的“对应于各个日期”是什么意思,没有更多解释。
  • 哦,好的。那不一样。我会先mytab = rbindlist(list(list(Name = "A", Date = 5), list("B", 4), list("C", 6))) 然后A[mytab,on=names(mytab)]

标签: r data.table


【解决方案1】:

无需创建单独的B data.table。您可以过滤掉零值,然后选择最后一个观察值。有几种方法可以做到这一点:

# method 1:
A[Amount!=0, tail(.SD,1), by = Name]

# method 2:
A[!!Amount, .SD[.N], by = Name]

# method 3:
A[Amount!=0, lapply(.SD, last), by = Name]

# method 4:
A[Amount!=0][!duplicated(Name, fromLast = TRUE)]

# method 5 (as proposed by @Frank in the comments):
unique(A[Amount!=0], by = "Name", fromLast = TRUE)

# method 6:
A[A[Amount!=0, .I[.N], Name]$V1]

全部给出:

   Name Date Amount
1:    A   11     92
2:    B    8    150
3:    C    7     80

注意事项:

  1. A[Amount!=0, last(.SD), by = Name] 将不起作用并导致错误消息。
  2. 方法 6 可能看起来过于复杂,但尤其是在非常大的数据集上,使用 .I 似乎是最快的方法。有关基准,请参阅this answer

关于您的第二个问题(如the comments 中提出的),您可以使用如下连接来获取特定日期的值:

B <- data.table(Name = c('A', 'B', 'C'), Date = c(5, 4, 6))

A[B, on = c('Name','Date')]
# or as proposed by @Frank:
A[B, on = names(B)]

给你:

   Name Date Amount
1:    A    5     98
2:    B    4    190
3:    C    6     85

【讨论】:

  • 如果我可以回答第二个问题,我会给你一个额外的接受;谢谢!
猜你喜欢
  • 1970-01-01
  • 2019-11-22
  • 2020-11-10
  • 2021-09-23
  • 1970-01-01
  • 2021-08-02
  • 2012-06-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多