data.table中的(合理的)默认值似乎是尽可能地保留列表中向量的矩形性质,必要时重复单元素项,然后将结果强制转换为data. table。
假设您有一个数据表foo
foo <- data.table(
a = c(1, 2, 3),
b = c(TRUE, TRUE, FALSE)
)
字符串
你想要的是返回一个非data.table命名的(不规则的)列表,像这样:
list(
a = foo[(b), a],
n = foo[(b), .N]
)
#$a
#[1] 1 2
#n
#[1] 2
型
我一直在用不同的方法处理列表的列表,但我无法阻止data.table将j中返回的内容强制转换到data.table中(如果它有能力这样做的话)。例如:
foo[(b), {
.(a = a,
n = .N)
}]
型
返回一个data.table,其中有两行,列名为a
和n
。
将其嵌套在另一个列表中,我将列表作为一个列返回,并且没有名称:
foo[(b),
.(.("a" = a,
"n" = .N))
]
# V1
# 1: 1,2
# 2: 2
型
无论如何,我觉得我错过了一些东西,但我已经用尽了搜索和阅读文档的方法。
我还想知道在索引结果列表时,拐点可能在哪里,因为也许这并不重要。如果我在列表中有一个长度为1000万的向量,以及100个长度为1的向量,我假设ragged列表在内存方面比data. table更有效。但是除了内存问题,这两个选项中的一个在计算上更有效:
bar_list$singleInt
# vs
bar_dt[1, singleInt]
型
编辑:澄清最后一个问题
library(data.table)
set.seed(123)
foo <- data.table(a = sample(1:100, 1e8, TRUE),
b = sample(c(TRUE, FALSE), 1e8, TRUE))
在我看来,给出了答案,我现在有三个潜在的对象可以包含我想要的信息。
有一个参差不齐的list
(无论我如何到达那里)
bar_list <- list(
a = foo[(b), a],
n = foo[(b), .N],
s = foo[(b), sum(b)],
m = foo[(b), max(a)]
)
# List of 4
# $ a: int [1:50005713] 9 84 37 56 63 60 55 74 99 32 ...
# $ n: int 50005713
# $ s: int 50005713
# $ m: int 100
这里有一个例子,这几乎就像你在转置列表。你生成了一个data.table,其中的列是命名的列表元素,正如@jblood94所建议的那样。(这对长向量有一个不幸的副作用,它们现在需要列表索引来访问它们,但在其他方面是一个有吸引力的选择。)
bar_dt <- foo[(b),
.(a = .(a),
n = .N)]
# Classes ‘data.table’ and 'data.frame': 1 obs. of 2 variables:
# $ a:List of 1
# ..$ : int 9 84 37 56 63 60 55 74 99 32 ...
# $ n: int 50005713
然后还有这个,它允许data.table
通过重复单个元素向量来将列表强制转换为传统的矩形形式,使它们与其他向量具有相同的长度。
bar_dt2 <- foo[(b),
.(a = a,
n = .N,
s = sum(b),
m = max(a))]
# Classes ‘data.table’ and 'data.frame': 50005713 obs. of 4 variables:
# $ a: int 9 84 37 56 63 60 55 74 99 32 ...
# $ n: int 50005713 50005713 50005713 50005713 50005713 50005713 50005713 50005713 50005713 50005713 ...
# $ s: int 50005713 50005713 50005713 50005713 50005713 50005713 50005713 50005713 50005713 50005713 ...
# $ m: int 100 100 100 100 100 100 100 100 100 100 ...
访问元素的基准测试内存方面,bar_list
和bar_dt
是等价的(此处为190.7 MB),而bar_dt2
要大得多(762.9 MB)。我们有两种我们感兴趣的索引类型:索引像n
这样的单元素向量,和索引像a
这样的长向量。从你的microbenchmark@jblood94没有太大的变化。最快的方法仍然是列表,避免了data.table开销。
microbenchmark::microbenchmark(
bar_list$n,
bar_dt[1, n],
bar_dt[, n],
bar_dt$n,
bar_dt[["n"]],
bar_dt[[2]],
bar_dt2[1, n],
check = "identical"
)
# Unit: nanoseconds
# expr min lq mean median uq max neval
# bar_list$n 200 500 940 850 1100 8700 100
# bar_dt[1, n] 237000 270450 371931 324100 465800 898100 100
# bar_dt[, n] 237100 278500 378559 340950 465800 954300 100
# bar_dt$n 800 1700 2961 2700 3250 19700 100
# bar_dt[["n"]] 5000 7750 10832 9850 12100 45200 100
# bar_dt[[2]] 5000 6950 10125 9050 11300 44400 100
# bar_dt2[1, n] 238800 278950 381454 338700 418350 925300 100
矩形bar_dt2
唯一的优点是它在这里稍微快一点,因为它不必用[[
索引,但列表仍然更快。
microbenchmark::microbenchmark(
bar_list$a,
bar_dt[, a[[1]]],
bar_dt$a[[1]],
bar_dt[["a"]][[1]],
bar_dt[[1]][[1]],
bar_dt2[, a],
bar_dt2$a,
bar_dt2[["a"]],
bar_dt2[[1]],
check = "identical"
)
# Unit: nanoseconds
# expr min lq mean median uq max neval
# bar_list$a 200 600 1966 1500 2600 19300 100
# bar_dt[, a[[1]]] 271900 420600 559898 537650 675300 1068900 100
# bar_dt$a[[1]] 1100 1900 5285 3700 6700 26500 100
# bar_dt[["a"]][[1]] 6600 8350 16123 10950 16650 70900 100
# bar_dt[[1]][[1]] 6100 8100 18479 11350 23750 91800 100
# bar_dt2[, a] 47781800 57865500 86941905 64417200 71424750 326081400 100
# bar_dt2$a 900 1500 4130 2250 5100 30600 100
# bar_dt2[["a"]] 5900 8100 18919 11550 26550 91700 100
# bar_dt2[[1]] 5800 8100 19484 11950 19300 282600 100
所以这回答了我最初的问题--当涉及到使用它的效率时,似乎有理由更喜欢一个不规则列表。然而,这引出了第二个问题,那就是创建哪一个更有效,效率有多高,因为如果bar_dt2
的创建比任何获取不规则列表的方法都要有效得多,则bar_list$a
和bar_dt$a
之间的小差异可能无关紧要。
基准列表或数据.表创建
坚持使用microbenchmark
,但只运行一次。
microbenchmark::microbenchmark(
list1 <- with(
foo[(b),],
list("a" = a,
"n" = length(a),
"s" = sum(b),
"m" = max(a))),
list2 <- unlist(
foo[(b),
.(a = .(a),
n = .N,
s = sum(b),
m = max(a))], FALSE),
list3 <- list(
"a" = foo[(b), a],
"n" = foo[(b), .N],
"s" = foo[(b), sum(b)],
"m" = foo[(b), max(a)]),
list4 <- foo[(b),
.(a = .(a),
n = .N,
s = sum(b),
m = max(a))],
times = 1
)
# Unit: seconds
# min lq mean median uq max neval
# 1.263952 1.263952 **1.263952** 1.263952 1.263952 1.263952 1
# 1.928070 1.928070 1.928070 1.928070 1.928070 1.928070 1
# 4.501306 4.501306 4.501306 4.501306 4.501306 4.501306 1
# 1.800121 1.800121 1.800121 1.800121 1.800121 1.800121 1
**t1; dr:**看起来使用with
结构是最快的,比之前快了很多,而(正如预期的那样)通过反复调用foo
生成的列表要慢得多。由于这会自动生成一个不规则的列表,这也是访问其内容的最快方法,我认为这个结构对我来说是赢家。我绝对不会考虑这样做,所以我很高兴我问了这个问题。
2条答案
按热度按时间lymnna711#
如果
j
参数是list
、data.frame
或data.table
,则data.table
将返回data.table
。但data.table
获取不规则列表的方法是将其保留为data.table
:字符串
然而,
data.tables
是向量列表,所以如果你真的想要一个非data.table
的list
:型
至于从单行
data.table
和list
访问元素的问题,data.table
增加了一些额外的开销,所以list
访问速度会更快:型
这与文件一致:
DT“v” #与DT[,v]相同,但速度更快
wgx48brx2#
字符串
它的名字是
V1
,因为外部的.(..)
意味着返回一个新的列表/表,但是由于您没有命名任何直接内部的参数,所以它自己命名。型
我还在R示例中设置了
options(datatable.print.class=TRUE)
(默认情况下),这允许我们看到这是一个列表列(正如你可能怀疑的那样)。这是一个列表,因为摘要中的 inner.(.)
。不幸的是,
data.table
默认将j=
参数中的任何list
-return转换为data.table
(为了方便起见,.(.)
是list(.)
的同义词)。型
注意,我们不能再使用
.N
特殊符号,尽管在这里使用length(a)
非常便宜。