Main Content

本页对应的英文页面已更新,但尚未翻译。 若要查看最新内容,请点击此处访问英文页面。

matlab.tall.reduce

通过对数据块应用归约算法来减少数组

说明

示例

tA = matlab.tall.reduce(fcn,reducefcn,tX) 将函数 fcn 应用于数组 tX 的每个以生成部分结果。然后,该函数将 reducefcn 重复应用于部分结果的垂直串联,直到它得到一个最终结果 tA

示例

tA = matlab.tall.reduce(fcn,reducefcn,tX,tY,...) 指定几个 tX,tY,... 数组,这些数组是 fcn 的输入。fcn 对每个数组的相同行进行运算;例如,fcn(tX(n:m,:),tY(n:m,:))。高度为 1 的输入将传递给 fcn 的每次调用。使用此语法时,fcn 必须返回一个输出,reducefcn 必须接受一个输入并返回一个输出。

示例

[tA,tB,...] = matlab.tall.reduce(fcn,reducefcn,tX,tY,...)(其中 fcnreducefcn 是返回多个输出的函数)返回数组 tA,tB,...,其中每个数组对应于 fcnreducefcn 的一个输出参数。此语法具有以下要求:

  • fcn 返回的输出数目必须与从 matlab.tall.reduce 请求的输出数目相同。

  • reducefcn 具有的输入和输出数目必须与从 matlab.tall.reduce 请求的输出数目相同。

  • fcnreducefcn 的每个输出必须与第一个输入 tX 的输出类型相同。

  • fcnreducefcn 的对应输出必须具有相同的高度。

示例

[tA,tB,...] = matlab.tall.reduce(___,'OutputsLike',{PA,PB,...}) 指定输出 tA,tB,... 分别与原型数组 PA,PB,... 具有相同的数据类型。您可以使用上述语法中的任何输入参数组合。

示例

全部折叠

创建一个 tall 表,从该表中提取 tall 向量,然后求向量中的元素总数。

airlinesmall.csv 数据集创建 tall 表。该数据包含有关美国航班的到港和离港时间的信息。提取 ArrDelay 变量,该变量是到港延误的向量。

ds = tabularTextDatastore('airlinesmall.csv','TreatAsMissing','NA');
ds.SelectedVariableNames = {'ArrDelay' 'DepDelay'};
tt = tall(ds);
tX = tt.ArrDelay;

使用 matlab.tall.reduce 计算 tall 向量中非 NaN 元素的总数。第一个函数 numel 计算每个数据块中的元素数目,第二个函数 sum 将每个块的所有计数相加,以产生标量结果。

s = matlab.tall.reduce(@numel,@sum,tX)
s =

  MxNx... tall double array

    ?    ?    ?    ...
    ?    ?    ?    ...
    ?    ?    ?    ...
    :    :    :
    :    :    :

将结果收集到内存中。

s = gather(s)
Evaluating tall expression using the Local MATLAB Session:
- Pass 1 of 1: Completed in 1.3 sec
Evaluation completed in 1.6 sec
s = 123523

创建一个 tall 表,从该表中提取两个 tall 向量,然后计算每个向量的均值。

airlinesmall.csv 数据集创建 tall 表。该数据包含有关美国航班的到港和离港时间的信息。提取 ArrDelayDepDelay 变量,这些变量是到港和离港延误的向量。

ds = tabularTextDatastore('airlinesmall.csv','TreatAsMissing','NA');
ds.SelectedVariableNames = {'ArrDelay' 'DepDelay'};
tt = tall(ds);
tt = rmmissing(tt);
tX = tt.ArrDelay;
tY = tt.DepDelay;

在算法的第一阶段,计算向量中每个数据块的总和及元素计数。为此,您可以编写一个函数,它接受两个输入项,返回一个输出项,该输出项包含每个输入项的总和及计数。该函数作为局部函数列在示例的末尾。

function bx = sumcount(tx,ty)
  bx = [sum(tx) numel(tx) sum(ty) numel(ty)];
end

