Data.table: 不一致的理智,像 data.frame 一样容易转换

创建于 2015-06-21  ·  32评论  ·  资料来源: Rdatatable/data.table

(与马特简短讨论后)

行为with=FALSE

require(data.table)
DT = data.table(x=1:5, y=6:10, z=11:15)

DT[, c("y", "z"), with=FALSE]

在与同事交谈,在会议上或通过电子邮件时,似乎仅在j是整数/字符向量的情况下恢复 data.frame 行为只会带来更多的理智(交易不一致)。

问题是data.table用法经常围绕[展开,因此用户必须尽早了解这种差异,并且必须为已知的基本操作学习新语法坐好。 它似乎也无助于解释 data.table 如何是具有此基本操作的 data.frame。

AFAICT,在j中只有字符/整数向量并没有真正的用途。 因此,最好不要with=FALSE并且能够以 data.frame 方式对列进行子集化:

DT[, c("y", "z")]
DT[, 2:3]

在只有一列和使用drop=FALSE的情况下,向量的默认返回也应该恢复。 这将有助于快速了解基本 data.frame 的用法,而不必怀疑“为什么”,并开始学习 data.table 提供的实际基本增强性。

也很高兴听到其他用户的想法。

这之前出现过(由马特提出): http :

enhancement internals

所有32条评论

+1MM

我也喜欢这个主意。 从之前的回复来看,最大的缺点似乎是引入了不一致,因为新用户会期望下面的两种方法返回相同的结果。

DT[,c("colA","colB")] 

colvars = c("colA","colB") 
DT[,colvars]

有没有办法他们都可以返回相同的结果?

@jrowen谢谢。 是的,它们都应该返回相同的结果(就像 data.frame 一样)。
尽管如此,还是要多考虑一下。

x <- "z"
DT[, x]

在这种情况下它会模棱两可,不是吗?

我头顶的一种方法是增强性启动j ,只有当它用.()list()包裹时,但也许这太大了设计变更...

嗯,现在我在想这是否只会造成更多问题:-(

也许您可以使错误消息更友好并帮助用户。 或者甚至找到案例并添加“with = FALSE”并告知用户已进行更改(例如以“旧”方式设置列名称)。 我已经使用 data.table 一年半了,我不时地想使用列号进行一些快速的交互式工作并得到一个错误。 输入 = FALSE 没什么大不了的,但欢迎提供一个很好的提醒。 这也将有助于教授新用户。

我不知道。 它可能只会让人们更难学习。 我同意 Mark 的观点,即添加一个令人沮丧的警告会对此有所帮助。

如果您让这种访问列的方式过于突出,则可能证明是一种滑坡。 你真的能做到这一点而不做这些吗?

  • 允许数字向量(在 data.frames 中被截断为floor(j)
  • 使DT[int_or_char]匹配 data.frame 模拟(它像列表一样对 DT 进行子集)

_Aside:_ 如果你这样做,也许你可以为j添加一些更快的访问器(就更短的代码而言),类似于我上一个要点中的列表子集。 我发现with=FALSE尴尬和冗长,所以一直在做像[.listof`(DT,int_or_char)` (broken in R 3.2.0 onward) and [.noquote (DT,int_or_char)这样的变通方法。 像这样的功能将允许新功能的有经验的用户避开 Mark 建议的警告并编写更清晰、更易读的代码(因为在审查他们的代码时,他们不必怀疑他们是否正在查看 data.table-或 data.frame-style j )。

_EDIT_:我试图在这里解释我的意思: http ://chat.stackoverflow.com/transcript/message/24012297#24012297

我很喜欢自动with=FALSE猜测,但不喜欢drop
复职——我不想看到那个可怕的选择复活
data.table的水弄混了。

我同意 eduard,drop= true 是 data.frame 中最糟糕的部分之一。 我认为实现 with=false 是有道理的,因为这可以提高一致性并且不会实质性地降低数据表的质量,但是 drop=true 只是为了一致性而实施一个坏主意。

从我的iPhone发送

在2015年6月22日,在5:22,爱德华[email protected]写道:

我很喜欢自动with=FALSE猜测,但不喜欢drop
复职——我不想看到那个可怕的选择复活
data.table的水弄混了。

2015 年 6 月 21 日星期日上午 10:37 franknarf1 [email protected]写道:

我不知道。 它可能只会让人们更难学习。 我同意
与马克说,添加一个令人沮丧的警告将有助于解决这个问题。

如果您过多地强调这种访问列的方式,则可能
证明是一个滑坡。 你真的可以这样做吗
做这些?

  • 将 drop=TRUE 设置为默认值
  • 允许数字向量(在
    数据框)
  • 使 DT[int_or_char] 匹配 data.frame 模拟(它在哪里
    子集 DT 就像一个列表)

_Aside:_ 如果你这样做,也许你可以为 j 添加一些更快的访问器
(就较短的代码而言),类似于我上次中的列表子集
要点。 我发现 with=FALSE 既笨拙又冗长,所以一直在做
变通方法如 [.listof (DT,int_or_char)(broken in R 3.2.0 onward) and [.noquote (DT,int_or_char). A function like this would allow experienced users of the new functionality to sidestep the warning Mark suggested and to write clearer, more readable code (since, on reviewing their code, they wouldn't have to wonder whether they were looking at data.table- or data.frame-stylej )。


直接回复此邮件或在 GitHub 上查看
https://github.com/Rdatatable/data.table/issues/1188#issuecomment -113915592
.


直接回复此邮件或在 GitHub 上查看。

我认为如果您不将drop=TRUE用于这些字符或整数情况,它就会违背更改的目的。 如果使用 data.table 语法DT[,.(mycol)] ,保留drop=FALSE ,当然; 我认为改变这种情况不会有任何帮助。

@franknarf1我不同意。 drop参数仅与单列检索相关,因此没有它只会影响部分情况,并且它对这些情况的影响是一致性之一,而不是奇怪有时这有时data.frame行为

@eantonya是的,我想我们确实不同意; 对不起,如果我重复自己的话,但我会尽力澄清。 我对 data.frame 的有时-有时-有时-那种行为也不感到疯狂,但是这个提议的增强的前提是应该在一定程度上支持 data.frame 语法。

在这个有限的范围内(当j (1) 不使用DT任何列并且 (2) 计算为字符或整数......或类似的东西时),我们应该给人们什么他们期待。 它不像你我不会用它,那有什么害处? 如果我们不给他们期望的东西,为什么要给他们让步呢? 他们仍然有理由抱怨不一致。 (我不会使用它,因为我希望能够阅读我的代码,而无需考虑是否使用 data.frame 语法。)

@franknarf1也许我应该澄清

理想情况下,我想要的是j中的 data.frame 语法来完成 data.frame 语法所做的一切,如下所示:

DT[, 1:2]
DT[, c("x", "y")]

cols = c("x", "y")
DT[, cols]

所有这些都应该返回两列data.table。

然而,正如@jrowen在旧帖子中指出的那样,最后一个案例很棘手(对于我在上一篇文章中展示的案例)。 除非可以很好地处理这种情况,否则我个人认为实现此功能没有巨大的优势。 我可以想象自己向初学者(或在谈话中)解释了太多 ifs-and-buts 的行为......这无济于事。

所以,最好是弄清楚是否有办法绕过最后一个场景而不会破坏太多东西。 以及是否值得。

我对drop = .是否存在没有强烈的感觉。 IMO 这不是本次讨论的主要部分,至少在我们将实施此功能明确之前。

我也完全了解DT[3:4]DF[3:4] ,但这似乎根本不是问题..(在 SO,或 r-help 或此处或data.table-help)AFAICT。

@arunsrinivasan是的,我也没有看到功能更改带来的好处。 正如您所说,这似乎会使解释语法变得更加困难,并导致到处都是混乱的代码(因为人们开始使用 data.frame 语法作为拐杖)。

_回到我身边(在你的最后一句话中提到)_。 是的,我从未见过其他人抱怨DT[1:3] vs DF[1:3] ,但也许他们应该! 真的,如果我们有这个线程中提到的功能,以便DT[.SDcols=1:3]DT[.SDcols=c("a","b")]像我的直觉所暗示的那样工作,那将非常方便。 这是题外话,因为对于不想学习 data.table 语法的人来说,这种变化不会是任何形式的拐杖。 不确定这是否已经是 FR... 哦,刚刚找到它: https :

@arunsrinivasan我实际上没有看到某些情况下不起作用的大问题。 我认为这是 _guessing_ with=FALSE ,有时猜错是可以的。 也许可以在猜测的同时打印警告消息,类似于melt/dcast make 的猜测。

@franknarf1我不确定你的意思 - 当然我自己会使用这个功能 - 我经常使用with=FALSE ,并且希望不必输入它。

我从中看到这一变化的框架是增强每个人对data.table使用,并且强调不是试图模仿data.frame作用。 从这个角度来看,添加drop破坏高级用户的使用,因为我认为对于初学者来说,这是一个非常小的短期收益和长期损失。 而with=FALSE猜测是对每个人的短期和长期增强。

@eantonya我的错误。 我会发现在我的代码中使用该功能非常难以解析(通过肉眼)。

就增强功能而言(不包括模仿),Richardo 的DT[.SDcols=1:3]不是更好地实现了(上面链接,问题 1149)?

我没有反对该选项的任何内容(并且我认为无论这个选项如何,它都应该起作用),但更喜欢输入DT[, 1:3]因为它的输入较少。

至于如何猜测 - 我会提出以下建议 - 如果j中的任何名称包含列名或任何特殊点符号( .SD等),则不要不猜。 否则尝试在外部环境中评估表达式 - 如果成功并返回字符/整数/数字向量 - 然后猜测with=FALSE 。 否则回到我们现在做的事情。

我认为这可以解决上述情况以及我现在能想到的更多情况。

再想一想 - 两次评估 smth 是相当危险的,所以无论评估结果是什么,也许都可以接受(所以返回字符/整数/数字和实际结果的列,否则)。

@eantonya我不太熟悉解析 R 调用,但听起来像这样的情况:

DT <- data.table(a1=1:2, a2=3:4, a3=5:6)
suff = 2
DT[,mean(get(paste0("a",suff)))] # 3.5

suffy = 3
DT[,plot(get(paste0("a",suff)),get(paste0("a",suffy)))] # plots a2 v a3

将不再有效,因为j找不到任何名称......?

如果实现了一些猜测的方式,也许可以将它变成一个选项, datatable.guesswith ,默认情况下关闭,但推荐给与 data.frame 语法密切相关的人。

好的,让我们将get到包含.SD和朋友的列表中。 它不适用于哪些其他情况? 让我们看看是否容易对表达式进行分类。

好的,我会看看我是否想到或遇到任何其他人。 除了mget (我不知道如何在这里实际使用)和eval ,什么都没有想到,比如

str  = paste0("a",suff)
expr = parse(text=str)
DT[,eval(expr)]

上面的评论很棒。 为了将它们整合在一起,我认为我们应该进行以下更改。 如果我没看错,我认为(希望!)这会让每个人都满意而不会让任何人不高兴。

  1. 在评估之前检查 j (无论如何都是这样做的)。 如果它是单个数字或单个字符串,则将假定 with=FALSE。 然后这些将起作用:
    DT[,1]
    DT[,"someCol"]
    无论如何,这些现在没有任何用处,因此不会破坏现有代码。 在这两种情况下,将返回单列 data.table,与 'with=FALSE' 和删除 'drop' 一致。 获得单列 data.table(与 data.frame 不同)的可能惊喜不太可能令人不安,特别是因为该列将打印得很好(顶部和底部 5 行)而不是填充控制台的长向量。
  2. 如果 j 是单个符号,它将像往常一样将该列作为向量返回。 但是,如果缺少该列名,则会引发新错误(无论如何现在都不会做任何有用的事情,因此新错误不会破坏现有代码)。
    DT[, existingCol] # 像以前一样将列作为向量返回
    DT[, missingCol]
    错误:j([...] 中的第二个参数)是一个不是列名的单一符号。 在 data.table 中,j 在其范围内进行评估。 如果missingCol是调用范围内包含列名或数字的变量,则加上with=FALSE; 即 DT[, missingCol, with=FALSE]。 与 data.frame 的这种差异是经过深思熟虑的,并在 FAQ 1.1 中进行了解释 . 它允许更高级的用法:参见 ?data.table 中的示例。
  3. 如果 j 不包含任何符号(例如调用 c()、 : 、paste()、paste0() 并且只有列号或字符串),评估它并期望结果是一个数字或字符向量。 然后设置为=FALSE。 然后这些将起作用:
    DT[, c(1:10, 50)]
    DT[, c("ColA","ColB")]
    DT[, paste0("V",20:25)]
  4. 否则,当前行为。

我们总是可以在以后走得更远,这取决于它的进展情况。 我们将等待到目前为止评论过的每个人在继续之前确认(并且只有在 1.9.6 之后(最终)在 CRAN 上!)

对我来说似乎很好。 一如既往,感谢您和 Arun 的辛勤工作。

对我来说听起来很棒。

为了减少 _inconsistency_,最好删除with参数的默认值,或者使其成为NULL / logical(0) / NA ,这将对应于 _guess_。 然后明确使用with=TRUE仍然能够覆盖新的提议行为。 因此,只有在未提供with参数时,更改才会集中在猜测参数上。

@jangorecki是的好主意 - 同意。

我也赞成修改后的提案。

这里有一个稍微不同的观点。 在某些地方,我非常喜欢将 DT 分派到一些期待 DF 的旧代码中,并自动获得 merge() 速度的重大改进(可以想象,对于涉及分组的其​​他操作)。 不幸的是, [ 的非 DF 行为通常不起作用,有时我最终会在 DT 和 DF 之间来回切换,以保持旧代码正常运行。

一个干净的解决方案是 setcompatibility(c("on","off,"?"))。

off 为那些想要充分利用 DT 功能的人提供“本地 DT”行为。

on 提供“本机 DF”行为,除非该操作显然对 DF 没有意义。 例如,我不认为你可以拥有 DF[DF,] 所以这样的事情显然会调用一个 DT 风格的连接。

可以想象,可能存在其他兼容性级别,例如几乎没有下降的 DF。

由于这将是一个 setxxx 参考它也希望有很少的性能成本。

@ronhylton你的评论比讨论的话题范围更广。 最好将其隔离为新的 FR。 例如,可以通过将with默认值实现为getOption("datatable.with")等来管理所讨论的j兼容性检测。

@ronhylton同意 Jan - 最好提出一个新问题。 一种选择是将旧代码放在一个包中,然后在传递 data.table 时它会自动转向基本语法。

在过去一年左右的时间里,我已经访问了这个线程 3 次,那是我开始使用data.table 。 每次都是因为我忘记了with = FALSE选项。 每次我读到这个帖子时,我都想“啊,是的,真的,必须记住这一点”,但不知何故我没有。

data.table是一个很棒的包。 我对这个主题的 2 美分:我不太关心与data.frame兼容性/一致性。 如果它在那里,很好(1 块石头,2 只鸟),但不应该仅仅为了它而存在一致性,如果该功能是可取的,它应该在那里。 对我来说,最重要的是dt[i,j]是一种非常非常直观的访问数据的方式,它几乎是已经存在了几个世纪(或至少一个世纪)的

“r data.table subset by column and row”的热门搜索结果之一是此页面: http ://personal.colby.edu/personal/m/mgimond/RIntro/04_Manipulating_data_tables.html,其中指出“例如,要访问第 23 行和第 4 列的dat单元格值,请键入dat[23, 4] ",因为这是 _everyone_ 所期望的。

对于data.table用户来说,这是一个真正的绊脚石,这是一个很好的解决方案。 高超! 在这里留下注释,因为我在相关 SO 答案之后

[.data.table (mba, , i) 中的错误:
j([...] 中的第二个参数)是单个符号,但未找到列名 'i'。 也许您打算使用 DT[,..i] 或 DT[,i,with=FALSE]。 与 data.frame 的这种差异是经过深思熟虑的,并在 FAQ 1.1 中进行了解释。

如此烦人的错误,不让我编写文件,我编码很差,这让我更加畏惧。

你好。

我注意到我的一些旧代码由于此更改而不再起作用。

例如,此代码正在缩放 mycols 定义的列中包含的值。

mycols <- c("A", "B", "C")
myDT[,scale(mycols)]
or
myDT[paste0("z",mycols) := scale(mycols) ]

但现在它不起作用。
以下行也不起作用。
myDT[,scale(..mycols)]
添加 with=FALSE 不能解决问题。

我需要做这样的事情:
myDT[,scale(.SD), .SDcols=esca ]
myDT[,lapply(.SD[,..esca], scale) ]
myDT[, paste0("z",names(.SD)) := scale(.SD) , .SDcols=mycols]

有没有更好的办法?

@skanskan
请开始一个新问题并链接到这个问题。 很难跟踪已关闭问题中的评论。 请包括数据以创建myDT和输出。 我试图按照你写的,但我不能没有数据,输入和输出显示: http :
谢谢,马特

此页面是否有帮助?
0 / 5 - 0 等级