如何使用非标量大小的变量预分配 table?
How to pre-allocate a table with non-scalar sized variables?
出于各种原因,当我遇到以下挑战时,我正在尝试使用 tables
作为常规数值数组的替代品:如何(预)分配非 table标量变量?
给定一个循环:
function A = myfun(...)
N = large number
A = zeros(N,4);
for i = 1:N
do stuff
A(i,:) = [scalar, vector];
end
我想 return 一个带有命名变量的 table。
我可以简单地重写它说:
function T = myfun2(...)
N = large number
A = zeros(N,4);
for i = 1:N
do stuff
A(i,:) = [scalar, vector];
end
T = table(A(:,1), A(:,2:end),'VariableNames',{'scalar','vector'});
这显然会产生格式为 table 的
T =
N×2 table
scalar vector
______ ___________
0 0 0 0
0 0 0 0
0 0 0 0
... ...
现在,如果我想预分配输出 table 并在每次迭代时更新它,我会尝试以下方法:
function T = myfun3(...)
N = large number
T = table('Size',[N,2],...
'VariableTypes',{'double','double'},...
'VariableNames',{'scalar', 'vector'});
for i = 1:N
do stuff
T(i,:) = {scalar, vector};
end
myfun3
的问题是T的格式是:
T =
N×2 table
scalar vector
______ ______
0 0
0 0
0 0
很明显变量 'vector' 现在是标量而不是 array/vector。从 table
文档中读到 'size' 类型的预分配似乎不能接受数组大小?
Q1:如何使用非标量变量预分配 table
?
Q2:如果 myfun2
中的 A 很大,是开销不好还是这是一个 acceptable 解决方案?
我担心索引 into/out-of 和 table 的额外开销与数值数组相比非常大,会对性能代码产生不利影响。
======= 编辑 =======
我联系了 MathWorks,他们确认从 MATLAB R2019b 开始,无法使用 size
参数实现 Q1。
您可以在 for 循环之前创建 table,然后通过列名访问它:
function T = myfun2(...)
N = large number
A = zeros(N,4);
T = table(A(:,1), A(:,2:end),'VariableNames',{'scalar','vector'});
for i = 1:N
do stuff
T.scalar(i,:) = scalar_i;
T.vector(i,:) = vector_i;
% or in one line: T(i,:) = table(scalar_i, vector_i);
end
我不确定每次迭代创建一点 table 是否有效,所以可能更喜欢一次访问一列。
注意
正如 Juhl 在评论中指出的那样,可能存在使用临时对象创建 table 的双重分配,而对于 'Size' 参数,您可以预期只有一个数据块已分配。
让我们检查一下。在我的电脑上,使用 Matlab 2019a,有:
>> memory
Maximum possible array: 56239 MB (5.897e+10 bytes) *
所以我可以在单个数组中分配 56.239e9 / 8 = 7.0299e9 个元素(知道双精度在 8 个字节上)。让我们四舍五入,假设我想创建一个 table,其中一列超过一半(3.51e9 个元素):
>> T = table(zeros(4e9,1));
>> memory
Maximum possible array: 33644 MB (3.528e+10 bytes)
分配时间较长,但已完成。用'Size',完全一样:
>> T = table(zeros(4e9,1));
>> memory
Maximum possible array: 33677 MB (3.531e+10 bytes) *
看来我们没有双重分配。
有一个有趣的事实:T
占用的内存比我们预期的要少。如果我尝试修改我的 table 的最后一个元素,它消耗的内存似乎达到了预期的内存大小:
>> T.Var1(end) = 1;
>> memory
Maximum possible array: 27574 MB (2.891e+10 bytes)
披露
请注意修改这种table需要时间:
>> tic; T.Var1(end) = 1; toc
Elapsed time is 33.286967 seconds.
所以我的结论是:使用普通数组,速度要快得多:
>> tic; T = table('Size', [4e9, 1], 'VariableTypes',{'double'}); toc
Elapsed time is 15.997680 seconds.
>> tic; T.Var1(end) = 1; toc
Elapsed time is 33.286967 seconds.
>> clear T;
>> tic; A = zeros(4e9,1); toc
Elapsed time is 0.043366 seconds.
>> tic; A(end) = 1; toc
Elapsed time is 0.002430 seconds.
>> clear A;
出于各种原因,当我遇到以下挑战时,我正在尝试使用 tables
作为常规数值数组的替代品:如何(预)分配非 table标量变量?
给定一个循环:
function A = myfun(...)
N = large number
A = zeros(N,4);
for i = 1:N
do stuff
A(i,:) = [scalar, vector];
end
我想 return 一个带有命名变量的 table。
我可以简单地重写它说:
function T = myfun2(...)
N = large number
A = zeros(N,4);
for i = 1:N
do stuff
A(i,:) = [scalar, vector];
end
T = table(A(:,1), A(:,2:end),'VariableNames',{'scalar','vector'});
这显然会产生格式为 table 的
T =
N×2 table
scalar vector
______ ___________
0 0 0 0
0 0 0 0
0 0 0 0
... ...
现在,如果我想预分配输出 table 并在每次迭代时更新它,我会尝试以下方法:
function T = myfun3(...)
N = large number
T = table('Size',[N,2],...
'VariableTypes',{'double','double'},...
'VariableNames',{'scalar', 'vector'});
for i = 1:N
do stuff
T(i,:) = {scalar, vector};
end
myfun3
的问题是T的格式是:
T =
N×2 table
scalar vector
______ ______
0 0
0 0
0 0
很明显变量 'vector' 现在是标量而不是 array/vector。从 table
文档中读到 'size' 类型的预分配似乎不能接受数组大小?
Q1:如何使用非标量变量预分配 table
?
Q2:如果 myfun2
中的 A 很大,是开销不好还是这是一个 acceptable 解决方案?
我担心索引 into/out-of 和 table 的额外开销与数值数组相比非常大,会对性能代码产生不利影响。
======= 编辑 =======
我联系了 MathWorks,他们确认从 MATLAB R2019b 开始,无法使用 size
参数实现 Q1。
您可以在 for 循环之前创建 table,然后通过列名访问它:
function T = myfun2(...)
N = large number
A = zeros(N,4);
T = table(A(:,1), A(:,2:end),'VariableNames',{'scalar','vector'});
for i = 1:N
do stuff
T.scalar(i,:) = scalar_i;
T.vector(i,:) = vector_i;
% or in one line: T(i,:) = table(scalar_i, vector_i);
end
我不确定每次迭代创建一点 table 是否有效,所以可能更喜欢一次访问一列。
注意
正如 Juhl 在评论中指出的那样,可能存在使用临时对象创建 table 的双重分配,而对于 'Size' 参数,您可以预期只有一个数据块已分配。
让我们检查一下。在我的电脑上,使用 Matlab 2019a,有:
>> memory
Maximum possible array: 56239 MB (5.897e+10 bytes) *
所以我可以在单个数组中分配 56.239e9 / 8 = 7.0299e9 个元素(知道双精度在 8 个字节上)。让我们四舍五入,假设我想创建一个 table,其中一列超过一半(3.51e9 个元素):
>> T = table(zeros(4e9,1));
>> memory
Maximum possible array: 33644 MB (3.528e+10 bytes)
分配时间较长,但已完成。用'Size',完全一样:
>> T = table(zeros(4e9,1));
>> memory
Maximum possible array: 33677 MB (3.531e+10 bytes) *
看来我们没有双重分配。
有一个有趣的事实:T
占用的内存比我们预期的要少。如果我尝试修改我的 table 的最后一个元素,它消耗的内存似乎达到了预期的内存大小:
>> T.Var1(end) = 1;
>> memory
Maximum possible array: 27574 MB (2.891e+10 bytes)
披露
请注意修改这种table需要时间:
>> tic; T.Var1(end) = 1; toc
Elapsed time is 33.286967 seconds.
所以我的结论是:使用普通数组,速度要快得多:
>> tic; T = table('Size', [4e9, 1], 'VariableTypes',{'double'}); toc
Elapsed time is 15.997680 seconds.
>> tic; T.Var1(end) = 1; toc
Elapsed time is 33.286967 seconds.
>> clear T;
>> tic; A = zeros(4e9,1); toc
Elapsed time is 0.043366 seconds.
>> tic; A(end) = 1; toc
Elapsed time is 0.002430 seconds.
>> clear A;