使用 tall 数组处理无法放入内存的数据
tall 数组用于处理由 datastore
支持的无法放入内存的数据。数据存储允许以单个放入内存的小块形式处理大型数据集,而不是一次将整个数据集加载到内存中。tall 数组扩展了此功能,允许使用公共函数处理无法放入内存的数据。
什么是 tall 数组?
由于数据不是一次加载到内存中,因此 tall 数组在第一维中可以是任意大的(即可以具有任意数量的行)。tall 数组允许您以一种直观方式处理大型数据集,这与处理内存 MATLAB® 数组的方式类似;您不必为超大的数据量而专门编写特殊的代码(例如使用 MapReduce 之类的技术)。许多核心运算符和函数能够像处理内存数组一样处理 tall 数组。MATLAB 一次只处理一小块数据,而在后台执行所有的数据分块和处理,因此可以对大型数据集应用常见的运算表达式,如 A+B
。
tall 数组的好处
与内存数组不同,tall 数组通常不执行计算,直到您使用 gather
函数请求执行计算后才会真正计算。这种惰性评估可快速处理大型数据集。当使用 gather
最终请求输出时,MATLAB 尽可能合并排队的计算,并执行最少次数的数据遍历操作。遍历数据的次数会极大地影响执行时间,因此建议仅在必要时才请求输出。
注意
由于 gather
将结果作为内存 MATLAB 数组返回,因此遵循标准内存考量即可。如果 gather
返回的结果太大,MATLAB 可能会耗尽内存。
创建 tall 表
除了可以包含任意数量的行以外,tall 表与内存 MATLAB 常规表并无不同。要基于大型数据集创建 tall 表,首先需要为数据创建数据存储。如果数据存储 ds
包含表格数据,则 tall(ds)
返回包含这些数据的 tall 表或 tall 时间表。有关创建数据存储的详细信息,请参阅数据存储。
创建指向航空公司航班数据表格文件的电子表格数据存储。对于包含文件集合的文件夹,可以指定整个文件夹位置,或使用通配符 '*.csv'
来包括数据存储中具有相同文件扩展名的多个文件。通过将 'NA'
值视为缺失数据来清理数据,以便 tabularTextDatastore
用 NaN
值替换它们。另外,将几个文本变量的格式设置为 %s
,以便 tabularTextDatastore
将它们作为字符向量的元胞数组读取。
ds = tabularTextDatastore('airlinesmall.csv'); ds.TreatAsMissing = 'NA'; ds.SelectedFormats{strcmp(ds.SelectedVariableNames,'TailNum')} = '%s'; ds.SelectedFormats{strcmp(ds.SelectedVariableNames,'CancellationCode')} = '%s';
基于数据存储创建 tall 表。当对此 tall 表执行计算时,基础数据存储读取数据块并将其传递给 tall 表进行处理。数据存储和 tall 表都不保留任何基础数据。
tt = tall(ds)
tt = M×29 tall table Year Month DayofMonth DayOfWeek DepTime CRSDepTime ArrTime CRSArrTime UniqueCarrier FlightNum TailNum ActualElapsedTime CRSElapsedTime AirTime ArrDelay DepDelay Origin Dest Distance TaxiIn TaxiOut Cancelled CancellationCode Diverted CarrierDelay WeatherDelay NASDelay SecurityDelay LateAircraftDelay ____ _____ __________ _________ _______ __________ _______ __________ _____________ _________ _______ _________________ ______________ _______ ________ ________ ______ _____ ________ ______ _______ _________ ________________ ________ ____________ ____________ ________ _____________ _________________ 1987 10 21 3 642 630 735 727 'PS' 1503 'NA' 53 57 NaN 8 12 'LAX' 'SJC' 308 NaN NaN 0 'NA' 0 NaN NaN NaN NaN NaN 1987 10 26 1 1021 1020 1124 1116 'PS' 1550 'NA' 63 56 NaN 8 1 'SJC' 'BUR' 296 NaN NaN 0 'NA' 0 NaN NaN NaN NaN NaN 1987 10 23 5 2055 2035 2218 2157 'PS' 1589 'NA' 83 82 NaN 21 20 'SAN' 'SMF' 480 NaN NaN 0 'NA' 0 NaN NaN NaN NaN NaN 1987 10 23 5 1332 1320 1431 1418 'PS' 1655 'NA' 59 58 NaN 13 12 'BUR' 'SJC' 296 NaN NaN 0 'NA' 0 NaN NaN NaN NaN NaN 1987 10 22 4 629 630 746 742 'PS' 1702 'NA' 77 72 NaN 4 -1 'SMF' 'LAX' 373 NaN NaN 0 'NA' 0 NaN NaN NaN NaN NaN 1987 10 28 3 1446 1343 1547 1448 'PS' 1729 'NA' 61 65 NaN 59 63 'LAX' 'SJC' 308 NaN NaN 0 'NA' 0 NaN NaN NaN NaN NaN 1987 10 8 4 928 930 1052 1049 'PS' 1763 'NA' 84 79 NaN 3 -2 'SAN' 'SFO' 447 NaN NaN 0 'NA' 0 NaN NaN NaN NaN NaN 1987 10 10 6 859 900 1134 1123 'PS' 1800 'NA' 155 143 NaN 11 -1 'SEA' 'LAX' 954 NaN NaN 0 'NA' 0 NaN NaN NaN NaN NaN : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : :
上面显示的结果指示行数 M
当前未知。MATLAB 显示了一些行,垂直省略号 :
则表示 tall 表中还存在许多当前未显示的行。
创建 tall 时间表
如果正在处理的数据有与每一行数据相关联的时间,则您可以使用 tall 时间表来处理这些数据。有关创建 tall 时间表的信息,请参阅扩展功能 (timetable
)。
在本例中,tall 表 tt
具有与每一行相关联的时间,但这些时间分解为几个表变量,例如 Year
、Month
、DayofMonth
等。将所有这些日期时间信息组合为一个基于出发时间 DepTime
的新 tall 日期时间变量 Dates
。然后,使用 Dates
作为行时间创建 tall 时间表。由于 Dates
是表中唯一的日期时间变量,因此 table2timetable
函数自动将其用于行时间。
hrs = (tt.DepTime - mod(tt.DepTime,100))/100; mins = mod(tt.DepTime,100); tt.Dates = datetime(tt.Year, tt.Month, tt.DayofMonth, hrs, mins, 0); tt(:,1:8) = []; TT = table2timetable(tt)
TT = M×21 tall timetable Dates UniqueCarrier FlightNum TailNum ActualElapsedTime CRSElapsedTime AirTime ArrDelay DepDelay Origin Dest Distance TaxiIn TaxiOut Cancelled CancellationCode Diverted CarrierDelay WeatherDelay NASDelay SecurityDelay LateAircraftDelay ____________________ _____________ _________ _______ _________________ ______________ _______ ________ ________ ______ _____ ________ ______ _______ _________ ________________ ________ ____________ ____________ ________ _____________ _________________ 21-Oct-1987 06:42:00 'PS' 1503 'NA' 53 57 NaN 8 12 'LAX' 'SJC' 308 NaN NaN 0 'NA' 0 NaN NaN NaN NaN NaN 26-Oct-1987 10:21:00 'PS' 1550 'NA' 63 56 NaN 8 1 'SJC' 'BUR' 296 NaN NaN 0 'NA' 0 NaN NaN NaN NaN NaN 23-Oct-1987 20:55:00 'PS' 1589 'NA' 83 82 NaN 21 20 'SAN' 'SMF' 480 NaN NaN 0 'NA' 0 NaN NaN NaN NaN NaN 23-Oct-1987 13:32:00 'PS' 1655 'NA' 59 58 NaN 13 12 'BUR' 'SJC' 296 NaN NaN 0 'NA' 0 NaN NaN NaN NaN NaN 22-Oct-1987 06:29:00 'PS' 1702 'NA' 77 72 NaN 4 -1 'SMF' 'LAX' 373 NaN NaN 0 'NA' 0 NaN NaN NaN NaN NaN 28-Oct-1987 14:46:00 'PS' 1729 'NA' 61 65 NaN 59 63 'LAX' 'SJC' 308 NaN NaN 0 'NA' 0 NaN NaN NaN NaN NaN 08-Oct-1987 09:28:00 'PS' 1763 'NA' 84 79 NaN 3 -2 'SAN' 'SFO' 447 NaN NaN 0 'NA' 0 NaN NaN NaN NaN NaN 10-Oct-1987 08:59:00 'PS' 1800 'NA' 155 143 NaN 11 -1 'SEA' 'LAX' 954 NaN NaN 0 'NA' 0 NaN NaN NaN NaN NaN : : : : : : : : : : : : : : : : : : : : : : : :
创建 tall 数组
当从 tall 表或 tall 时间表中提取变量时,其结果是一个具有适当基础数据类型的 tall 数组。tall 数组可以是数字、逻辑、日期时间、持续时间、日历持续时间、分类、字符串或元胞数组。此外,可以按照 tA = tall(A)
条件将内存数组 A
转换为 tall 数组。内存数组 A
必须具有受支持的数据类型之一。
从 tall 时间表 TT
提取到港延误 ArrDelay
。这将创建一个新的 tall 数组变量,其基础数据类型为 double。
a = TT.ArrDelay
a = M×1 tall double column vector 8 8 21 13 4 59 3 11 : :
classUnderlying
和 isaUnderlying
函数可用于确定 tall 数组的基础数据类型。
惰性评估
tall 数组的一个重要特征是,在处理它们时大多数运算不会立即执行。这些运算看起来执行速度很快,是因为实际计算被延迟,直到特别请求时才执行计算。可以使用 gather
函数(将结果放入内存)或 write
函数(将结果写入磁盘)来启动 tall 数组的计算。惰性评估非常重要,因为对一个包含十亿行的 tall 数组执行一个类似 size(X)
的简单命令都是一次耗时的计算过程。
在处理 tall 数组时,MATLAB 跟踪要执行的所有操作。然后,此信息用于优化在使用 gather
函数请求输出时需要的遍历数据次数。因此,通常情况下应使用未经计算的 tall 数组,并仅在需要时请求输出。有关详细信息,请参阅 Lazy Evaluation of Tall Arrays。
计算到港延误的均值和标准差。使用这些值来构建在均值的一个标准差内的延误的上限和下限阈值。请注意,每个运算的结果表示该数组尚未真正计算。
m = mean(a,'omitnan')
m = tall double ? Preview deferred. Learn more.
s = std(a,'omitnan')
s = tall ? Preview deferred. Learn more.
one_sigma_bounds = [m-s m m+s]
one_sigma_bounds = M×N×... tall array ? ? ? ... ? ? ? ... ? ? ? ... : : : : : : Preview deferred. Learn more.
用 gather
计算
惰性评估的好处是,当到了 MATLAB 执行计算的时间,通常可以合并多个运算,这样能最大限度地降低遍历数据的次数。因此,即使您执行大量运算,MATLAB 也仅在绝对必要时才会对数据进行额外次数的遍历。
gather
函数强制执行所有排队的运算,并将生成的输出载入内存中。因此,您可以考虑将 gather
作为 tall 数组和内存数组之间的桥梁。例如,您不能使用 tall 逻辑数组控制 if
或 while
循环,但一旦使用 gather
计算该数组后,它就成为内存逻辑数组,从而可用于上述控制目的。
由于 gather
在 MATLAB 中返回整个结果,因此应确保结果可放入内存中。
使用 gather
计算 one_sigma_bounds
并将结果放入内存中。在本例中,one_sigma_bounds
需要几个运算来计算,但 MATLAB 将这些运算合并在一次遍历数据中进行。由于此示例中的数据较小,因此 gather
可快速执行。然而,随着数据大小的增加,消除数据遍历会变得更有价值。
sig1 = gather(one_sigma_bounds)
Evaluating tall expression using the Local MATLAB Session: - Pass 1 of 1: Completed in 1.5 sec Evaluation completed in 1.8 sec sig1 = -23.4572 7.1201 37.6975
如果要同时计算几个 tall 数组,可以给 gather
指定多个输入和输出。这种方法比多次调用 gather
更快。例如,计算最小和最大到港延误。如果单独计算,则计算每个值都需要遍历一次数据,总共两次。然而,同时计算两个值则仅需要一次数据遍历。
[max_delay, min_delay] = gather(max(a),min(a))
Evaluating tall expression using the Local MATLAB Session: - Pass 1 of 1: Completed in 1.1 sec Evaluation completed in 1.1 sec max_delay = 1014 min_delay = -64
这些结果表明,大多数航班平均延误约 7 分钟。但是在一个标准差之内,航班最多可能延误 37 分钟或早到 23 分钟。数据集中最快的航班大约早到一小时,而最迟的航班延误了许多小时。
保存、加载和检查 tall 数组
save
函数保存 tall 数组的状态,但不复制任何数据。生成的 .mat
文件通常很小。但是,原始数据文件必须保留在原位置,以便以后使用 load
。
write
函数创建数据的副本,并将副本保存为文件集合,这可能会占用大量磁盘空间。write
对 tall 数组执行所有待处理运算,以在写入前计算出值。一旦 write
完成数据复制,就与原始裸数据再无关联。因此,即使原始裸数据不再可用,也可以基于已写入的文件重新创建 tall 数组。
您可以创建一个指向文件写入位置的新数据存储,基于已写入的文件重新创建 tall 数组。此功能支持创建 tall 数组数据的检查点或快照。创建检查点是保存数据预处理结果的好方法,这样数据就能以更有效的方式加载。
如果有一个 tall 数组 TA
,则可以用以下命令将它写入文件夹 location
:
write(location,TA);
稍后,要基于写入的文件重新构造 TA
,请使用命令:
ds = datastore(location); TA = tall(ds);
此外,可以使用 write
函数触发 tall 数组的计算,并将结果写入磁盘。write
的这种用法类似于 gather
,但 write
不会将任何结果写入内存中。
支持函数
大多数核心函数采用与处理内存数组相同的方法处理 tall 数组。但在某些情况下,函数处理 tall 数组的方法可能较为特殊,或存在局限性。可以通过查看函数参考页底部的扩展功能部分(例如,查看 filloutliers
),判断函数是否支持 tall 数组,以及是否具有任何限制。
有关支持 tall 数组的所有 MATLAB 函数的过滤列表,请参阅函数列表(tall 数组)。
有几个工具箱也支持 tall 数组,您可以使用这些工具箱完成诸如编写机器学习算法、部署独立 App 以及并行运行或在集群上运行计算之类的任务。有关详细信息,请参阅Extend Tall Arrays with Other Products。
另请参阅
gather
| tall
| datastore
| table
| mapreducer