在算法的归约阶段,您需要将所有中间总和与计数相加。因此,matlab.tall.reduce 返回每个输入向量的元素总和与元素个数,这样,计算均值就是简单的除法。对于此步骤,您可以将 sum 函数应用于第一阶段 1×4 向量输出的第一个维度。

 reducefcn = @(x) sum(x,1);
 s = matlab.tall.reduce(@sumcount,reducefcn,tX,tY)
s =

  MxNx... tall double array

    ?    ?    ?    ...
    ?    ?    ?    ...
    ?    ?    ?    ...
    :    :    :
    :    :    :
 s = gather(s)
Evaluating tall expression using the Local MATLAB Session:
- Pass 1 of 1: Completed in 1.3 sec
Evaluation completed in 1.6 sec
s = 1×4

      860584      120866      982764      120866

s 的前两个元素是 tX 的总和与计数,后两个元素是 tY 的总和与计数。将总和与计数相除得到均值,您可以将均值与 mean 函数返回的答案进行比较。

 my_mean = [s(1)/s(2) s(3)/s(4)]
my_mean = 1×2

    7.1201    8.1310

 m = gather(mean([tX tY]))
Evaluating tall expression using the Local MATLAB Session:
- Pass 1 of 1: Completed in 0.47 sec
Evaluation completed in 0.62 sec
m = 1×2

    7.1201    8.1310

局部函数

此处列出的是 sumcount 函数,matlab.tall.reduce 调用该函数来计算中间总和与元素计数。

function bx = sumcount(tx,ty)
  bx = [sum(tx) numel(tx) sum(ty) numel(ty)];
end

创建一个 tall 表,然后计算数据中每年的航班延误均值。

airlinesmall.csv 数据集创建 tall 表。该数据包含有关美国航班的到港和离港时间的信息。从该表中删除缺失数据行并提取 ArrDelayDepDelayYear 变量。这些变量是数据集中每个航班到港和离港延误以及相关年份的向量。

ds = tabularTextDatastore('airlinesmall.csv','TreatAsMissing','NA');
ds.SelectedVariableNames = {'ArrDelay' 'DepDelay' 'Year'};
tt = tall(ds);
tt = rmmissing(tt);

使用 matlab.tall.reduce 将两个函数应用于 tall 表。第一个函数合并 ArrDelayDepDelay 变量来求每个航班的总延误均值。该函数确定每个数据分块中有多少唯一年份,然后循环遍历每一年并计算该年中航班总延误的平均值。结果是一个包含年份和总延误均值的二变量表。这些中间数据需要进一步约简,直到得到每年的延误均值。在您的当前文件夹中将此函数另存为 transform_fcn.m

type transform_fcn
function t = transform_fcn(a,b,c)
ii = gather(unique(c));

for k = 1:length(ii)
    jj = (c == ii(k));
    d = mean([a(jj) b(jj)], 2);
    
    if k == 1
        t = table(c(jj),d,'VariableNames',{'Year' 'MeanDelay'});
    else
        t = [t; table(c(jj),d,'VariableNames',{'Year' 'MeanDelay'})];
    end
end

end

第二个函数使用第一个函数的结果来计算每年的总延误均值。reduce_fcn 的输出与 transform_fcn 的输出兼容,因此数据块可以按任意顺序串联和持续约简,直到每年只剩下一行。

type reduce_fcn
function TT = reduce_fcn(t)
[groups,Y] = findgroups(t.Year);
D = splitapply(@mean, t.MeanDelay, groups);

TT = table(Y,D,'VariableNames',{'Year' 'MeanDelay'});
end

将变换和归约函数应用于 tall 向量。由于输入(类型 double)和输出(类型 table)具有不同数据类型,因此使用 'OutputsLike' 名称-值对组来指定输出是表。指定输出类型的一种简单方法是使用虚拟输入调用变换函数。

a = tt.ArrDelay;
b = tt.DepDelay;
c = tt.Year;
d1 = matlab.tall.reduce(@transform_fcn, @reduce_fcn, a, b, c, 'OutputsLike',{transform_fcn(0,0,0)})
d1 =

  Mx2 tall table

    Year    MeanDelay
    ____    _________

     ?          ?    
     ?          ?    
     ?          ?    
     :          :
     :          :

