在SAS中我们比较习惯使用DATA步来解决数据处理工作,但是当我们需要处理的是两个以上有关联的数据文件或需要处理的数据观测记录达到百万级别时,DATA步显然不能满足我们的要求。若使用hash散列表(哈希表),把关键字映射到散列表中,通过散列表直接获取需要访问数据的存储地址,可以快速的进行查找、添加等操作。SAS提供了hash和hiter两种方法处理哈希表,hash提供了查找、修改、添加、删除等方法,hiter提供了用于定位和遍历等方法。1.1Hash的定义及使用在使用之前需要定义哈希表,如下:Daclarehashmyhash;myhash=_new_hash();或者:Declarehashmyhash();例如:Daclarehashmy_hash;my_hash=_new_hash();定义了一个名字为my_hash的哈希表。初始化哈希表:declarehashvariable_name(argument_tag-1:value-1,...argument_tag-n:value-n);或者:variable_name=_new_hash(argument_tag-1:value-1,...argument_tag-n:value-n);例如:declarehashh(hashexp:4,dataset:'work.sales',ordered:'yes');定义了一个名字为h的哈希表,并且把work.sales的数据映射到哈希表上;hashexp:4表示哈希表的大小定为2的4次方,ordered:'yes'表示在哈希表中根据关键字升序排序。语句解读:使用第一种方法定义哈希表时需要注意myhash是定义的表名,其余部分为固定格式。初始化时可选填的项有:Hashexp:n指定哈希表的大小为n2,但并不表示观测数。默认为8。Dataset:`dataset_name'指定需要映射到哈希表的数据集。Ordered:’option’指定输出到数据集或读取数据时是否排序,option选项如下:Ascending和yes表示根据key升序;Descending表示根据key值降序;no为不做任何操作。此外各选项均可只用首字母代替。duplicate:’option’option选项如下:replace表示遇到相同key时,仅保留最后一条观测;error表示遇到相同的key时,在日志窗口报错multidata:’option’’option’选项如下:yes表示允许重复的key在哈希表中出现,no则不允许,默认情况为不允许。在定义一个新的哈希表后需要指定哈希表的关键字段等,如下:语句功能DEFINEKEY定义关键字段;DEFINEDATA定义值;DEFINEDONE定义完成;CALLMISSING避免提示变量未初始化哈希表中可以实现较多的操作,语句格式如下:myhash.’option’();option可选选项句及功能:语句功能FIND查找相同的键值,存在则返回rc=0;ADD添加键值,若hash表中已存在,则不添加;CHECK查找健值,若存在返回rc=0;REPLACE若哈希表中不存在指定的关健字段,则添加;若存在则替换;REMOVE移去哈希表中指定关键字对应的地址等数据;EQUAL判断两个哈希表是否完全相等;OUTPUT将哈希表输出到数据集;REF若查找关键字成功则无操作,若查找不成功则添加关键字,相当于find与add的结合;SETCUR规定一个关键字,用于起始迭代。通常在对数据排序后使用。SUM计算并返回相同key下指定计数变量的和CLEAR清空hash表,但不删除数据;DELETE删除整个哈希表NUM_ITEMS计算哈希表中观测的条数ITEM_SIZE计算哈希表的大小在哈希表中还有如下操作,与上述操作不同的是,使用下列的操作必须在允许哈希表中出现相同的关键字前提下。HAS_NEXT在允许出现相同关键字情况下,是否存在下一条相同关键字的观测FIND_NEXT在允许出现相同关键字情况下,寻找下一条相同关键字的观测FIND_PREV在允许出现相同关键字情况下,寻找前一条相同关键字的观测REMOVEDUP在允许相同关键字出现的情况下,在哈希表中移去当前关键字等数据REPLACEDUP在允许出现相同关键字情况下,使用新的数据代替当前关键字对应的数据等SUMDUP在允许出现相同关键字情况下,计算并返回当前关键字下的指定计数的变量和由于哈希表的定义及操作的语法及使用语句较多,格式较新,下面我们通过一个例子来解释哈希表的定义及简单操作。[例5.1]定义一个哈希表并对其进行ADD、CHECK、FIND、REMOVE、REPLACE、NUM_ITEMS等操作。[程序5.1]datahash_sample;inputfruit$amount;cards;apple100orange200tomato300cherry400cherry500;run;data_null_;lengthfruit$10;lengthamount8;if_N_=1thendo;declarehashmyhash(dataset:'work.hash_sample',duplicate:r);rc=myhash.defineKey('fruit');rc=myhash.defineData('fruit','amount');rc=myhash.defineDone();callmissing(fruit,amount);end;rc=myhash.add(key:'peach',data:'peach',data:500);rc=myhash.find(key:'peach');ifrc=0thenputfruit=amount=;rc=myhash.check(key:'orange');ifrc=0thenput'theorangeisexist';rc=myhash.remove(key:'tomato');ifrc=0thenput'tomatoisnotexist';rc=myhash.replace(key:'apple',data:'apple',data:300);ifrc=0thendo;rc=myhash.find(key:'apple');ifrc=0thenputfruit=amount=;end;rc=myhash.ref(key:'tomato',data:'tomato',data:200);totalitems=myhash.num_items;puttotalitems=;myhash.output(dataset:'out');run;输出结果为:表1程序5.1输出前后数据集hash_sample数据集out数据集fruitamountfruitamountapple100apple300orange200tomato200tomato300orange200cherry400cherry500cherry500peach500程序解读:程序5.1中把数据集映射到哈希表,并在哈希表中做添加、修改等简单操作,最后输出哈希表。rc为接收返回值,程序中若操作成功则返回0,失败则返回非0。lengthfruit$10;lengthamount8;在定义哈希表前,先定义需要映射到哈希表的变量的类型及长度,且必须与数据集中的类型匹配。declarehashmyhash(dataset:'work.hash_sample',duplicate:r);定义一个名为myhash的哈希表,并把work.hash_sample的数据集映射到哈希表上;duplicate:r表示相同关键字时数据会不断代替,其结果是哈希表中只有相同关键字的最后一观测的地址,如上例中“cherry400”将被忽略。rc=myhash.defineKey('fruit');定义关键字段为fruit。rc=myhash.defineData('fruit','amount');定义数据为fruit和amount,需要注意的是,这里若只定义amount变量,则哈希表中不存在fruit变量,而只是存在其对应的地址,当以需要引用哈希表输出的时候将忽略fruit变量。rc=myhash.defineDone();表示定义完成。callmissing(fruit,amount);当未找到匹配值时,返回空值给指定变量。myhash.add(key:'peach',data:'peach',data:500);在哈希表中增加一关键字为peach的记录,若已经存在则忽略。由于定义时数据项为fruit和amount,则增加记录时必须对应全部罗列出来。且amount为数值型,则500无单引号。rc=myhash.find(key:'peach');ifrc=0thenputfruit=amount=;查找关键字为peach的记录,若存在返回0,使用ifthen打印查看详细信息。rc=myhash.check(key:'orange');ifrc=0thenput'theorangeisexist';查找关键字为orange的记录,使用ifthen打印指定字符。你或许会疑惑为什么不直接打印orange的详细信息?其实这就是check和find的区别,可以理解为指针的形式,find不仅找到而且指针指向find的记录,而check只是查看有没有,并不移动指针,所以如果把ifthen指定的打印字符换成输出信息,则输出的仍为peach的信息。rc=myhash.remove(key:'tomato');移去关键字为tomato的记录信息。rc=myhash.replace(key:'apple',data:'apple',data:300);替换关键字为apple的记录信息。由于replace和check一样不会使指针移动,则可以先使用find再打印查看是否替换成功。rc=myhash.ref(key:'tomato',data:'tomato',data:200);若查找关键字tomato成功则无操作,若查找不成功则添加关键字等信息,相当于find与add的结合。需要注意的是ref同样不移动指针。totalitems=myhash.num_items;计算哈希表中观测的数量并赋值给totalitems。myhash.output(dataset:'out');把哈希表的信息输出到out数据集。[例5.2]在允许关键字重复出现情形下进行简单操作。[程序5.2]datahash_keys;inputkeysdata;cards;210032002300240015003600;run;data_null_;lengthkeysdata8;if_N_=1thendo;declarehashh(dataset:'hash_keys',multidata:'y');h.definekey('keys');h.definedata('keys','data');h.definedone();callmissing(keys,data);end;dokeys=1to3;rc=h.find();put'@'keys=data=;h.has_next(result:r);dowhile(rne0);rc=h.find_next();put'@@'keys=data=;rc=h.find_prev();put'@@@'keys=data=;rc=h.find_next();put'@@@@'keys=data=;h.has_next(result:r);end;h.removedup();end;h.output(dataset:'hash_out');run;输出结果:表2程序5.2数据集整理hash_keys数据集hash_out数据集keysdatakeysdata210021003200240023003200240015003700程序解读:程序5.2中在允许相同关键字出现的情况下映射到哈希