罗兰谈MATLAB的艺术

将想法转化为MATLAB

基于PCT和MDCS的内存大数据分析

MATLAB产品管理组的Ken Atwell正在撰写关于使用的客座博客分布式对内存中的“大数据”执行数据分析的数组。

大数据应用可以有多种形式。与许多商业和消费者应用中常见的非结构化文本处理不同,科学和工程领域的大数据通常可以采取更“规则”的形式,要么是冗长的观察数据集或时间序列数据,要么是捕获一些问题空间的单一多维矩阵。

64位MATLAB基本上非常适合处理这些类型的数据,只要这些数据适合计算机的内存。当数据量超过一台计算机的能力时,MATLAB分布式计算服务器(MDCS)可以让MATLAB应用程序简单地扩展和利用计算机集群的总内存和计算能力。

这篇博客文章探讨了如何使用MDCS来分析不适合典型桌面计算机内存的表格数据集。亮点包括:

  • 通过并行文件I/O导入来自多个文件的大型数据集,并对该数据集进行后处理
  • 跨聚合数据集执行并行计算
  • 可视化汇总数据集的统计特征
  • 利用MATLAB MAT-Files优化文件I/O性能
  • 在工作人员之间重新平衡数据和工作负载

你需要并行计算工具箱(PCT)访问分布式数组,用于跨集群处理数据存储的数据类型。它的用法几乎与普通的MATLAB矩阵相同,支持简单快速的采用。金宝app分布式阵列的内存特性促进了MATLAB用户所期望的实验和快速迭代工作流。

最后,请注意,这个示例被设计为在大约20 GB的内存中运行。这很难说是“大数据”,但问题的大小已经被有意地减小到足以适合中高端工作站,如果你没有访问集群跟随。

内容

加载数据集

这个例子使用了一个公开的数据集,其中包含了从1987年到2008年20多年间超过1亿的航空公司飞行记录。分别下载年份:

  1. https://s3.amazonaws.com/h2o-airlines-unpacked/year1987.csv
  2. https://s3.amazonaws.com/h2o-airlines-unpacked/year1988.csv
  3. https://s3.amazonaws.com/h2o-airlines-unpacked/year1989.csv
  4. https://s3.amazonaws.com/h2o-airlines-unpacked/year1990.csv
  5. https://s3.amazonaws.com/h2o-airlines-unpacked/year1991.csv
  6. https://s3.amazonaws.com/h2o-airlines-unpacked/year1992.csv
  7. https://s3.amazonaws.com/h2o-airlines-unpacked/year1993.csv
  8. https://s3.amazonaws.com/h2o-airlines-unpacked/year1994.csv
  9. https://s3.amazonaws.com/h2o-airlines-unpacked/year1995.csv
  10. https://s3.amazonaws.com/h2o-airlines-unpacked/year1996.csv
  11. https://s3.amazonaws.com/h2o-airlines-unpacked/year1997.csv
  12. https://s3.amazonaws.com/h2o-airlines-unpacked/year1998.csv
  13. https://s3.amazonaws.com/h2o-airlines-unpacked/year1999.csv
  14. https://s3.amazonaws.com/h2o-airlines-unpacked/year2000.csv
  15. https://s3.amazonaws.com/h2o-airlines-unpacked/year2001.csv
  16. https://s3.amazonaws.com/h2o-airlines-unpacked/year2002.csv
  17. https://s3.amazonaws.com/h2o-airlines-unpacked/year2003.csv
  18. https://s3.amazonaws.com/h2o-airlines-unpacked/year2004.csv
  19. https://s3.amazonaws.com/h2o-airlines-unpacked/year2005.csv
  20. https://s3.amazonaws.com/h2o-airlines-unpacked/year2006.csv
  21. https://s3.amazonaws.com/h2o-airlines-unpacked/year2007.csv
  22. https://s3.amazonaws.com/h2o-airlines-unpacked/year2008.csv