将结果收集到内存中以查看每年的总航班延误均值。

d1 = gather(d1)
Evaluating tall expression using the Local MATLAB Session:
- Pass 1 of 1: Completed in 1.9 sec
Evaluation completed in 2.3 sec
d1=22×2 table
    Year    MeanDelay
    ____    _________

    1987     7.6889  
    1988     6.7918  
    1989     8.0757  
    1990     7.1548  
    1991     4.0134  
    1992     5.1767  
    1993     5.4941  
    1994     6.0303  
    1995     8.4284  
    1996     9.6981  
    1997     8.4346  
    1998     8.3789  
    1999     8.9121  
    2000     10.595  
    2001     6.8975  
    2002     3.4325  
      ⋮

替代方法

按组计算相同统计量的另一种方法是使用 splitapply 调用 matlab.tall.reduce (而不是使用 matlab.tall.reduce 调用 splitapply)。

使用此方法,可以直接对数据调用 findgroupssplitapply。对每组数据进行操作的函数 mySplitFcn 包括对 matlab.tall.reduce 的调用。matlab.tall.reduce 使用的变换和归约函数不需要对数据进行分组,因此这些函数只对 splitapply 向其传递的预分组数据执行计算。

type mySplitFcn
function T = mySplitFcn(a,b,c)
T = matlab.tall.reduce(@non_group_transform_fcn, @non_group_reduce_fcn, ...
    a, b, c, 'OutputsLike', {non_group_transform_fcn(0,0,0)});

    function t = non_group_transform_fcn(a,b,c)
        d = mean([a b], 2);
        t = table(c,d,'VariableNames',{'Year' 'MeanDelay'});
    end

    function TT = non_group_reduce_fcn(t)
        D = mean(t.MeanDelay);
        TT = table(t.Year(1),D,'VariableNames',{'Year' 'MeanDelay'});
    end

end

调用 findgroupssplitapply 对数据进行操作,并将 mySplitFcn 应用于每组数据。

groups = findgroups(c);
d2 = splitapply(@mySplitFcn, a, b, c, groups);
d2 = gather(d2)
Evaluating tall expression using the Local MATLAB Session:
- Pass 1 of 2: Completed in 0.45 sec
- Pass 2 of 2: Completed in 1.5 sec
Evaluation completed in 2.8 sec
d2=22×2 table
    Year    MeanDelay
    ____    _________

    1987     7.6889  
    1988     6.7918  
    1989     8.0757  
    1990     7.1548  
    1991     4.0134  
    1992     5.1767  
    1993     5.4941  
    1994     6.0303  
    1995     8.4284  
    1996     9.6981  
    1997     8.4346  
    1998     8.3789  
    1999     8.9121  
    2000     10.595  
    2001     6.8975  
    2002     3.4325  
      ⋮

使用权重向量计算 tall 数组的加权标准差和方差。以下示例说明如何使用 matlab.tall.reduce 来解决 tall 数组尚不支持的功能。

创建两个由随机数据构成的 tall 向量。tX 包含随机数据,tP 包含对应的概率,以使 sum(tP)1。这些概率适合为数据加权。

rng default
tX = tall(rand(1e4,1));
p = rand(1e4,1);
tP = tall(normalize(p,'scale',sum(p)));

编写一个恒等式函数,它返回的输出等于输入。此方法跳过 matlab.tall.reduce 的变换步骤,将数据直接传递到约简步骤,其中重复应用归约函数以减少数据的大小。

type identityTransform.m

接下来,编写一个对 tall 向量块进行运算的归约函数,以计算加权方差和标准差。

type weightedStats.m
function [wvar, wstd] = weightedStats(X, P)
  wvar = var(X,P);
  wstd = std(X,P);
end

使用 matlab.tall.reduce 将这些函数应用于 tall 向量中的数据块。

