《深入解析sas:数据处理、分析优化与商业应用》一3.4 循环和数组

本节书摘来自华章出版社《深入解析sas:数据处理、分析优化与商业应用》一书中的第3章,第3.4节,作者 夏坤庄 徐唯 潘红莲 林建伟,更多章节内容可以访问云栖社区“华章计算机”公众号查看

3.4 循环和数组

SAS还提供了循环语句以满足在编程中需要多次执行相同操作的情况。有时还需要对不同的变量执行相同的操作,此时可定义SAS数组,并通过数组名和下标来引用这些变量。

3.4.1 循环

SAS循环语句通常有如下几种形式:迭代DO语句、DO WHILE语句和DO UNTIL语句。
1.?迭代DO语句
迭代DO语句的基本形式如下:

DO 索引变量=开始值 <TO 结束值> <BY递进值> <WHILE(表达式)> <UNTIL(表达式)>;
… SAS语句…
END;

其中:
索引变量用于指定一个变量,若该变量不存在,则创建新变量。DO语句和END语句之间的语句称为DO组,索引变量的值会控制DO组的执行。
开始值指定索引变量的初始值,可以是表达式或表达式序列。DO组的执行从“索引变量=开始值”开始。在循环的第一个迭代开始前,对开始值求值。如果结束值和递进值不存在,那么开始值可能是一系列项,则DO语句的形式如下。

DO 索引变量=项1 <, …项n>;

项1~项n可以是数字常量、字符常量或变量。SAS为列表中的每个项执行一次DO组。
结束值指定索引变量的结束值。当开始值和结束值都存在时,DO组执行直到下面任意一种情况发生时循环执行结束:索引变量的值超过结束值;DO组中存在指示退出循环的语句,例如LEAVE语句、GO TO语句;如果有WHILE或UNTIL选项,则WHILE之后的表达式不满足或UNTIL之后的表达式满足(可参考后面对DO UNTIL语句和DO WHILE语句的介绍)。
递进值指定一个数字,或者是产生数字值的表达式,来控制索引变量的增量。递进值在循环执行前进行计算。因此,在DO组内对递进值的修改不会影响循环迭代次数。每次迭代后,索引变量的值为其当前值的基础上增加递进值。如果未指定递进值,则索引变量的值增加1。
例3.24:使用DO循环生成数据集,数据集中包含两个变量x和y。x的值为1、3、5,y为x的平方。
代码如下:

data work.square;
    do x=1 to 5 by 2;
        y=x**2;
        output;
    end;
run;

proc print data=work.squart noobs;
run;

PRINT过程打印生成的数据集的内容如图3.28所示。
2.?DO UNTIL语句
DO UNTIL语句重复执行DO循环中的语句,直到条件为真。DO UNTIL语句的基本形式如下:

DO UNTIL (表达式);
    ...SAS语句...
END;

表达式可是任意的SAS表达式。DO UNTIL语句中至少包含一个表达式,也可以包含多个表达式。在DO循环中的语句执行完成后将对表达式求值。所以,DO循环至少被执行一次。
例3.25:将给定字符串中包含的各个单词分开写入数据集中。
在DO循环中使用SCAN函数依次读取字符串中的单词,并存储在变量word中且输出。LENGTHN函数用于判断循环结束的标志。当SCAN函数扫描到字符串结尾,word值为空时,LENGTHN函数返回0,循环结束。代码如下:

data work.all;
    length word $20;
    drop string;
    string='The quick brown fox jumps over the lazy dog.';
    do until(lengthn(word)=0);
        count+1;
        word=scan(string, count);
        output;
   end;
run;

proc print data=work.all noobs;
run;

PRINT过程打印字符串中各个单词的内容如图3.29所示。
3.?DO WHILE语句
DO WHILE语句在条件为真时重复执行DO循环。DO WHILE语句的基本形式如下:

DO WHILE (表达式);
    ...SAS语句...
END;

表达式可是任意的SAS表达式。DO UNTIL语句中至少包含一个表达式,也可以包含多个表达式。在DO循环中的语句被执行前先对表达式求值。如果第一次执行时表达式为假(false),DO循环不会被执行。
例3.26:将给定字符串中包含的各个单词分开写入数据集中。
代码如下:

data work.all;
    length word $20;
    drop string;
    string=' ';
    word=scan(string, 1);
    do while(lengthn(word)>0);
        count+1;
        word=scan(string, count);
        output;
    end;
run;

因为要WHILE表达式必须成立才会执行DO循环,也就是说在执行到DO WHILE语句之前,word不能为空或缺失值,所以代码中先对word值赋值(word可以赋值为其他任何不为空的字符值)。DO循环中,当SCAN函数扫描到字符串结尾,word值为空时, LENGTHN函数返回0,不满足条件lengthn(word)>0,循环结束。所生成的数据集与例3.25的结果一样,这里不再给出。
下面是对3种循环语句的比较:
迭代DO语句基于索引变量值重复执行DO语句和END语句之间的SAS语句。使用迭代DO语句较容易控制循环次数。
DO UNTIL语句重复执行在DO循环中的语句,直到条件为真。DO UNTIL语句在每次DO循环迭代结束后检查条件。
DO WHILE语句在条件为真时重复执行DO循环中的语句。DO WHILE语句在每次DO循环迭代开始前检查条件。

3.4.2 SAS数组

