写
在前面
本期开始大猫将直奔主题,从“语法灵活性(Syntax)”、“性能与并行计算(Performance & Parallel Computation)”、“商业/社区支持(Support)”三个方面比较不同统计软件。本期主题是“语法灵活性(Syntax)”,首先总结我们平时做的研究具有I/O Intensive以及Interactive的特点,然后告诉大家什么样的语法才最适合具有这些特点的研究工作。
注意!本期可谓整个系列中的精华,字字句句都是大猫亲身的血泪教训,虽然有点长(3000字也不算太长吧?),但是大猫相信小伙伴们读完以后肯定会有收获!
一
个误区
大猫被问得最多的问题是“Xx软件好用吗?”、“Xx软件难不难”之类。其实会Xx软件的人都清楚,这种问题是最难回答的,毕竟难者不会,会者不难。大猫认为,初学者应该改变自己最初的问题,与其问一个软件难不难,不如问“我要实现Xx需求,Xx软件能满足我吗?如何满足?语法是否灵活简介?性能是否足够?商业/社区支持是否充分?”。这些问题基本决定了你适合学习什么软件,由于统计软件的使用具有强大的路径依赖性,同时使用多个软件远不如精通一门来得高效,所以在选择你的语言之前,仔细思考以上问题对你大有裨益。大猫比较熟练的有SAS与R,故下文主要就这两门语言进行讨论,期间会偶尔涉及到其他语言,例如Mysql、Python、Matlab,以及非常小众但迅速发展的Julia。此外再次重申,下文所有比较与结论都仅针对大多数的经济学研究
语
法灵活性(Syntax)
你在Coding上花的时间越少,你在Research上花的时间就越多
”
对于90%的人的90%的需求,其实绝大多数语言都可以胜任,关键在于寻找语法最简洁、最灵活的那一种语言。举一个不恰当的例子,假设解决你的问题有两种方法:第一种,使用半个小时来编写直观、易懂、简短的程序,然后花一个小时来运行;第二种,使用一个小时来写一段晦涩的代码,然后只要30分钟就能跑出结果,你选择哪一种?这几年论文写作与数据处理的经验告诉大猫:在Syntax(Readability)和Performance之间,大猫宁愿选择前者,这也是大猫现在从SAS阵营转战到R阵营的最大原因。
你会选择那种?
注意!下文中大猫指R的语法高效很大程度上基于data.table包,原生的R语法在大猫看来还是有些臃肿
此外,R的效率现在也可以与SAS比肩,详见大猫前几期的《高效R开发:Microsoft R Open》
为什么大猫认为在经济学的研究中,R比SAS更高效?还是从我们的需求看起,经济学研究中用到的数据处理,主要有两个特点:I/O Intensive以及Interactive,而R语法的灵活非常适合应对这两个特点。先来看I/O Intensive,我们进行的数据处理,大致可以分为CPU Intensive与I/O Intensive两类。顾名思义,前者需要大量的CPU运算,这在数值模拟中非常常见,后者需要大量的磁盘/内存读写操作,这在数据清洗中非常常见。绝大多数的经济学研究中遇到的数据处理需求是I/O Intensive的,这由学科性质所决定:社会科学注定要搜集大量原始的“脏”数据并进行诸如删除缺失值、连接表(Table Join)等各种I/O操作(你想想看你自己写代码的时候是不是把绝大部分时间花在这些事上了),而自然科学的数据一般更加规范,它们会花更多时间在计算上,你看Matlab的字符串函数远不如SAS的字符串函数强大就知道理工科其实对于数据清洗的要求远没社会科学多。
说句题外话,SAS的字符串函数种类真是多到了变态的地步,这一部分是前后版本兼容的需要,另外也是SAS内部分团队开发模块的结果。相关小故事大猫以后可以给大家介绍。
其次,我们的编程活动又可以分为Interactive与Programmatic(在SAS中称为Batch Mode)两类,前者是一个探索的过程,这在我们写论文的时候很常见。因为我们不可能一开始就知道模型应该如何设定,就连大牛们写文章也是各种“Play With Your Data”、“Try-And-Error”过来的,因此我们需要不断尝试与修改我们的代码,往往在所有Coding时间中,95%的是用来调试,而只有5%的时间是用来复用最终版本的代码。甚至在我们获得那个“最终版本”的代码后,我们的文章也写完了,代码也就没用了。相反,在生产环境中,更多需要的是Programmatic的编程,比如程序员花了5%的时间在开发环境中写完了一段代码,剩下95%的时间都是这段代码在生产环境中的不断复用。
为什么说这两大特征使得R的语法产生优势了呢?首先,I/O Intensive决定了你大多数Coding的时间都会花在数据清洗上,因而你需要寻找一门在Data Manipulation上语法非常简洁的语言;其次,Interactive的编程需要你拥有快速将想法转化为代码的能力,转化的过程所需时间越少、转化后的代码越短,越有利于你思考问题。在这两方面,R都具有优势。
两
个例子
举特例子,现在大猫有全国每个省的GDP,然后大猫想比较下每个省和全国的均值相差多少。如果用SAS,那么大猫首先需要用Proc Means来统计全国的均值,然后通过Output语句将结果输出,假设输出数据集是Means吧,然后大猫还得把数据集Means给Join回原来的数据集,关键是在Join的过程中还要用到 If _N_ Eq 1 Then Set...这种非常Tricky的技巧,这对于初学者和不懂SAS的PDV(Program Data Vector)机制的人简直是噩梦啊!更关键的是,类似的操作大猫可能经常进行,每次都走一遍Proc Means – Output – Join的过程不仅非常繁琐(到你想骂娘),还可能出错。大猫无数次有这样痛苦的经历:在Coding的过程中经常写了一半回过头看前面,就已经不知道前面的代码是要干什么的了,因为满屏全是Proc Means和Proc Sort,这时更别说回过头去看十天半个月前些的代码了,这时唯一能做的就是Keep My Head Beating Against The Wall了啊!另外作为一个完美主义者,大猫希望每个数据集都能被优雅地、有意义地命名,但是看着那么多Proc Means输出的数据集大猫真是想死的心都有了啊!该怎么命名啊!逼死强迫症啊!更加更加关键的是,也是大猫在此着重强调的:不到万不得已,尽量不要在SAS中进行Join Table操作!因为跨表操作,例如Join,其过程往往非常抽象,你需要考虑一对多,多对一,多对多这几种SAS中会出现的情况,如果你以前学过SQL,那么可能还会和Inner Join、Outer Join、Cartesian Product等各种概念搅和在一起,绝对让你欲哭无泪!
那么R是如何解决的呢?如果你使用了R/Data.Table,那么一行代码就可以搞定:
▶ dt[, gdp_delta := gdp – means(gdp)]
另外一个SAS中常遇到的问题是“Retain/Sort的诅咒”(大猫自创的词,但是很贴切)。我们都知道,除非预先建立index,SAS基本上是逐条处理数据的(Sequentially Access),处理过的数据就被丢弃,如果需要用到前几条的数据,不得不用Retain/By或者Lag语句,而为了使用Retain/By语句,必须提前做一个Proc Sort步。但可谓成也萧何败也萧何,这种特性给予了SAS处理无限大数据的可能,但是严重影响了语句的灵活性。举一个最简单的例子,大猫现在有个数据集,记录了某人每天的消费,然后大猫想建立一个变量统计他的“累计”消费,在SAS中需要用到Retain语句,如果有很多个人,大猫可能还要按照个人ID分组,然后使用First与Last变量,代码如下:
▶
Proc Sort Data = Expense;
By Id, Date;
Run;
Data A;
Retain Cum 0;
Set Expense;
By Id Date;
If First.Date Then Cum = 0;
Cum Expense;
Run;
而在R中也只需一行代码:
▶ dt[, sum := cumsum(expense), by = id]
为了实现一个小小的需求,大猫不得不多写8行代码,在写这8行代码的过程中,可能大猫就忘了当初是要做什么了。大家因此可以看出语法的简洁对于Interactive编程来说是多么重要。原来,由于SAS语法在处理有些数据的时候不是那么简洁,大猫是SQL语法和SAS语法混着用的(在SAS中调用SAS/ACCESS访问MySQL),现在,大猫处理数据已经全部换成了R。这种转换带来的最直观的变化是代码行数要少了很多,这为大猫日后回顾代码带来了巨大的便利。
以上是大猫一直在强调经济学研究编程I/O Intensive与Interactive特性的原因:虽然SAS的 Data Step – Proc Step的Philosophy可能在总体上有着更好的Performance和Robustness,但是毕竟我们的目标是学术探索,代码是我们思考的工具而不是卖给别人的产品,因此我们需要有一种语法尽可能直观简介地表达我们的需求,而不是某种运行更快但是冗长的语法,我们在Coding上花的时间越少,我们在Research上花的时间就越多。大猫在这引用R社区开发大神Hadley Wickha的话,这个戴耳钉的Assistant Professor在谈到为什么会开发Plyr、Dplyr等包的时候说到:
“程序员都说数据挖掘中70%的时间都花在数据清洗上面了,数据清洗不仅是数据挖掘的第一步,而且还会在整个数据挖掘的过程中不断重复,但是似乎专门研究如何减少这70%的数据清洗时间的研究还很少。我的Plyr/Dplyr/Tidyr包就是为此而生的。我希望你们能用宝贵的时间去做更多更重要的事。”
—— Hadley Wichham
”
Thanks Hadley, thanks Matt, both of you save our life.
注:Matt: data.table包的作者
下
期预告
下一期大猫会带领大家探索编程语言的另一个重要指标:性能与并行运算,一定要继续follow哦!
我是大猫,咱们下一期见!
参
考文献
大猫在人大经济论坛上的原帖请见
http://bbs.pinggu.org/thread-3861040-1-1.html