[tX_var_weighted, tX_std_weighted] = matlab.tall.reduce(@identityTransform, @weightedStats, tX, tP)
tX_var_weighted =

  MxNx... tall double array

    ?    ?    ?    ...
    ?    ?    ?    ...
    ?    ?    ?    ...
    :    :    :
    :    :    :


tX_std_weighted =

  MxNx... tall double array

    ?    ?    ?    ...
    ?    ?    ?    ...
    ?    ?    ?    ...
    :    :    :
    :    :    :

输入参数

全部折叠

要应用的变换函数,指定为函数句柄或匿名函数。fcn 的每个输出必须与第一个输入 tX 的类型相同。您可以使用 'OutputsLike' 选项返回不同数据类型的输出。如果 fcn 返回多个输出,则这些输出必须具有相同的高度。

fcn 的一般函数签名是

[a, b, c, ...] = fcn(x, y, z, ...)
fcn 必须满足以下要求:

  1. 输入参数 - 输入 [x, y, z, ...] 是可放入内存的数据块。通过分别从各个 tall 数组输入 [tX, tY, tZ, ...] 中提取数据来生成这些数据块。输入 [x, y, z, ...] 满足以下属性:

    • 在任何允许的扩展后,所有 [x, y, z, ...] 在第一个维度中具有相同的大小。

    • [x, y, z, ...] 中的数据块来自 tall 维度中的相同索引(假设 tall 数组的 tall 维度是非单一维度)。例如,如果 tXtY 的 tall 维度为非单一维度,则第一组数据块可能是 x = tX(1:20000,:)y = tY(1:20000,:)

    • 如果任一 [tX, tY, tZ, ...] 的第一个维度的大小为 1,则对应的块 [x, y, z, ...] 包含该 tall 数组中的所有数据。

  2. 输出参数 - 输出 [a, b, c, ...] 是可放入内存的数据块,将被发送到对应的输出 [tA, tB, tC, ...]。输出 [a, b, c, ...] 满足以下属性:

    • 所有 [a, b, c, ...] 的第一个维度必须具有相同的大小。

    • 所有 [a, b, c, ...] 与先前对 fcn 的调用的对应结果垂直串联。

    • 所有 [a, b, c, ...] 都将发送到各自目标输出数组中第一个维度中的相同索引。

  3. 函数规则 - fcn 必须满足函数规则:

    • F([inputs1; inputs2]) == [F(inputs1); F(inputs2)]:将函数应用于输入的串联应该等效于将函数分别应用于各输入,然后对结果进行串联。

  4. 空输入 - 确保 fcn 可以处理高度为 0 的输入。当文件为空或者您对数据进行大量筛选时,可能会出现空输入。

例如,以下函数接受两个输入数组,对它们求平方,并返回两个输出数组:

function [xx,yy] = sqInputs(x,y)
xx = x.^2;
yy = y.^2;
end 
将此函数保存到可访问文件夹后,可以调用该函数以对 tXtY 求平方并使用此命令求最大值:
tA = matlab.tall.reduce(@sqInputs, @max, tX, tY)

示例: tC = matlab.tall.reduce(@numel,@sum,tX,tY) 求每个块中的元素数,然后将结果相加以计算元素总数。

数据类型: function_handle

要应用的归约函数,指定为函数句柄或匿名函数。reducefcn 的每个输出必须与第一个输入 tX 的类型相同。您可以使用 'OutputsLike' 选项返回不同数据类型的输出。如果 reducefcn 返回多个输出,则这些输出必须具有相同的高度。

reducefcn 的一般函数签名是

