如何在data.table::setorder()中使用表达式?询替代方案与问题原因
问题解答
1. setorder()传入表达式是否普遍不可行?
是的,这是普遍情况。data.table::setorder()的参数设计逻辑是优先解析为data.table的列名、列符号或列索引,而非任意表达式。当你传入类似rownames(dt) == "Hornet Sportabout"的表达式时,它会尝试查找名为该表达式字符串的列,自然会因列不存在而报错。这和base R中order()接受任意排序键表达式的逻辑完全不同。
2. 高效使用setorder()的替代方案
如果要保持setorder()原地修改、低内存占用的高效特性,有两种实用方案:
方案一:临时辅助列法
先创建一个临时排序键列,用setorder()排序后再删除该列,全程原地操作:
library(data.table) dt <- as.data.table(mtcars) # 生成临时排序键:目标行标记为0,其余为1 dt[, .sort_key := ifelse(rownames(dt) == "Hornet Sportabout", 0, 1)] # 按临时列升序排序,目标行自然移到顶部 setorder(dt, .sort_key) # 删除临时列 dt[, .sort_key := NULL]
方案二:外部向量+..引用法
先在data.table外部计算好排序用的向量,再用..符号引用该向量传入setorder(),避免被解析为列名:
library(data.table) dt <- as.data.table(mtcars) # 外部计算排序向量:目标行为TRUE,其余为FALSE sort_vec <- rownames(dt) == "Hornet Sportabout" # 用..引用外部向量,负号实现降序(让TRUE行排前面) setorder(dt, -..sort_vec)
如果不介意生成新的data.table,也可以直接用dt[order(rownames(dt) == "Hornet Sportabout", decreasing = TRUE)],但这种方式会复制数据,内存效率不如上述两种原地方案。
3. 这是否是bug?
不是bug。setorder()的设计定位是针对data.table现有列的高效原地排序工具,其参数解析逻辑是为了最大化操作效率,而非兼容base Rorder()的任意表达式语法。两者的设计目标不同:base Rorder()是通用排序函数,接受任意可排序向量;而setorder()是data.table专属的高性能原地排序工具,优先适配列级操作。
内容的提问来源于stack exchange,提问作者TimTeaFan
相关产品推荐
相关产品推荐

