BookmarkSubscribeRSS Feed
从程序员到数据科学家:SAS 编程基础 (02)- 语言概述 / 作者:巫银良
neilyang
SAS Employee

TIOBE 指数是用来反映某种编程语言的流程程度的指标,根据2016年12月份最新数据显示,SAS 编程语言占比 1.380%,排名22位。 编程语言本质上是人类用来与机器沟通,并在人类之间分享思维的工具,与它所需要解决的问题领域紧密相关。世界上也没有哪一种语言能够解决所有问题。所以尽管计算机领域出现了超过上千种编程语言,但终究只有少数强大语言能生存下来,SAS 便是其中之一。

 

SAS 语言要入门很快,但要精通则需要较长时间,尤其是要掌握SAS特有的,在普通编程语言里面没有的一些功能。SAS 作为领域特定的第四代编程语言(4GL),与广泛流行的第三代编程语言C,C++,Java,C#,Javascript不同,它专门为数据分析和报告提供非常复杂的数据操作、图形图表制作以及文档编写功能。

 

SAS语言总体上是面向过程的计算机语言,有着传统编程语言的基本结构。但它不支持面向对象,尽管从SAS 9 开始提供两个预定义的对象:Hash和 HIter(Hash Iterator)并提供类似面向对象的语法,但用户至今不能用 SAS 语言创建自定义类。SAS提供强大的互操作能力,可以调用Java 对象和 Win32 API 函数来实现各种复杂功能。SAS 语言也并不像Python/Perl等严格意义上的脚本式编程语言,具有真正的脚本/流控制构造。

 

SAS 语言中只有两种基本的数据类型:字符型和数值型。从外部数据到SAS内部数据存储表达之间,可以使用INFORMAT和 FORMAT进行读/写转换。然而,在一些 PROC 步中,SAS 也提供特定的数据类型来覆盖这两种基本类型,比如PROC FCMP(从 SAS 9.1开始)中类似C语言的结构体,以及PROC IML 中的矩阵。与传统编程语言支持各种各样的基本数据类型(比如布尔/整数/浮点/字符…)不同,统计学上的数据类型根据计量尺度只有定类(如性别)、定序(如年级)、定距(如温度)以及定比(重量)等四种需要考虑。

 

SAS 语言提供非常强大预处理器,实现编译前的宏替换功能。它允许 SAS 程序在编译和运行期间,动态改变程序自身,并可实现递归调用宏本身。使用 SAS 语言的统计分析人员经常赞叹SAS语言的强大功能,而不熟悉 SAS 语言的编程人员有时也会为它与第三代编程语言的不同而困惑不解,迷失在宏与非宏的代码陷阱里。

 

SAS 语言是一门比较复杂的计算机语言,甚至它到底是编译执行还是解释执行有时候连有经验的SAS开发人员也会感到困惑。由于SAS 语言包含灵活的语言元素,SAS Macro宏是由解释器展开,但是 DATA Step和 Proc Step 则以步为单位进行一次编译,然后统一执行而非逐语句进行解释执行。SAS是兼具编译和解释的混合型计算机语言,因此维基百科的分类中也很难将 SAS 语言归入编译还是解释类别(Compiled_language vs. Interpreted_Language)。

 

SAS 语言最强大的能力是为分析编程人员提供了完备细致的数据访问,而不用太考虑数据的存储格式和存储位置,比如最常用的DATA Step和 PROC SQL就提供了各种各样的数据操作能力,而丰富的 PROC Step 支持则让分析人员专注于分析本身和参数设定。一旦 SAS 编程入门,则只有不懂的统计类型,没有不会使用的 PROC Step。

 

从传统语言编程基础转换到SAS 语言编程,首先要记得如下一些SAS语言的核心规则:

 

  1. SAS 程序由一系列SAS 语句组成,所有的语句都以分号 ; 结束。
  2. SAS 代码中也可以包含数据行,但数据行不作为语句,不以分号结尾。
  3. 一个SAS语句可以跨行,多个 SAS 语句也可以在一行上;SAS 语句可以从一行中的任意位置开始,代码缩进并非必需。
  4. SAS 语句中的关键字是以空格分隔的,通常由“关键字”或“关键字=参数”系列组成;某些语句可以在必选项和可选项之间用 / 分隔。
  5. SAS 语言不区分大小写,你可以使用大写/小写以及它们的混合。但字符变量的值是区分大小写的。比如 “Hello World” 和 “HELLO WORLD” 为不同的两个值。
  6. SAS 代码中标识符长度较短:逻辑库引用/文件引用(libref/fileref)名称最长不超过8字节、数据表/数据列(即变量)名称最多不超过32字节。
  7. SAS 代码提供强大的SAS宏系统支持,包含 % 和 & 符的文本会在编译前触发宏展开。SAS提供语言级的宏支持,包括变量、函数以及分支/循环等完备的流程控制。

 