与许多大数据应用程序中的典型情况一样,数据分布在一组中型到大型文件中,而不是单个非常大的文件。这实际上是有利的,允许数据更容易地分布在运行MDCS的集群中。在这个开发阶段,使用一个适度的池(8个工人),并且只使用数据集中的前8个文件——完整的数据集将在本文后面看到。

确定要处理的文件

下载文件后,在MATLAB中查找它们。修改的值dataFolder匹配你下载文件的地方。

关闭所有清晰的所有dataFolder =/卷/ HD2 /航空/数据的;allFiles = dir(fullfile(dataFolder,‘* . csv‘));删除“点”文件(Mac)allFiles = allFiles(~strncmp({allFiles(:).name},“。”1));files = allFiles(1:8);%用于初始开发

检查数据

数据集是逗号分隔的文本。为了在这个特定的示例中保持简单,只读入数据集中的一些列。从文本导入是一项昂贵的操作——将导入减少到只需要的列可以大大加快MATLAB应用程序的速度。首先看原始数据最左边的部分:

如果~ ispc系统(['head -20 'fullfile (dataFolder文件(8). name)' | cut -c -60']);结束
DepTime,年、月、DayofMonth DayOfWeek CRSDepTime, ArrTime, 1994 C, 1, 7日,5858900954年,1003年,227年,NA, 56岁,63年,NA, 9日,2,解释水平理论,1994年开放,1,8日,6859900952年,1003年,美国,227年,NA, 53岁,63年,NA, 1,解释水平理论,-11年或1994年,1,10日,1935900年,1023年,1003年,227年,NA, 48岁,63年,NA, 20岁,35岁,解释水平理论,O 1994 1, 11日,2903900年,1131年,1003年,227年,NA, 148年,63年,NA, 88年,3,解释水平理论,O 1994 1, 12日,3933900年,1024年,1003年,227年,NA, 51岁,63年,NA, 21岁,33岁的解释水平理论,1994啊,1、13、4,NA, NA, 1003年,900年,227年,NA, NA, 63年,NA, NA, NA,解释水平理论,ORF,1994, 5903900, 1005, 1003, 227, NA, 62年,63年,NA, 2, 3,解释水平理论,1994年开放,1,15日,6859900年,1004年,1003年,227年,NA, 65年,63年,NA, 1, 1,解释水平理论,或1994年,1,17日,1859900955年,1003年,227年,NA, 56岁,63年,NA, 8日,1,解释水平理论,或1994年,1,18岁,2904900959年,1003年,227年,NA, 55岁,63年,NA, 4, 4,解释水平理论,1994年开放,1,19日,3858900947年,1003年,227年,NA, 49岁,63年,NA, -16, 2,解释水平理论,O 1994 1, 20日,4923900年,1015年,1003年,美国,227年,NA, 52岁,63年,NA, 12日,23日,解释水平理论,O 1994 1, 21日,5日,NA, NA, 1003年,900年,227年,NA, NA, 63年,NA, NA, NA,解释水平理论,ORF,1994, 22岁,6859900年,1001年,1003年,227年,NA, 62年,63年,NA, 2, 1,解释水平理论,1994年啊,1,24岁,1901900年,1006年,1003年,美国,227年,NA, 65年,63年,NA, 3, 1,解释水平理论,1994年开放,1,25岁,2859900952年,1003年,227年,NA, 53岁,63年,NA, -11年,1,解释水平理论,O 1994 1, 27岁,4910900年,1008年,1003年,227年,NA, 58岁的63年,NA, 5, 10,解释水平理论,或1994年,1,28岁,5858900年,1020年,1003年,227年,NA, 82年,63年,NA, 17日,2,解释水平理论,O 1994 1, 29岁,6859900953年,1003年,227年,NA, 54岁,63年,NA, -10年,1,解释水平理论,O

加载一个文件

使用textscan函数导入数据,因为它提供了从混合文本数据文件导入特定列所需的灵活性;它还可以与“NA”竞争,作为缺失值的指标。提取列1、2、4和5 (一年DayOfWeekDepTime)从本地机器上的一个文件。

