如何使用数组对变量进行水平排序以及调用 sortc 的使用

How to sort variables horizontally with arrays and use of call sortc

sasprofessionals.net 上的一位用户遇到了无法按多个变量对其数据集进行分组的问题,其中变量值在观察中可以互换,因为它们具有相同的含义。

在示例数据集中,观察2,3和7是相同的,因为它们中的每一个都有A14,A14和A10作为Stat1到Stat3的值,只是顺序不同。这些应该按计数分组。观察 5 和 6 形成另一组,应按计数求和。

示例数据集:

Obs Stat1 Stat2 Stat3 Count
1   A14   A14   A14   53090
2   A14   A14   A10   6744
3   A14   A10   A14   5916
4   A01   A01   A01   4222
5   A10   A10   A10   3085
6   A10   A10   A10   2731
7   A10   A14   A14   2399

理想的输出:

Obs Stat1 Stat2 Stat3 Count
1   A14   A14   A14   53090
4   A01   A01   A01   4222
6   A10   A10   A10   5816
7   A10   A14   A14   15059

实际数据集更大更复杂。我不知道用户是否尝试过任何方法来解决问题。

这个问题最初发布在 sasprofessionals.net 上,为了社区的利益,它被复制到 Whosebug。它已更改以满足 Whosebug 问答标准。

这是我解决用户问题的答案。通常,我将 Stat1-Stat3 加载到一个数组中,使用 sortc 调用函数对数组进行排序,然后通过一个临时 ID 将其汇总,该临时 ID 由排序后的 Stat1-Stat3 数组构成。

/* Loading the data into SAS dataset */ 
/* Loading Stat1-Stat3 into an array */
/* Sorting stat1-stat3 creating a new ID */
data have; 
input obs stat1 $ stat2 $ stat3 $ count; 
array stat{3} stat1-stat3;
call sortc(of stat1-stat3); 
ID = CATX("/",stat1,stat2,stat3);
datalines; 
1 A14 A14 A14 53090
2 A14 A14 A10 6744
3 A14 A10 A14 5916
4 A01 A01 A01 4222
5 A10 A10 A10 3085
6 A10 A10 A10 2731
7 A10 A14 A14 2399
; 


/* sorting the data set in preparation for data step with by statement*/
PROC SORT data=have; 
BY ID OBS; 
RUN; 

/* Summarising the dataset and outputing into final dataset*/
DATA summed (drop=ID count); 
set sorted_arrays; 
by ID; 
retain sum 0; 
if first.ID then sum = 0; 
sum + count; 
if last.ID then output; 
RUN; 

/* Sorting it back into original order */
PROC SORT data=summed out=want; 
BY OBS; 
RUN; 

由于我一直在给自己做散列练习,所以我决定通过散列来尝试一下。 Paul Dorfman 有几篇论文讨论了使用散列 tables 对数组进行排序,例如Black Belt Hashigana

下面,我使用一个散列 table 进行水平排序,然后使用另一个散列 table 按 ID 对计数求和。数据只需读取一次,但鉴于数据的大小,我当然不会在这种情况下声称效率优势。我没有 return 将数据恢复为原始排序顺序。

Edits/questions/suggestions 欢迎,因为这是我的哈希学习曲线的一部分。 :)

data have; 
input stat1 $ stat2 $ stat3 $ count; 
datalines; 
A14 A14 A14 53090
A14 A14 A10 6744
A14 A10 A14 5916
A01 A01 A01 4222
A10 A10 A10 3085
A10 A10 A10 2731
A10 A14 A14 2399
; 

data want;
  length _stat ;

  if _n_=1 then do;
    declare hash  hstat(multidata:"y", ordered:"y");
    declare hiter hstatiter ("hstat" ) ;      
    hstat.definekey('_stat');
    hstat.definedata('_stat'); 
    hstat.definedone();
    call missing(_stat);

    declare hash  hsum(suminc: "count", ordered: "y");
    declare hiter hsumiter ("hsum" ) ;      
    hsum.definekey("stat1","stat2","stat3");
    hsum.definedone();
  end;

  set have end=last;

  array stat{3};

  *load the array values into htable hstat to sort them;
  *then iterate over the hash, returning the values to array in sorted order;
  do _i=1 to dim(stat);  
    hstat.add(key:stat{_i},data:stat{_i});
  end;
  do _i=1 to dim(stat);
    hstatiter.next();
    stat{_i}=_stat;
  end;
  _rc=hstatiter.next(); *hack- there is no next, this releases hiter lock so can clear hstat;
  hstat.clear();

  *now that the stat keys have been sorted, can use them as key in hash table hsum;
  *as data are loaded into/checked against the hash table, counts are summed;
  *Then if last, iterate over hsum writing it to output dataset;

  hsum.ref(); *This sums count as records are loaded/checked;

  if last then do;
    _rc = hsumiter.next();
    do while(_rc = 0);
      _rc = hsum.sum(sum: count);
      output ;
      _rc = hsumiter.next();
    end;
  end;

  drop _: ;
run;