[rA, rB, rC, ...] = reducefcn(a, b, c, ...)
reducefcn 必须满足以下要求:

  1. 输入参数 - 输入 [a, b, c, ...] 是可放入内存的块。数据块是 fcn 返回的输出,或 reducefcn 的经过部分约简的输出,需要再次进行操作以进一步约简。输入 [a, b, c, ...] 满足以下属性:

    • 输入 [a, b, c, ...] 在第一个维度中具有相同的大小。

    • 对于第一个维度中的给定索引,数据块 [a, b, c, ...] 的每行都来自输入,或者来自之前对 reducefcn 的相同调用。

    • 对于第一个维度中的给定索引,该索引的输入 [a, b, c, ...] 的每行都来自第一个维度中的相同索引。

  2. 输出参数 - 所有输出 [rA, rB, rC, ...] 在第一个维度中必须具有相同的大小。此外,它们必须可以与对应的输入 [a, b, c, ...] 垂直串联,以便在必要时重复约简。

  3. 函数规则 - reducefcn 必须满足以下函数规则(取决于舍入误差):

    • F(input) == F(F(input)):将该函数重复应用于相同的输入不应改变结果。

    • F([input1; input2]) == F([input2; input1]):结果应该与串联顺序无关。

    • F([input1; input2]) == F([F(input1); F(input2)]):将函数应用于某些中间结果的串联应该等效于单独应用函数再进行串联。

  4. 空输入 - 确保 reducefcn 可以处理高度为 0 的输入。当文件为空或者您对数据进行大量筛选时,可能会出现空输入。对于此调用,在除第一个维度以外的维度中,所有输入块均为正确类型和大小的空数组。

sumprodmax 等内置维度归约函数是一些合适的归约函数。这些函数可以处理由 fcn 生成的中间结果并返回单一标量。这些函数具有以下属性,即串联发生的顺序和应用归约运算的次数不会更改最终答案。有些函数,例如 meanvar,通常应避免作为归约函数,因为应用归约运算的次数会更改最终答案。

示例: tC = matlab.tall.reduce(@numel,@sum,tX) 求每个块中的元素数,然后将结果相加以计算元素总数。

数据类型: function_handle

输入数组,指定为标量、向量、矩阵或多维数组。输入数组用作变换函数 fcn 的输入。每个输入数组 tX,tY,... 必须具有兼容的高度。如果两个输入具有相同的高度,或者其中一个输入的高度为 1,则它们具有兼容的高度。

输出数组的原型,指定为数组。当您指定 'OutputsLike' 时,matlab.tall.reduce 返回的输出数组 tA,tB,... 与指定的数组 {PA,PB,...} 具有相同的数据类型和属性。

示例: tA = matlab.tall.reduce(fcn,reducefcn,tX,'OutputsLike',{int8(1)});(其中 tX 是双精度 tall 数组)以 int8(而不是 double)形式返回 tA

输出参数

全部折叠

输出数组,以标量、向量、矩阵或多维数组形式返回。如果 matlab.tall.reduce 的任一输入为 tall,则所有输出参数也为 tall。否则,所有输出参数均为可放入内存的数组。

输出数组的大小和数据类型取决于指定的函数 fcnreducefcn。通常,输出 tA,tB,... 必须与第一个输入 tX 具有相同的数据类型。但是,您可以指定 'OutputsLike' 以返回不同数据类型。输出数组 tA,tB,... 都具有相同的高度。

详细信息

全部折叠

tall 数组块

从数据存储创建 tall 数组时,基础数据存储便于在计算过程中移动数据。数据以分块形式的离散片段移动,其中每个块是一组可放入内存的连续行。例如,二维数组(例如表)的一个块是 X(n:m,:),表示下标为 nm 的一些数据。每个块的大小基于数据存储的 ReadSize 属性的值,但该块不一定完全等于该大小。对于 matlab.tall.reduce 来说,tall 数组被视为许多此类块的垂直串联:

例如,如果使用 sum 函数作为变换函数,则中间结果是每个块之和。因此,结果是长度等于块数的一个向量,而不是针对元素之和返回单一标量值。

ds = tabularTextDatastore('airlinesmall.csv','TreatAsMissing','NA');
ds.SelectedVariableNames = {'ArrDelay' 'DepDelay'};
tt = tall(ds);
tX = tt.ArrDelay;

f = @(x) sum(x,'omitnan');
s = matlab.tall.reduce(f, @(x) x, tX);
s = gather(s)
s =

      140467
      101065
      164355
      135920
      111182
      186274
       21321

在 R2018b 中推出