tic srcFile = fullfile(dataFolder, files(8).name);f = fopen(srcFile);C = textscan(f,'%f %f %*f %f %f %*[^\n]'“分隔符””、“...“HeaderLines”, 1“TreatAsEmpty”“NA”);文件关闭(f);[年月日,星期日期,DepTime] = C{:};sprintf ('%g秒加载“%s”。\n', toc, srcFile)
ans = 52.4414秒加载“/Volumes/HD2/airline/data/1994.csv”。

打开一个本地工人池

前面的步骤可能需要一些时间来导入,可能需要整整一分钟。幸运的是,该操作稍后将并行运行。如果有必要,现在打开一个8人的工作池:

试一试parpool (“本地”8);结束
使用“本地”配置文件启动并行池(parpool)…连接8个工人。

加载一个文件到所有工人

移动textscan编码到集群,并让集群中的所有节点加载该文件。这是用单程序/多数据(SPMD)块。一个spmd在所有八个工位上并行执行MATLAB代码块:

抽搐spmdsrcFile = fullfile(dataFolder, files(8).name);f = fopen(srcFile);C = textscan(f,'%f %f %*f %f %f %*[^\n]'“分隔符””、“...“HeaderLines”, 1“TreatAsEmpty”“NA”);[年月日,星期日期,DepTime] = C{:};文件关闭(f);结束sprintf ('%g秒到所有工作人员上加载“%s”。\n', toc, srcFile{1})
ans = 65.2914秒在所有工人上加载“/Volumes/HD2/airline/data/1994.csv”。

在每个Worker上加载不同的文件

前一个步骤所花费的时间与前一个加载所花费的时间相同,几乎是线性加速。但是,请注意同一个文件被加载了八次。为了允许不同的工人有不同的行为,每个工人都被分配了一个唯一的索引,称为labindex.使用labindex让每个员工在一个公司里有不同的表现spmd块。在本例中,只需替换硬编码的索引文件(8)文件(labindex),spmdBlock在不同的worker上加载不同的文件(回想一下文件是数据集中所有文件名的向量)——这spmdBlock在其他方面与前一个相同。

抽搐spmdsrcFile = fullfile(dataFolder, files(labindex).name);f = fopen(srcFile);C = textscan(f,'%f %f %*f %f %f %*[^\n]'“分隔符””、“...“HeaderLines”, 1“TreatAsEmpty”“NA”);[年月日,星期日期,DepTime] = C{:};文件关闭(f);结束sprintf ('%g秒加载每个worker上的不同文件。\n'toc)
Ans = 61.4875秒在每个worker上加载不同的文件。

为每个Worker执行非合作计算

现在每个工作人员都有自己的一组数据。在某些应用程序中,这可能就足够了,每个数据集都可以彼此独立地进行处理。这有时被称为“令人尴尬的并行”问题。在这个数据集的情况下,每个文件都是一整年的数据,因此按年计算是微不足道的。的价值一年每个工人都应该是相同的。请在spmd块,然后将该计算的结果带回本地进行检查。方法之后立即使用单元格数组样式的索引spmd块;这是检索变量在每个worker上的所有值的方法。因为有8个worker,所以返回了8个值——这被称为a复合在MATLAB。

spmdallYearsSame = all(Year==Year(1));myYear = Year(1);结束[allYearsSame {:}] [myYear {}):
ans = 1 1 1 1 1 1 1 1 ans =第1至6栏1987 1988 1989 1990 1991 1992第7至8栏1993 1994

可视化出发时间

创建一个过去八年的出发时间直方图,以获得第一次真实的数据。请注意,有一年(1987年)的规模明显小于其他年份——这是因为它只是部分年份的数据。使用文件和文件夹层次结构对数据进行course-grain分区可以直接将数据集划分为有意义的(可管理的)子集。