如果需要对许多变量做相同的操作,虽然可以通过写一系列赋值语句来实现,但是使用数组会简化程序代码。
数组是一组以特殊顺序排列并由数组名标识的SAS变量。只要一组变量名都是同一类型,例如都是数值型或字符型,就可以为该组变量定义一个数组。这些变量可以是数据集中已经存在的,也可以是要创建的新变量。数组仅仅在当前DATA步中存在,在同一DATA步中,数组按名字区分。SAS数组不是一种数据结构,只是临时标识一组变量较方便的方法,在这点上SAS数组不同于其他编程语言中的数组。
1.?数组定义及引用
在DATA步中使用ARRAY语句定义数组。定义数组的基本形式如下:
ARRAY 数组名 {下标} <$> <长度> <数组元素> <(初始值列表)>;
其中:
数组名是指定给数组的名称。该数组名在同一DATA步中不能与任何其他变量名或关键字重名。其命名需遵循SAS变量的命名规范(不超过32个字符,以字母或下划线开始,可包含字母、数字和下划线)。
下标可以有多种形式,通常为指定数组元素个数、上下边界或表示通过计算数组元素个数确定数组下标。
$指定数组中的元素是字符元素。如果数组元素是数字元素或先前已经定义的字符元素,则不需要使用$。
长度指定先前未指定长度的元素的长度。
数组元素指定组成数组的元素名称。
初始值列表给出数组中对应元素的初始值,以空格隔开。
定义数组时的括号可以是()、{}或[]。定义数组时的下标形式指定了数组维度、各个维度的下边界和上边界。还可以通过不同格式的下标指定多维数组,多个维度之间用逗号(,)分隔,同一个维度内的上下边界使用冒号(:)分隔,如表3.8所示。
《深入解析sas:数据处理、分析优化与商业应用》一3.4 循环和数组

数组元素可以是数值型或字符型,并且可以以任何顺序列出,其个数必须等于括号{}中给出的下标值。
数组元素可以是已经存在的变量或不存在的变量,当数组元素是不存在的变量时,SAS会创建新变量。除了列出变量外,变量可以是关键字_NUMERIC_、_CHARACTER_或_ALL_,其说明如表3.9所示。
《深入解析sas:数据处理、分析优化与商业应用》一3.4 循环和数组

还可使用关键字_TEMPORARY_来创建临时数据元素。临时数据元素不会出现在输出数据集中,并且其值总是自动保持,而不会在DATA步的每次迭代开始时自动设置为缺失值。临时数组元素仅用于计算,如果要保留计算结果,需要将结果赋值给其他变量。使用临时数组元素的好处是可以提高性能。
使用数组名引用变量时,要给出数组名和该变量的下标。例如,使用如下ARRAY语句定义变量STORE,共4个元素,分别为变量Macys、Penneys、Sears和Target。

array Store {4} Macys Penneys Sears Target;

使用数组名引用变量时,STORE{1}是变量Macys,STORE{2}是Penneys,STORE{3}是Sears,STORE{4}是Target。
如果定义数组时指定了上下边界,例如:

array Month {4:6} April May June;

则Month{4}是变量April,Month{5}是变量May,Month{6}是变量June。
这样就可以在循环中通过改变数组下标来操作每个数组元素对应的变量,所以数组经常在DO组中使用,后面会介绍。
2.?数组函数
SAS提供了DIM、HBOUND和LBOUND函数返回数组中指定维度的元素个数、上边界和下边界。
DIM函数返回一维数组中的元素个数或多维数组中指定维度中的元素个数。其形式如下:

DIM<n>(数组名) 
或
DIM(数组名, 维度)

HBOUND函数返回一维数组的上边界或多维数组中指定维度的上边界。其形式如下:

HBOUND<n>(数组名) 
或
HBOUND(数组名, 维度)

LBOUND函数返回一维数组的下边界或多维数组中指定维度的下边界。其形式如下:

LBOUND<n>(数组名) 
或
LBOUND(数组名, 维度)

其中:
n指定维度的整型常量。如果未指定n值,则返回数组的第一维的结果。
数组名指定在同一DATA步中先前定义的数组名称。
维度是指定维度的数字常量、数字变量或数字表达式。
例如,对于下面的数组定义,各数组函数的返回值如表3.10所示。

array x {2,11:15} a1-a10;

《深入解析sas:数据处理、分析优化与商业应用》一3.4 循环和数组

3.?DO循环中引用数组元素
将数组与迭代DO语句结合使用,在对用数组所表示的变量进行处理时会变得简单。其基本形式如下:

DO 索引变量=1 TO 数组元素个数;
    … SAS语句…
END;

例3.27:使用例3.18中的数据集saslib.revenue_quarter制定明年的销售计划,预期各季度的销售额为今年的150%。
代码如下:

data saslib.revenue_2014;
    set saslib.revenue_quarter;
    array Quarter {4} Rev_Q1-Rev_Q4;
    do i=1 to 4;
        Quarter{i} = Quarter{i}*1.5;
    end;
    drop i;
run;

其中:
ARRAY语句定义了数组Quarter,其元素个数为4。因为变量Rev_Q1到Rev_Q4在输入数据集中存在,所以该数组只是引用存在的变量Rev_Q1到Rev_Q4,并不创建新变量。这里数组元素使用了简化的表达方式。
在DATA步的每次迭代中,通过在DO循环中引用Quarter{1}到Quarter{4}来分别操作变量Rev_Q1到Rev_Q4,将销售额在现有销售数据的基础上增加50%。
数据集saslib.revenue_2014在VIEWTABLE窗口打开如图3.30所示。
《深入解析sas:数据处理、分析优化与商业应用》一3.4 循环和数组

上一篇:Tensorflow入门1-CNN网络及MNIST例子讲解


下一篇:一致性哈希算法应用与分析