SAS 程序主要由一系列的 Steps 构成,而 Step 由一些列的 Statements  构成。每一个 Step 都有开始和结束的边界,SAS 根据 Step 边界独立编译和执行。

 

neilyang_0-1599110127235.png

 

 

一个所谓的步(Step)由 DATA或PROC 语句开始,默认结束于下一个 DATA/PROC 步的开始处。但我们通常用 RUN 语句来执行前面输入的SAS语句,并显式标识 DATA/PROC 步的边界;另外有一些资源依赖的 PROC则需要以 QUIT 语句来结束当前 PROC,比如 PROC SQL、PROC CAS等可以包含多个 RUN语句,但只有当 QUIT语句执行才释放资源真正返回SAS。

 

  1. 全局语句:在 DATA/PROC 步之外,用于指定全局的选项或者其他全局性的功能。
    比如 TITLE 语句用来指定输出报告的标题,最多达11级。

 

TITLE "The title of my first report"; /*设置输出报告的标题*/

TITLE2 "Author: Yinliang Wu";  

 

各种SAS 选项(SAS Options)语句也属于全局语句,功能上类似于操作系统的环境变量,用来指定当前 SAS会话有关的系统设置。

options ls=80 ps=24 nocenter; /*设置输出为 24 X 80 左对齐*/

 

  1. DATA 步(Data Step):SAS 程序只包含两种类型的步DATA步和PROC步,DATA 步负责为后续数据步/过程步准备数据,基本语法为:

 

DATA MYDATA;

  <STATEMENT and/or DATA>;

RUN;

 

比如:如下代码创建一行5列的数据,其中 Name和Sex 是字符变量,而其他3个为数值变量。

 

DATA MyData;

  input Name $ Sex $ Age Height Weight;

  datalines;

YINLIANG M 30 175 83.5

RUN;

 

  1. PROC 步(PROC Step):过程步是执行特定任务的SAS语句 的集合,以 PROC 语句开始,直到下一个 RUN语句结束。 每个过程步有自己特定的SAS 语句和命令,但也也有很多过程步有相同的SAS 语句和命令。

 

PROC PRINT DATA=MyData;

   VAR Name Height;

RUN;

 

PROC CONTENTS DATA=MyData;

RUN;

 

DATA=MyData是PROC PRINTPROC CONTENTS 语句特定的参数(也叫选项),所有的PROC步都有 DATA= 选项。如果没有指定,则系统默认为执行过程中最近创建的那个数据集。

 

  1. 程序注释:
    1. 块注释:SAS 支持C、C++和 Java 等广泛使用的块注释 /* …注释… */ 功能,注释可以是任何长度,可包括分号但不能嵌套使用块注释;

/* …

 * …注释…;

 * …

 */

PROC CONTENTS DATA=MyData;

RUN;

 

  1. 行注释:以星号 * 开始,以往后的第一个分号 ; 结束。比如:

* …注释…;

PROC CONTENTS DATA=MyData; * …注释…;

  * …注释…;

RUN;

 

注释通常用于说明程序的功能、标注非执行文本或者用于文档目的,可以在代码调试过程中将调好的代码暂时隔离。SAS 运行时注释中的代码会被忽略,但注释也会被写入日志文件。 需要特别注意的一点是,在SAS Macro 宏代码要谨慎使用行注释,以免导致不期望的宏展开。

  1. 推荐代码规范

虽然SAS代码格式具有极强的灵活性,但良好的格式化能提高代码的可读性和可维护性。

  1. 通常 DATA/PROC/RUN 语句开始于第一列,而其他子语句则采用缩进,以显示结构关系。
  2. SAS 步之间通常用空白列隔离,以示SAS代码的编译边界,从而方便代码调试。
  3. 全局语句与顶级语句 DATA/PROC/RUN 平齐。

 

结语: SAS 的 DATA 步和PROC 步是SAS 语言对数据分析工作的精妙抽象和完美封装,DATA 步解决数据结构和准备问题,而PROC步解决对特定分析流程的实现和封装。你可以把它对应成传统编程中的数据结构和算法设计,分别负责数据和逻辑的实现。