spmdH = hist(DepTime, 24);结束情节(cell2mat (H (:)) ',“o”)标题(“每年出发次数直方图”)包含(“一天中的每一小时”) ylabel (“飞行次数”arrayfun(@(x) sprintf(' % d ', [myYear{:}],“UniformOutput”假),...“位置”“西北”

执行每个Worker的协作计算

但是假设必须对数据集中的所有观测数据进行分析。为此,分布式可以使用数组。一个分布式数组允许MATLAB处理分布在许多工作中的数组,就像它是一个单一的“正常”MATLAB数组一样。分布式数组被大量的MATLA金宝appB函数所支持,包括线性代数。

要创建分布式数组从内存中已经存在的数据中,首先确定每个worker的块数组有多大,以及它的总大小。计算单个长度和总长度,然后创建四个分布式数组,一个用于原始数据集中的每一列,使用所谓的codistributor(称为共同分配者,因为这是从每个工人的角度;每个人都需要与其他工人合作)。

抽搐spmd长度= size(Year,1);结束长度= cell2mat(长度(:));长度=长度';totalLength = sum(长度);spmdcodistr = codistributor1d(1,长度,[totalLength, 1]);年份=共分布。构建(一年,codistr);月=共分布。构建(月、codistr);DayOfWeek =共分发。构建(DayOfWeek codistr);DepTime =共分布。构建(DepTime codistr);结束sprintf ('%g秒从复合材料创建分布式数组。\n'toc)
ans = 1.86178秒从复合材料创建分布式数组。

在整个数据集上可视化年份

现在有四个变量(一年DayOfWeek,DepTime),包含整个8年的数据集;这些变量可以像普通的MATLAB变量一样使用。这个例子使用了一个一维数组,但是是多维的分布式如果您需要在集群中分布“块”数据金宝app,则还支持数组。如果你想收集分布式数组转换为一个正常的MATLAB数组,您可以这样做收集函数。执行此操作时要小心,并确保您的本地计算机有足够的内存来保存整个文件的聚合内容分布式数组中。创建出发年份直方图:

firstYear = gather(min(Year));lastYear = gather(max(Year));figure hist(年份,第一年:去年,“EdgeColor”' w ')标题(“年度柱状图”)包含(“年”) ylabel (“飞行次数”

注意使用收集在这里。任何操作分布式数组生成另一个分布式数组中。尽管最小值而且马克斯返回小数组,但它们必须是收集的第二个参数中使用它们之前,先返回到本地MATLAB函数。现在你可以更清楚地看到,1987年的观测数据比随后几年少得多。

在整个数据集上可视化出发时间

接下来生成出发时间的直方图。这次不需要收集任何东西,因为第二个参数为(24,以一天中的小时为单位)是一个常数值,而不是需要从分布式数据。

图;嘘(DepTime, 24);标题(“出发时间直方图”)包含(“一天中的一小时”) ylabel (“飞行次数”

使用24个直方图箱

读者会注意到,直方图箱的最大长度是2500,而不是25。这是因为原始数据集将时间表示为整数编码HHMM。只考虑时辰:

嘘(地板(DepTime / 100), 24)标题(“出发时间直方图(仅限小时)”)包含(“一天中的一小时”) ylabel (“飞行次数”

重新表达出发时间

实际上,将HHMM重新表示为0到24之间的数字,小数点后的值表示为每分钟的1/60。的值分布式数组,然后再次绘制直方图DepTime的改进表现。

清晰的C;DepTime = floor(DepTime/100)+mod(DepTime,60)/60;嘘(DepTime 24)标题(“出发时间直方图(分数时间)”)包含(“一天中的一小时”) ylabel (“飞行次数”

注意使用清晰的使工作区摆脱C,在前面导入文件时在代码中创建的临时对象。MATLAB通常避免保留不必要的数据副本,并且保留临时数据到目前为止对程序没有实质性的影响。然而,由于的值DepTime将会发生变化,MATLAB将处于需要保持相似但不同的数组副本的位置。出于显而易见的原因,要避免这种情况,所以在修改之前要清除这些旧的“快照”DepTime

删除缺失的数据点

到目前为止,还没有检查数据是否存在缺失数据,这些数据在出发时间中用not-a-number (NaN)表示。寻找nan,然后从所有分布式数组中删除那些坏的行。

tic goodRows = ~isnan(DepTime);流('有%d行要从数据中删除。\n'...totalLength-gather (sum (goodRows)));Year = Year(goodRows);Month = Month(goodRows);DayOfWeek = DayOfWeek(goodRows);DepTime = DepTime(goodRows);sprintf ('%g秒来修剪坏行。\n'toc)
需要从数据中删除419397行。Ans = 23.9631秒修剪坏行。

使用完整的数据集

现在是时候处理数据集中的所有文件了。因为数据文件是文本,所以这个应用程序将花费大部分运行时解析文本并将其转换为数字。对于一次性分析来说,这可能是可以接受的,但是如果您预计需要多次加载数据集,那么将数值表示保存起来以供将来使用可能是值得的。这是在简单的parfor循环,为原始数据集中的每个文件创建MATLAB MAT-File。第一次运行这个循环需要几分钟。然而,该代码也足够聪明,不会在MAT-File已经存在时重新创建它,因此当稍后重新运行时,此循环很快结束。

注意:如果您有一个具有8个以上核的集群,那么现在是关闭8个工作人员池并打开一个更大的池的好时机。如果您没有访问集群的权限,只要您的本地计算机有大约16gb或更多的RAM,这也是可以的。

清晰的所有dataFolder =/卷/ HD2 /航空/数据的;files = dir(fullfile(dataFolder)‘* . csv‘));Files = Files (~strncmp({Files (:).name},“。”1));删除“点”文件(Mac)抽搐parfori = 1:元素个数(文件)参见//www.tatmou.com/supp金宝apport/solu金宝搏官方网站tions/en/data/1-D8103H了解下面的技巧parSave = @(fname,年月日,星期日期,DepTime)...保存(帧,“年”“月”“DayOfWeek”“DepTime”);srcFile = fullfile(dataFolder, files(i).name);[path, name, ~] = fileparts(srcFile);cacheFile = fullfile(path, [name . name .“.mat”]);如果isempty(dir(cacheFile)) f = fopen(srcFile);C = textscan(f,'%f %f %*f %f %f %*[^\n]'“分隔符””、“...“HeaderLines”, 1“TreatAsEmpty”“NA”);文件关闭(f);[年月日,星期日期,DepTime] = C{:};parSave(cacheFile,年,月,日,星期,DepTime);结束结束sprintf ('%g秒导入文本文件以保存到MAT-Files。\n'toc)
ans = 220.984秒导入文本文件以保存到MAT-Files。

加载整个数据集

现在是加载整个数据集的时候了,它刚刚保存到MAT-Files中。由于worker的数量可能少于文件的数量,每个worker将负责一个文件的收集。文件被分配给工作人员使用labindex还有模运算。每个worker读取所有文件,然后vertcat把它们的内容放到一个数组中。在导入数据时,这是执行上面介绍的处理的方便时间——过滤NaN行,转换出发时间的HHMM表示,并注意数据集中每个工人的部分的长度。接下来,创建分布式数组。虽然这部分代码是本文中最长的,但它基本上是前面讨论过的代码的复制粘贴。

抽搐spmdmyIndicies = labindex:numlabs: nummel(文件);Year = cell(size(myIndicies));Month = cell(size(myIndicies));DayOfWeek = cell(size(myIndicies));DepTime = cell(size(myIndicies));i = 1:numel(myIndicies) srcFile = fullfile(dataFolder, files(myIndicies(i)).name);[path, name, ~] = fileparts(srcFile);cacheFile = fullfile(path, [name . name .“.mat”]);tmpT = load(cacheFile);年{i} = tmpt .年;月份{i} = tmpT.Month;DayOfWeek{i} = tmpT.DayOfWeek;DepTime{i} = tmpT.DepTime;结束结束清晰的人体如上所述,避免深度复制spmd年份= vertcat(年份{:});月= vertcat(月{:});DayOfWeek = vertcat(DayOfWeek{:});DepTime = vertcat(DepTime{:});goodRows = ~isnan(DepTime);Year = Year(goodRows);Month = Month(goodRows);DayOfWeek = DayOfWeek(goodRows);DepTime = DepTime(goodRows);DepTime = floor(DepTime/100)+mod(DepTime,60)/60; lengths = size(Year,1);结束长度= cell2mat(长度(:));长度=长度';totalLength = sum(长度);spmdcodistr = codistributor1d(1,长度,[totalLength, 1]);年份=共分布。构建(一年,codistr);月=共分布。构建(月、codistr);DayOfWeek =共分发。构建(DayOfWeek codistr);DepTime =共分布。构建(DepTime codistr);结束sprintf ('%g秒从MAT-Files导入并创建分布式数组。\n'toc)
ans = 12.121秒从MAT-Files导入并创建分布式数组。

重新运行可视化

现在可以对整个数据集执行一些可视化操作。DayOfWeek这次是可视化的。

figure hist(DayOfWeek, 1:7)“星期直方图”)包含(“星期几”) ylabel (“飞行次数”甘氨胆酸)组(,“XTickLabel”, {“我的”“面前”“结婚”“碰头”“星期五”“坐”“太阳”});

在工人池中重新平衡数据

负载平衡是讨论的最后一个主题。的分布式数组在文件级进行分区,每个worker有零个或多个文件。如果您的输入文件大小大致相同,并且worker的数量小于文件的数量,那么这可能是没问题的。然而,不均匀的文件大小可能会导致工作不平衡,或者更糟糕的是,工作人员多于文件将导致完全空闲的工作人员!如果您正在运行超过22个工作程序,那么在使用此数据集时将会看到这种效果。

因此,它有助于重新平衡a的分布分布式数组中。这将需要在工作人员之间传输一些数据,但这可能是一项值得的投资,可以加速后续的计算。计算“理想”(平衡)分布,创建一个新的codistributor在理想分布下,然后重新分配利用这个理想。

抽搐spmdbeforeSizes = size(getLocalPart(Year), 1);结束newBreaks=round(linspace(0, totalLength, numel(长度)+1));newLengths = newBreaks(2:结束)-newBreaks (1: end-1);spmdnewCodistr = codistributor1d(1, newlength, [totalLength, 1]);Year = redistribute(Year, newCodistr);Month = redistribute(Month, newCodistr);DayOfWeek =重新分发(DayOfWeek, newCodistr);DepTime = redistribute(DepTime, newCodistr);结束spmdafterSizes = size(getLocalPart(Year), 1);结束sprintf ('%g秒重新分配数组。\n'toc)图酒吧([[beforeSizes{}):”,[afterSizes{}): '])标题(“每个工人的数据(前后)”)包含(工人数量的) ylabel (“数据量”)({传奇“之前”“后”},“位置”“西北”
Ans = 14.1069秒来重新分配数组。

在关闭

希望本文和代码已经让您了解了使用分布式数组时可能使用大数据的交互式工作流。介绍和探讨了并行文件I/O、分布式和协作操作、可视化和负载平衡。虽然这里使用的示例很简单,但这些基本原则可以扩展到其他类似分布在文件集合中的数据集。

值得注意的是,这篇文章只触及了分布式数组的实际数值能力的表面——更高阶的操作,如单值分解(SVD)和其他线性代数函数、fft等也可用。有关分布式数组的进一步阅读,请尝试以下内容:

你如何管理你的大数据分析?让我们知道在这里




发布与MATLAB®R2013b

|
  • 打印
  • 发送电子邮件

评论

如欲留言,请点击在这里登录您的MathWorks帐户或创建一个新帐户。