【问题标题】:Accessing a data.table via column numbers and grep通过列号和 grep 访问 data.table
【发布时间】:2016-09-30 22:38:10
【问题描述】:

如果我使用这个简单的 data.table(一列)

mydata <- data.table(A=c("ID123", "ID22", "AAA", NA))

我可以找到以“ID”开头的行的位置

grep("^ID", mydata[,A])

如何改用数字来获得相同的结果? (说我想要第一列)。

我试过了

grep("^ID", mydata[,1, with=F])

但它不起作用。

更重要的是,我想以data.table的方式来做,在括号内引入命令。

mydata[,grep("^ID",.SD), .SDcols=1]

但这不起作用。

我找到了这种方式,但是太复杂了

mydata[,lapply(.SD, grep,pattern="ID"), .SDcols=1] 

正确的方法是什么?

稍微复杂一点:
如果我想同时计算有多少行不是 NA 并以“ID”开头怎么办?

有点像

any(!(grepl("^ID", mydata[,A] ) | is.na(mydata[,A])))

但更紧凑且位于括号内。

我不喜欢 grep 将 NA 视为不匹配而不是输出 NA 的事实。

【问题讨论】:

  • 我找到了这种方式,但是太复杂了 mydata[,lapply(.SD, grep,pattern="ID"), .SDcols=1]
  • FAQ 中的第一个答案解释了为什么“按数字而不是名称来引用列通常是不好的做法”rawgit.com/wiki/Rdatatable/data.table/vignettes/… 一种计算行数的方法是 mydata[ A %like% "^ID" | is.na(A), .N ] -- %like% 是只是一个方便的快捷方式grepl。顺便说一句,如果您的示例包含 NA,可能会更好。
  • 我更改了问题标题,以便更准确地总结请求。
  • 我知道使用列号可能很危险,因为它们可能会按时更改。但有时您需要使用它们一次引用多个列,或者因为这些数字来自之前的过滤。
  • 我刚刚发现,只要参数为 NA,grepl 就会产生 FALSE。然后我们不需要添加 is.na() 检查。这取决于我们想要什么。

标签: r data.table


【解决方案1】:

别忘了data.table 也是list。因此,如果您真的并且只是想要一整列作为向量,那么鼓励只在其上使用基本 R 方法:[[$。 p>

mydata <- data.table(A=c("ID123", "ID22", "AAA"))
mydata
#       A
#1: ID123
#2:  ID22
#3:   AAA
grep("^ID", mydata[[1]])   # using a column number
#[1] 1 2
grep("^ID", mydata$A)
#[1] 1 2

如果您在循环中需要这个,那么[[$ 会更快,因为它们避免了DT[...] 内部的参数检查开销。如果只是一个调用,那么开销可以忽略不计。

grep("^ID", mydata[,1, with=F])“不起作用”(请包括您看到的错误消息而不是“不起作用”!)因为grep 想要一个向量,但DT[] 总是返回一个 data.table,即使如果是 1 列,则用于重要的类型一致性,例如链接时。直接mydata[[1]] 更干净,但另一种说明方式是grep("^ID", mydata[,1,with=F][[1]])

正如 Frank 在 cmets 中所说,强烈建议不要使用列号,因为文档解释说,随着您的数据在未来数月和数年内发生变化,可能会出现错误。在 DT[...] 中改用列名。

但如果你真的必须,而且有时它是有效的,那么如何:

..theCol = DT[[theNumber]]
DT[ grep(,..theCol) & ..theCol | ..theCol etc , ... ]

变量名中的.. 前缀有点像目录路径一样表示“向上”。但是任何肯定不是列名的变量名都可以。这样,您可以在 DT[...] 内多次使用它,而无需重复表名 DT 和列号,只需按数字多次访问列。 (我们尽量避免符号名称重复,以减少由于拼写错误而导致错误的可能性。)

【讨论】:

  • 我知道使用列号可能很危险,因为它们可能会按时更改。但有时您需要使用它们一次引用许多列,或者因为这些数字来自先前的过滤。我的问题是一个简单的例子,但我正在使用一个包含 1700 个变量的大表。
  • 拜托,你能解释一下..的意思吗?
  • 我试过 ..theCol = DT[[1]] DT[, grep("^ID",..theCol) | !is.na(..theCol)] 但结果是逻辑的(0)
  • @skan grep 返回一个向量索引,长度可能为零。你想要grepl
  • @skan:请阅读 Matt 的 cmets 中提到的 [.data.table 中即将发生的更改,以了解我之前但知识较少的答案。现在可以通过 ":" 运算符使用不带引号的命名列跨度。
【解决方案2】:

一种按数字索引列的 data.table 方法是转换为列名,转换为 R 符号,然后计算:

mydata[ , eval( as.symbol( names(mydata)[1] ) )]
[1] "ID123" "ID22"  "AAA" 

> grep("^ID", mydata[,eval(as.symbol(names(mydata)[1]))])
[1] 1 2

但由于 DT 常见问题解答 #1 以及行号不被视为有效目标这一事实,这并不是一条真正获准的成功之路。哲学(据我了解)是行号是偶然的,您应该使用唯一标识符存储您的记录。

【讨论】:

  • mydata[[1]] 怎么样?
  • 哈。来自大师。但在我看到这个评论的作者之前,我准备说“[[”是一个基于列表的方法,而不是一个 true-data.table 方法。但我很可能是错的。我确实检查了:?'[[.data.table' No documentation for ‘[[.data.table’ in specified packages and libraries: you could try ‘??[[.data.table’
  • [[$data.table 没有特殊的方法,因此没有机制或需要记录。没有什么不同,这是有意的:它只是一个list。在这些情况下,请随时使用[[$。我愿意。 example(data.table)中有例子和评论:“DT[["v"]] # same as DT[, v] but much faster
  • 并且需要明确的是,我只是鼓励 [[$ 将单个列作为向量读取。使用[[&lt;-$&lt;- 分配是完全不同的,我们更喜欢:=
  • 更改#3 欢迎阅读。我完全同意从 j 参数返回单个文字值的旧行为没有实际价值,通常不是新 DT 用户所期望的。对我来说,你很清楚它只用于提取而不是分配。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-05
  • 2016-09-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多