罗兰在MATLAB的艺术

把想法变成MATLAB

分析和MATLAB R2015b婴儿名字

今天的嘉宾是马特Tearle,从在MathWorks培训服务集团。马特把两个数据分析函数中引入R2015b通过他们的步伐与一些意想不到的用途。

内容

引入两个新功能

我们不知疲倦的MATLAB开发人员会不断添加伟大的新功能进行数据分析。我一直很开心玩中引入两个新功能R2015b:findgroupssplitapply。这些功能可以很容易地找到所有的独特组合关键变量,一组号码分配给每个观测数据基于这些关键变量,然后将一个函数应用到每一个组。

文档发布视频显示标准的例子,预期使用,如发现病人的平均血压测量在一个试验中,分组的患者的性别和他们收到的药物剂量。让我们做一些模拟数据的表这药物试验的例子。

rng numpatients = 15 (1234);性别=分类(randi (2 numpatients 1), 1:2, {“F”,“米”});drugdose = 50 *兰迪(3 numpatients 1);英国石油(bp) = 100 + 40 *兰德(numpatients, 1);patientdata =表(性别、drugdose英国石油(bp)“VariableNames”,{“性别”,“剂量”,“英国石油公司”})
看上去patientdata =性别剂量BP辩得一样100 134.77 100 117.45 F 50 132.09 150 105.75 150 128.17 100 128.18 F 100 108.75 50 136.99 100 108.75 150 136.37 F 100 102.39 100 107.37 150 101.89 50 127 F 100 123.78

第一次使用findgroups将一组号码分配给每个病人,定义的组的组合性别剂量。那么容易计算的中值英国石油公司为每个组使用splitapply

组= findgroups (patientdata (:, {“性别”,“剂量”}));medianbp = splitapply (@median、patientdata.BP集团)
medianbp = 132.09 123.78 131.99 117.45 - 116.96

请注意,有五个值,即使有六个可能的组合的性别和药物剂量。您可以使用第二个输出findgroups获取组列表。由于输入是一个表,组名称也将作为一个表返回。可以使用点符号索引的平均血压值添加到表中。

[集团,gender_and_dose] = findgroups (patientdata (:, {“性别”,“剂量”}));gender_and_dose。MedianBP = splitapply (@median、patientdata.BP集团)
gender_and_dose = __ ________性别剂量MedianBP ______ 50 132.09 F 100 123.78 50 123.78 100 117.45 150 116.96

关于我的工作最好的事情之一是,我可以看到这些新特性成形和想想MATLAB用户(喜欢你!)可以让他们去工作。真正有趣的是当我为我自己的工作自己使用的特性。或者玩。真的是什么真的有趣的是当使用雇佣了一点“开箱即用”的思想。在这种情况下,我认为我发现了申请findgroupssplitapply那还不开发者所想要的。

受欢迎的婴儿名字,Aagot Zzyzx

也许有一些水在我们的建筑,但很多同事生孩子。这自然导致许多咖啡机对话关于宝宝的东西:睡眠不足,爱管闲事的人姻亲,当然,名字。

一个特定的话题转到婴儿名字常见和罕见。(我自己的孩子们在前一类,和一个同事在后者。)数据驱动的MATLAB书呆子,我们,我们认为必须有一个方法来量化给定的名字是多么的普遍。我回想起,美国,the Social Security Administration记录婴儿的名字。一两分钟和一位知名的互联网搜索引擎和我我的数据来源——一个zip文件,其中包含名称和编号为每年从1880年到2014年。我所要做的就是从这些文件中读取数据到MATLAB,我能找出任何给定的名字排在历史的声望。

每个文件在下载文件夹中包含的数据对于一个给定的,包括三列:姓名,性别,和儿童的数量(性别)在当年那个名字注册。文件的前几行1880年看起来像这样:

dbtype (的名字\ yob1880.txt ',“1:8”)
1玛丽,F, 7065 2安娜,F, 2604 3艾玛,F, F,伊丽莎白2003年4 1939 5米妮,F, 1746 6玛格丽特,F, 1578 7 Ida, F, 1472 8爱丽丝,1414

1980是这样的:

dbtype (的名字\ yob1980.txt ',“1:8”)
1詹妮弗,F, 58385 2阿曼达,F, 35820 3杰西卡,F, 33920 4梅丽莎,F, 31631 5莎拉,F, 25741 6希瑟,F, 19971 7妮可,F, 19916 8艾米,19832

注意,名称不相同的顺序从每年(他们的受欢迎程度)。不同的文件甚至不包含相同的名单。在2010年有5个男孩叫Zzyzx, 1915年有5个女孩叫Aagot;没有其他年要么是这些名字受欢迎的达到5-name截止。

阅读任何一个文件到MATLAB是很容易的,只是分隔文本。但是我该如何编译所有年份的数据到一个数组?这实际上取决于我打算使用数据。如果我想通过时间研究模式,我需要一个表名称,年,像这样:

|玛丽安娜……Aagot……- - - - - - | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1880 | 7065 2604…0…1881 | 6919 2698…0……|……1915 | 58187 15120…5……|……

有很多方法来完成这项工作,这是另一个(稍微)新功能的MATLAB我爱和经常使用自己的工作。但在这种情况下,我不需要个人年度值;我只是需要历史总数。在我看来,如果我有所有数据连接成一个大名单,我只需要把所有重复的姓名/性别组合:

玛丽,7065 < - - - - - - |安娜,F, 2604 |艾玛,F, 2003 |…玛丽| 6919 < - - - - - - |安娜,F, 2698 |…| - - - - - >和玛丽的女孩叫玛丽,总数58187 < - - - - - - |海伦,F, 30866 |多萝西,F, 25154 |…玛丽|莉莉安娜,F 2624 | 2611 < - - - - - - |艾琳娜,F, 2598…

现在希望你看到我所看到的:findgroupssplitapply可以适用于整理我的数据。组的组合名字和性别。函数的应用总和和数据应用到这个号码。

把理论付诸实践

现在我需要的是像一个大表中所有数据。事实证明,这是非常容易使用MATLAB最近的另一个特性:数据存储。数据存储是为了方便读取数据从多个文件相同的格式。在这种情况下,所有的文件与三列以逗号分隔的文本。

dat =数据存储(的名字\小无赖* . txt ',“ReadVariableNames”假的,“VariableNames”,{“名字”,“性别”,“数量”});

这将创建一个数据存储,是指所有的数据文件。三列的选项指定变量名称,而不是通过阅读获取它们的标题行文件(这些文件不包含任何头信息)。每一列的格式已经自动确定。但是而不是读性别作为一个字符串,我可以修改格式读它作为分类变量:

dat。SelectedFormats {2} =“% C”;

分类变量更容易和更高效处理字符串,在处理大量的实例时少量的独一无二的字符串(“F”和“M”,在这种情况下)。

现在我准备读取数据。我可以阅读每个文件单独函数,但我也可以读所有的readall功能:

rawnamedata = readall (dat);谁rawnamedata
类属性名称大小字节rawnamedata 1825433 x3 1825433表

哇。导入数据从未如此简单!现在,我有一个表中的所有数据,我可以使用findgroups找到所有姓名/性别组合,并分配相应的组:

[集团名称]= findgroups (rawnamedata (:, {“名字”,“性别”}));

最后,splitapply核对总数:

的名字。数量= splitapply (@sum、rawnamedata.Number、组);谁的名字
名字大小字节104110 x3 104110表类属性名称

现在的名字是这样的表rawnamedata但由于每个名称的所有重复总计为一个观察。

现在是直接获取历史对于任何给定的名称数量。我可以离开这个名字作为字符串,它允许各种各样的字符串匹配(“Aa”开头的名称),或将它们转换为分类,但只允许精确匹配= =而不是比较字符串

的名字。Name =分类(names.Name);名称(名称。名字= =“罗兰”:)名称(名称。名字= =“克里夫”:)
ans =姓名性别号码_____交罗兰F 11862罗兰45806 ans =姓名性别号码_____交克里夫F 23克里夫M 4503

宣扬好消息

您可能已经注意到,我利用很多新特性:表,分类,和数据存储,以及findgroupssplitapply。我肯定喜欢使用新功能更容易尽可能做更多的事情。为了比较,这就是我想出了如何做到这一点在2012年相同的任务:

dbtype (“oldschool.m”)
1等= cellstr (ls(的名字\小无赖* . txt '));2 nf =长度等);3 alldata =细胞(nf, 3);4 k = 1: nf 5 fid = fopen([‘名’,等于是{k}));6 alldata (k) = textscan (fid, ' % s % s % f”、“分隔符”,',');7 fid =文件关闭(fid);结束8 9 10名=猫(alldata {: 1});11性别=猫(1,alldata {: 2});12号=猫(1,alldata {: 3});13 14 ~,idx1, idx2] =独特(strcat(名称、”、“性别)); 15 number = accumarray(idx2,number); 16 17 names = [names(idx1) gender(idx1) num2cell(number)]; 18 19 names(strcmp(names(:,1),'Loren'),:) 20 names(strcmp(names(:,1),'Cleve'),:)

我认为这是一个证明我们专门开发人员只有三年后我们现在可以减少一半的脚本,失去了循环,不需要担心textscanaccumarray最后,我们的数据在一个非常巧妙的容器:

dbtype (“analyzeNameData.m”)
1 dat =数据存储(“名字\小无赖* . txt”,…2“ReadVariableNames”,假,VariableNames,{“姓名”,“性别”,“数量”});3 dat。文本scanFormats{2} = '%C'; 4 5 rawnamedata = readall(dat); 6 7 [group,names] = findgroups(rawnamedata(:,{'Name','Gender'})); 8 names.Number = splitapply(@sum,rawnamedata.Number,group); 9 10 names.Name = categorical(names.Name); 11 names(names.Name == 'Loren',:) 12 names(names.Name == 'Cleve',:)

将计数,百分比

我现在可以得到数字给定的名称,但很难真正了解一个名字的流行的上下文数据所代表的人的总数。换句话说,我想正常计数,百分比。计算人口总数的百分比将会容易,但它是更典型的性别比例。立即,听起来像是另一个分组操作,但我可以用splitapply在这里吗?正常情况下splitapply将一组数据并返回一个值从一个统计等操作总和的意思是。如果返回不是一个单一的值呢?

这里有一个函数需要一个向量的非负的值作为输入,并返回一个归一化向量作为输出的版本:

全国抵抗运动= @ (x) 100 * x /笔(x);

如果我用这个作为我的splitapply函数,作为数据数量和性别组?写,这实际上会产生一个错误。然而,这是一个非常有用的错误,建议的另一种选择:

全国抵抗运动= @ (x) {100 * x /笔(x)};

这将返回相同的结果,但是作为一个单元格的内容。所以我的函数返回一个值,除了它的细胞而不是一个数字。应用这种每组splitapply将返回一个单元阵列。请注意,我已经为我定义的群体names.Gender,但这是一个分类和数组splitapply希望数字组值。所以我将使用findgroups这里,但有趣的是,你不总是,有时你已经从别的地方组值,这很好。

性别= findgroups (names.Gender);pctcell = splitapply(全国抵抗运动,names.Number,性别)
pctcell = [64911 x1双][39199 x1双)

因为我有两个组,我得到两个输出。但我希望所有1825433个人价值对应我的原始数据。这不是真正的典型,预期用途splitapply,但我可以让它为我工作。我可以提取单元阵列的内容和连接在一起,但我不知道结果将在原始数据的顺序相同。一个简单的方法来避免被第一次我的数据进行排序。

名称= sortrows(名称,“性别”);性别= findgroups (names.Gender);pctcell = splitapply(全国抵抗运动,names.Number,性别);的名字。% =猫(1,pctcell {:});名称(名称。名字= =“罗兰”:)名称(名称。名字= =“克里夫”:)
ans =姓名性别号码百分比_____交________罗兰F 11862 0.0071罗兰45806 0.026934 ans =姓名性别号码百分比_____交__________克里夫F 23 1.3767 e-05克里夫M 4503 0.0026478

我可以把这多远?一种好方法是使用绘图函数作为我的分组操作。例如,名称是如何分布的?为了避免重复计算,我将首先计算累积分布,使用相同的方法如上。

名称= sortrows(名称,{“性别”,“百分比”},{“提升”,“下”});grpaccumulate = @ (x) {cumsum (x)};pctcell = splitapply (grpaccumulate、names.Percent、性别);的名字。CumulativeDist =猫(1,pctcell {:});

现在我可以做一个分组的阴谋splitapply情节作为我的函数。再一次,情节不是你通常使用的功能splitapply,但它工作吗?是的!(记住抓住这样重复调用情节不要互相覆盖。)

图保存splitapply (@plot、names.CumulativeDist、性别);传奇(类别(names.Gender),“位置”,“东南”5000 0 100)轴([0])包含(“数量的惟一名称”)ylabel ((男性或女性)人口的百分比网格)持有

从这可以看出,约3/4的女孩(自1880年以来在美国)分享了500年最受欢迎的名字。和一个小男孩共享相同数量的85%以上。让我们放大……

轴([0 250 0 60])

只需要大约60名解决一半的男人在美国!与创造力,我可以得到确切的数字……

qntl = @ (x)找到(x > = 50, 1,“第一”);splitapply (qntl、names.CumulativeDist、性别)
ans = 155 63

splitapply函数是非常有用的!我们发现25%的四分位数的数字。

qntl = @ (x)找到(1 x > = 25日,“第一”);splitapply (qntl、names.CumulativeDist、性别)
ans = 43 14

你觉得怎么样?每四个男性你知道(在美国,ignoring changing trends over time) has one of these 14 names:

男孩= names.Name(名字。性别= =“米”);disp(男孩(一14,:))
詹姆斯·约翰·罗伯特·迈克尔·威廉·大卫·约瑟夫·理查德·查尔斯·托马斯·克里斯托弗·丹尼尔·马修·乔治

还有我在名单之列。连同我的兄弟,父亲,祖父。哦。至少我有一个奇怪的姓来弥补我们缺乏想象力!

有趣的是,女孩的名字显示更大的多样性。我不知道为什么,可能是或(如果有的话)这意味着什么,但它确实使我非常不科学的证据来自父母,更“好”女孩比男孩在选择名称的选项。

将在哪里findgroupssplitapply带你吗?

有老话说,如果你有一把锤子,看什么都像是钉子。也许我有罪的落入陷阱,但我更愿意把它看成是看到这个新的“锤”的东西能做什么。在这种情况下,它看起来像一个很方便的工具。从讨论婴儿名字我已经发现了一些有趣的应用程序findgroupssplitapply。作为回报,他们让我一些有趣的发现男孩和女孩的名字是如何分布的。

你呢?你做了一些分析在哪里splitapply可能会有帮助吗?让我们知道在这里




发表与MATLAB®R2015b

|

评论

留下你的评论,请点击在这里MathWorks账户登录或创建一个新的。